From 0f42b3b2e96244da7364544dcceb4d2efb5100ac Mon Sep 17 00:00:00 2001 From: Grazfather Date: Mon, 16 Dec 2024 17:12:38 -0500 Subject: [PATCH 01/30] rp2xxx: Update PIO for rp2350 --- examples/raspberrypi/rp2xxx/build.zig | 48 +- .../rp2xxx/src/hal/compatibility.zig | 6 +- port/raspberrypi/rp2xxx/src/hal/cpu.zig | 4 + .../rp2xxx/src/hal/pio/assembler.zig | 26 +- .../hal/pio/assembler/comparison_tests.zig | 9 +- .../pio/assembler/comparison_tests/README.md | 4 +- .../pio/assembler/comparison_tests/irq.pio | 14 + .../pio/assembler/comparison_tests/irq.pio.h | 14 + .../rp2xxx/src/hal/pio/assembler/encoder.zig | 289 +- .../src/hal/pio/assembler/tokenizer.zig | 2509 +++++++++-------- .../raspberrypi/rp2xxx/src/hal/pio/common.zig | 4 +- 11 files changed, 1590 insertions(+), 1337 deletions(-) create mode 100644 port/raspberrypi/rp2xxx/src/hal/cpu.zig create mode 100644 port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/irq.pio create mode 100644 port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/irq.pio.h diff --git a/examples/raspberrypi/rp2xxx/build.zig b/examples/raspberrypi/rp2xxx/build.zig index 5f467d46..18a53707 100644 --- a/examples/raspberrypi/rp2xxx/build.zig +++ b/examples/raspberrypi/rp2xxx/build.zig @@ -13,24 +13,24 @@ pub fn build(b: *std.Build) void { const rp2040_only_examples: []const Example = &.{ // RaspberryPi Boards: - .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_adc", .file = "src/rp2040_only/adc.zig" }, - .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_flash-program", .file = "src/rp2040_only/flash_program.zig" }, - .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_flash-id", .file = "src/rp2040_only/flash_id.zig" }, - .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_i2c-bus-scan", .file = "src/rp2040_only/i2c_bus_scan.zig" }, - .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_pwm", .file = "src/rp2040_only/pwm.zig" }, - .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_random", .file = "src/rp2040_only/random.zig" }, - .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_rtc", .file = "src/rp2040_only/rtc.zig" }, - .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_spi-host", .file = "src/rp2040_only/spi_host.zig" }, - .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_uart-echo", .file = "src/rp2040_only/uart_echo.zig" }, - .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_uart-log", .file = "src/rp2040_only/uart_log.zig" }, - .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_usb-hid", .file = "src/rp2040_only/usb_hid.zig" }, - .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_usb-cdc", .file = "src/rp2040_only/usb_cdc.zig" }, - .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_multicore", .file = "src/rp2040_only/blinky_core1.zig" }, - .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_hd44780", .file = "src/rp2040_only/hd44780.zig" }, - .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_pcf8574", .file = "src/rp2040_only/pcf8574.zig" }, - - // WaveShare Boards: - .{ .target = mb.ports.rp2xxx.boards.waveshare.rp2040_matrix, .name = "rp2040-matrix_tiles", .file = "src/rp2040_only/tiles.zig" }, + // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_adc", .file = "src/rp2040_only/adc.zig" }, + // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_flash-program", .file = "src/rp2040_only/flash_program.zig" }, + // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_flash-id", .file = "src/rp2040_only/flash_id.zig" }, + // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_i2c-bus-scan", .file = "src/rp2040_only/i2c_bus_scan.zig" }, + // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_pwm", .file = "src/rp2040_only/pwm.zig" }, + // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_random", .file = "src/rp2040_only/random.zig" }, + // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_rtc", .file = "src/rp2040_only/rtc.zig" }, + // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_spi-host", .file = "src/rp2040_only/spi_host.zig" }, + // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_uart-echo", .file = "src/rp2040_only/uart_echo.zig" }, + // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_uart-log", .file = "src/rp2040_only/uart_log.zig" }, + // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_usb-hid", .file = "src/rp2040_only/usb_hid.zig" }, + // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_usb-cdc", .file = "src/rp2040_only/usb_cdc.zig" }, + // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_multicore", .file = "src/rp2040_only/blinky_core1.zig" }, + // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_hd44780", .file = "src/rp2040_only/hd44780.zig" }, + // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_pcf8574", .file = "src/rp2040_only/pcf8574.zig" }, + // + // // WaveShare Boards: + // .{ .target = mb.ports.rp2xxx.boards.waveshare.rp2040_matrix, .name = "rp2040-matrix_tiles", .file = "src/rp2040_only/tiles.zig" }, // .{ .target = "board:waveshare/rp2040_eth", .name = "rp2040-eth" }, // .{ .target = "board:waveshare/rp2040_plus_4m", .name = "rp2040-plus-4m" }, // .{ .target = "board:waveshare/rp2040_plus_16m", .name = "rp2040-plus-16m" }, @@ -38,15 +38,15 @@ pub fn build(b: *std.Build) void { const rp2350_only_examples: []const Example = &.{ // TODO: No RP2350 feature specific examples to show off yet + .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico2_arm, .name = "ws2812", .file = "src/ws2812.zig" }, }; const chip_agnostic_examples: []const ChipAgnosticExample = &.{ - .{ .name = "squarewave", .file = "src/squarewave.zig" }, - .{ .name = "ws2812", .file = "src/ws2812.zig" }, - .{ .name = "blinky", .file = "src/blinky.zig" }, - .{ .name = "gpio-clock-output", .file = "src/gpio_clock_output.zig" }, - .{ .name = "changing-system-clocks", .file = "src/changing_system_clocks.zig" }, - .{ .name = "custom-clock-config", .file = "src/custom_clock_config.zig" }, + // .{ .name = "squarewave", .file = "src/squarewave.zig" }, + // .{ .name = "blinky", .file = "src/blinky.zig" }, + // .{ .name = "gpio-clock-output", .file = "src/gpio_clock_output.zig" }, + // .{ .name = "changing-system-clocks", .file = "src/changing_system_clocks.zig" }, + // .{ .name = "custom-clock-config", .file = "src/custom_clock_config.zig" }, }; var available_examples = std.ArrayList(Example).init(b.allocator); diff --git a/port/raspberrypi/rp2xxx/src/hal/compatibility.zig b/port/raspberrypi/rp2xxx/src/hal/compatibility.zig index fb754124..a12b29bd 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 CPU = enum { - RP2040, - RP2350, -}; +const CPU = @import("cpu.zig").CPU; pub const cpu: CPU = blk: { if (std.mem.eql(u8, microzig.config.chip_name, "RP2040")) { diff --git a/port/raspberrypi/rp2xxx/src/hal/cpu.zig b/port/raspberrypi/rp2xxx/src/hal/cpu.zig new file mode 100644 index 00000000..79052eb4 --- /dev/null +++ b/port/raspberrypi/rp2xxx/src/hal/cpu.zig @@ -0,0 +1,4 @@ +pub const CPU = enum { + RP2040, + RP2350, +}; diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig index 7df6ead8..949155cf 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig @@ -1,6 +1,8 @@ const std = @import("std"); const assert = std.debug.assert; +const CPU = @import("../cpu.zig").CPU; +// TODO: Isn't this circular? const tokenizer = @import("assembler/tokenizer.zig"); const encoder = @import("assembler/encoder.zig"); @@ -71,9 +73,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 format: Format, comptime source: []const u8, diags: *?Diagnostics, options: AssembleOptions) !Output { + const tokens = try tokenizer.tokenize(format, source, diags, options.tokenize); + const encoder_output = try encoder.encode(format, 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 +123,25 @@ fn format_compile_error(comptime message: []const u8, comptime source: []const u }); } +// TODO: Place. Who should own this? +pub const Format = enum { + RP2040, + RP2350, +}; + +pub fn cpuToFormat(cpu: CPU) Format { + return switch (cpu) { + .RP2040 => .RP2040, + .RP2350 => .RP2350, + }; +} + pub fn assemble(comptime source: []const u8, comptime options: AssembleOptions) Output { var diags: ?Diagnostics = null; - return assemble_impl(source, &diags, options) catch |err| if (diags) |d| + // TODO: We can't import compatibility & zig build test since it depends on microzig + // cpuToFormat(cpu) instead of hardcoding + // const cpu = @import("../compatibility.zig").cpu; + return assemble_impl(.RP2040, source, &diags, options) catch |err| if (diags) |d| @compileError(format_compile_error(d.message.slice(), source, d.index)) else @compileError(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..9325a34a 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests.zig @@ -12,6 +12,7 @@ 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/nec_carrier_burst.pio.h"); @cInclude("comparison_tests/nec_carrier_control.pio.h"); @@ -31,7 +32,8 @@ const c = @cImport({ }); fn pio_comparison(comptime source: []const u8) !void { - const output = comptime assembler.assemble(source, .{}); + comptime var diags: ?assembler.Diagnostics = null; + const output = try comptime assembler.assemble_impl(.RP2040, source, &diags, .{}); try std.testing.expect(output.programs.len > 0); inline for (output.programs) |program| { @@ -87,6 +89,11 @@ test "pio.comparison.i2c" { try pio_comparison(@embedFile("comparison_tests/i2c.pio")); } +test "pio.comparison.irq" { + @setEvalBranchQuota(22000); + try pio_comparison(@embedFile("comparison_tests/irq.pio")); +} + test "pio.comparison.manchester_encoding" { @setEvalBranchQuota(11000); try pio_comparison(@embedFile("comparison_tests/manchester_encoding.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..a3b87b63 --- /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 ; DELETEME + irq set 1 rel side 0 ; DELETEME + irq set 1 next side 0 ; DELETEME + irq wait 1 prev side 0 ; DELETEME + irq wait 1 rel side 0 ; DELETEME + irq wait 1 next side 0 ; DELETEME + irq clear 1 prev side 0 ; DELETEME + irq clear 1 rel side 0 ; DELETEME + irq clear 1 next side 0 ; DELETEME +.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/encoder.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig index e8159f82..5928a2a5 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig @@ -5,6 +5,10 @@ const assembler = @import("../assembler.zig"); const Diagnostics = assembler.Diagnostics; const tokenizer = @import("tokenizer.zig"); +// TODO: How do I pass in the format to this module? There is no (comptime) +// instance object +// const format: assembler.Format = .RP2350; +// const Token = tokenizer.Token(format); const Token = tokenizer.Token; const Value = tokenizer.Value; @@ -16,11 +20,13 @@ pub const Options = struct { }; pub fn encode( - comptime tokens: []const Token, + // We don't want the user to have to specify this + comptime format: assembler.Format, + comptime tokens: []const Token(format), diags: *?assembler.Diagnostics, comptime options: Options, -) !Encoder(options).Output { - var encoder = Encoder(options).init(tokens); +) !Encoder(format, options).Output { + var encoder = Encoder(format, options).init(tokens); return try encoder.encode_output(diags); } @@ -41,10 +47,10 @@ pub const DefineWithIndex = struct { index: u32, }; -pub fn Encoder(comptime options: Options) type { +pub fn Encoder(comptime format: assembler.Format, comptime options: Options) type { return struct { output: Self.Output, - tokens: []const Token, + tokens: []const Token(format), index: u32, const Self = @This(); @@ -56,7 +62,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(format), 32); const BoundedLabels = std.BoundedArray(Label, 32); const Label = struct { name: []const u8, @@ -105,7 +111,7 @@ pub fn Encoder(comptime options: Options) type { } }; - fn init(tokens: []const Token) Self { + fn init(tokens: []const Token(format)) Self { return Self{ .output = Self.Output{ .global_defines = BoundedDefines.init(0) catch unreachable, @@ -117,14 +123,14 @@ pub fn Encoder(comptime options: Options) type { }; } - fn peek_token(self: Self) ?Token { + fn peek_token(self: Self) ?Token(format) { 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(format) { return if (self.peek_token()) |token| blk: { self.consume(1); break :blk token; @@ -303,12 +309,12 @@ pub fn Encoder(comptime options: Options) type { fn encode_instruction( self: *Self, program: *BoundedProgram, - token: Token.Instruction, + token: Token(format).Instruction, token_index: u32, diags: *?Diagnostics, ) !void { // guaranteed to be an instruction variant - const payload: Instruction.Payload = switch (token.payload) { + const payload: Instruction(format).Payload = switch (token.payload) { .nop => .{ .mov = .{ .destination = .y, @@ -353,6 +359,7 @@ pub fn Encoder(comptime options: Options) type { .block = @intFromBool(pull.block), }, }, + // TODO: Make sure this is OK? We just added one more encoding .mov => |mov| .{ .mov = .{ .destination = mov.destination, @@ -361,17 +368,32 @@ pub fn Encoder(comptime options: Options) type { }, }, .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 (format) { + .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,7 +403,7 @@ pub fn Encoder(comptime options: Options) type { }, }; - const tag: Instruction.Tag = switch (token.payload) { + const tag: Instruction(format).Tag = switch (token.payload) { .nop => .mov, .jmp => .jmp, .wait => .wait, @@ -422,7 +444,7 @@ pub fn Encoder(comptime options: Options) type { delay, ); - try program.instructions.append(Instruction{ + try program.instructions.append(Instruction(format){ .tag = tag, .payload = payload, .delay_side_set = delay_side_set, @@ -485,7 +507,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(format), @bitCast(try self.evaluate(u16, program.*, word, token.index, diags))), ), // already processed .label, .wrap_target, .wrap => {}, @@ -534,87 +556,98 @@ 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 format: assembler.Format) 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, + 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, + 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(format).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(format).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(format).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(format).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(format).Instruction.Mov.Source, + operation: Token(format).Instruction.Mov.Operation, + destination: Token(format).Instruction.Mov.Destination, + }; - pub const Irq = packed struct(u8) { - index: u5, - wait: u1, - clear: u1, - reserved: u1 = 0, - }; + pub const Irq = switch (format) { + .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, + }, + }; - pub const Set = packed struct(u8) { - data: u5, - destination: Token.Instruction.Set.Destination, + pub const Set = packed struct(u8) { + data: u5, + destination: Token(format).Instruction.Set.Destination, + }; }; -}; +} //============================================================================== // Encoder Tests @@ -624,22 +657,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 format: assembler.Format, source: []const u8, diags: *?assembler.Diagnostics) !Encoder(format, .{}).Output { + const tokens = try tokenizer.tokenize(format, source, diags, .{}); + var encoder = Encoder(format, .{}).init(tokens.slice()); return try encoder.encode_output(diags); } -fn encode_bounded_output(source: []const u8) !Encoder(.{}).Output { +fn encode_bounded_output(comptime format: assembler.Format, source: []const u8) !Encoder(format, .{}).Output { var diags: ?assembler.Diagnostics = null; - return encode_bounded_output_impl(source, &diags) catch |err| if (diags) |d| blk: { + return encode_bounded_output_impl(format, 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 +683,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 +691,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 +702,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 +721,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 +740,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 +769,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 +786,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 +805,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 +824,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 +838,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 +853,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 +868,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 +889,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 +911,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 +936,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 +948,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 +958,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 +968,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 +978,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 +988,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 +998,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 +1020,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 +1057,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 +1073,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..0867d8d5 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -11,12 +11,13 @@ pub const Options = struct { }; pub fn tokenize( + comptime format: assembler.Format, 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(format), options.capacity) { + var tokens = std.BoundedArray(Token(format), options.capacity).init(0) catch unreachable; + var tokenizer = Tokenizer(format).init(source); while (try tokenizer.next(diags)) |token| try tokens.append(token); @@ -62,1082 +63,1169 @@ 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(format: assembler.Format) type { + return struct { + const Self = @This(); + format: assembler.Format, + 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; + // TODO: Avoid name collision with the format field + pub fn format2( + 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{ + .format = format, + .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); - - return args; - } + /// 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); - const PeekResult = struct { - str: []const u8, - start: u32, - }; + return args; + } - fn peek_arg(self: *Tokenizer, diags: *?Diagnostics) TokenizeError!?PeekResult { - var tmp_index = self.index; - return self.peek_arg_impl(&tmp_index, diags); - } + const PeekResult = struct { + str: []const u8, + start: u32, + }; - fn consume_peek(self: *Tokenizer, result: PeekResult) void { - assert(self.index <= result.start); - self.index = result.start + @as(u32, @intCast(result.str.len)); - } + fn peek_arg(self: *Self, diags: *?Diagnostics) TokenizeError!?PeekResult { + var tmp_index = self.index; + return self.peek_arg_impl(&tmp_index, diags); + } - /// 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 consume_peek(self: *Self, result: PeekResult) void { + assert(self.index <= result.start); + self.index = result.start + @as(u32, @intCast(result.str.len)); } - 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; + const TokenizeError = error{ + EndOfStream, + NoValue, + NotAnExpression, + Overflow, + InvalidCharacter, + InvalidSource, + InvalidCondition, + MissingArg, + InvalidDestination, + InvalidOperation, + InvalidExpression, + TooBig, }; - return Token{ - .index = index, - .data = .{ .program = name }, - }; - } - 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(format) { + const name = (try self.get_arg(diags)) orelse { + diags.* = Diagnostics.init(index, "missing program name", .{}); + return error.MissingArg; + }; + return Token(format){ + .index = index, + .data = .{ .program = name }, + }; + } - fn eql_lower(comptime lhs: []const u8, rhs: []const u8) bool { - assert_is_lower(lhs); - if (lhs.len != rhs.len) - return false; + fn assert_is_lower(str: []const u8) void { + for (str) |c| + assert(std.ascii.isLower(c)); + } - var buf: [lhs.len]u8 = undefined; - for (&buf, rhs) |*b, r| - b.* = std.ascii.toLower(r); + fn eql_lower(comptime lhs: []const u8, rhs: []const u8) bool { + assert_is_lower(lhs); + if (lhs.len != rhs.len) + return false; - return std.mem.eql(u8, &buf, lhs); - } + var buf: [lhs.len]u8 = undefined; + for (&buf, rhs) |*b, r| + b.* = std.ascii.toLower(r); - 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; + return std.mem.eql(u8, &buf, lhs); + } + + fn get_define(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(format) { + 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(format){ + .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(format) { + _ = diags; + return Token(format){ + .index = index, + .data = .{ + .origin = try self.get_value(), + }, + }; + } + + fn get_side_set(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(format) { + 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(format){ + .index = index, + .data = .{ + .side_set = .{ + .count = count, + .opt = opt, + .pindir = pindirs, + }, + }, + }; + } + + fn get_wrap_target(_: *Self, index: u32, _: *?Diagnostics) TokenizeError!Token(format) { + return Token(format){ + .index = index, + .data = .{ .wrap_target = {} }, + }; } - if (args[2]) |arg| { - if (std.mem.eql(u8, "pindirs", arg)) - pindirs = true; + fn get_wrap(_: *Self, index: u32, _: *?Diagnostics) TokenizeError!Token(format) { + return Token(format){ + .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(format) { + _ = diags; + return Token(format){ + .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(format) { + _ = diags; + return Token(format){ + .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(format)).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_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 get_directive(self: *Self, diags: *?Diagnostics) !Token(format) { + 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(format).Instruction.Payload { + return Token(format).Instruction.Payload{ + .nop = {}, + }; + } + + fn target_from_string(str: []const u8) TokenizeError!Token(format).Instruction.Jmp.Target { + const value = Value.from_string(str); + return Token(format).Instruction.Payload{ + .jmp = .{ + .condition = .always, + .target = switch (value) { + .string => |label| Token(format).Instruction.Jmp.Target{ + .label = label, + }, + else => Token(format).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(format).Instruction.Payload { + const Condition = Token(format).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(format).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(format).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(format).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; - 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(format).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(format).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(format).Instruction.Payload{ + .in = .{ + .source = std.meta.stringToEnum(Token(format).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(format).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(format).Instruction.Payload{ + .out = .{ + .destination = std.meta.stringToEnum(Token(format).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(format).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()); - break :ret Token.Instruction.Payload{ + 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(format).Instruction.Payload{ + .push = .{ + .iffull = iffull, + .block = block, + }, + }; + } else Token(format).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(format).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(format).Instruction.Payload{ + .pull = .{ + .ifempty = ifempty, + .block = block, + }, + }; + } else Token(format).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(format).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(format).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(format).Instruction.Mov.Source, source_lower.slice()) orelse return error.InvalidSource; + const operation: Token(format).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(format).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; - } - else - first, - }; - - 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(format).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 format) { + .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(format).Instruction.Payload{ + .irq = .{ + .clear = clear, + .wait = wait, + .num = num, + .rel = rel, + }, + }; + }, + .RP2350 => { + // This doesn't work: idx_mode's type only exists at comptime, + // even though this is a comptime-only switch :/ + // Hardcoding the type + const idx_mode: u2 = 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())) { + // @compileLog("got rel"); // DELETEME + self.consume_peek(result); + break :blk 0b10; + // break :blk .rel; + } else if (std.mem.eql(u8, "prev", idxmode_lower.slice())) { + // @compileLog("got prev"); // DELETEME + self.consume_peek(result); + break :blk 0b01; + // break :blk .prev; + } else if (std.mem.eql(u8, "next", idxmode_lower.slice())) { + // @compileLog("got next"); // DELETEME + self.consume_peek(result); + break :blk 0b11; + // break :blk .next; + } else { + // Not specified: direct + break :blk 0b00; + // break :blk .direct; + } + } else 0b00; + // } else .direct; + + return Token(format).Instruction.Payload{ + .irq = .{ + .clear = clear, + .wait = wait, + .num = num, + .idxmode = @enumFromInt(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(format).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(format).Instruction.Payload{ + .set = .{ + .destination = std.meta.stringToEnum(Token(format).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(format).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(format) { + 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(format){ + .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(format) { + 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(format){ + .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(format){ + .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 format: assembler.Format) 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(format), .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, + 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 (format) { + .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 Pull = struct { - block: bool, - ifempty: 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 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 Push = struct { + block: bool, + iffull: bool, }; - pub const Operation = enum(u2) { - none = 0b00, - invert = 0b01, - bit_reverse = 0b10, + pub const Pull = struct { + block: bool, + ifempty: bool, }; - pub const Source = enum(u3) { - pins = 0b00, - x = 0b001, - y = 0b010, - null = 0b011, - status = 0b101, - isr = 0b110, - osr = 0b111, + // TODO: Add mov to RX for rp2350 + pub const Mov = struct { + destination: Destination, + operation: Operation, + source: Source, + + pub const Destination = switch (format) { + .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 = 0b00, + x = 0b001, + y = 0b010, + null = 0b011, + status = 0b101, + isr = 0b110, + osr = 0b111, + }; }; - }; - pub const Irq = struct { - clear: bool, - wait: bool, - num: Value, - rel: bool, - }; + pub const Irq = switch (format) { + .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 +1235,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 format: assembler.Format) type { + return @typeInfo(Token(format).Directive).Union.tag_type.?; +} +fn PayloadTag(comptime format: assembler.Format) type { + return @typeInfo(Token(format).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 format: assembler.Format, expected: []const u8, actual: Token(format)) !void { + try expectEqual(Token(format).Tag.program, @as(Token(format).Tag, actual.data)); try expectEqualStrings(expected, actual.data.program); } @@ -1172,21 +1264,21 @@ 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 format: assembler.Format, expected: Token(format).Define, actual: Token(format)) !void { + try expectEqual(Token(format).Tag.define, @as(Token(format).Tag, actual.data)); const define = actual.data.define; try expectEqualStrings(expected.name, define.name); try expect_value(expected.value, define.value); } -fn expect_origin(expected: Value, actual: Token) !void { - try expectEqual(Token.Tag.origin, @as(Token.Tag, actual.data)); +fn expect_origin(comptime format: assembler.Format, expected: Value, actual: Token(format)) !void { + try expectEqual(Token(format).Tag.origin, @as(Token(format).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 format: assembler.Format, expected: Token(format).SideSet, actual: Token(format)) !void { + try expectEqual(Token(format).Tag.side_set, @as(Token(format).Tag, actual.data)); const side_set = actual.data.side_set; try expect_value(expected.count, side_set.count); @@ -1194,16 +1286,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 format: assembler.Format, actual: Token(format)) !void { + try expectEqual(Token(format).Tag.wrap_target, @as(Token(format).Tag, actual.data)); } -fn expect_wrap(actual: Token) !void { - try expectEqual(Token.Tag.wrap, @as(Token.Tag, actual.data)); +fn expect_wrap(comptime format: assembler.Format, actual: Token(format)) !void { + try expectEqual(Token(format).Tag.wrap, @as(Token(format).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 format: assembler.Format, expected: Token(format).LangOpt, actual: Token(format)) !void { + try expectEqual(Token(format).Tag.lang_opt, @as(Token(format).Tag, actual.data)); const lang_opt = actual.data.lang_opt; try expectEqualStrings(expected.lang, lang_opt.lang); @@ -1211,13 +1303,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 format: assembler.Format, expected: Value, actual: Token(format)) !void { + try expectEqual(Token(format).Tag.word, @as(Token(format).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 format: assembler.Format, expected: Token(format).Label, actual: Token(format)) !void { + try expectEqual(Token(format).Tag.label, @as(Token(format).Tag, actual.data)); const label = actual.data.label; try expectEqual(expected.public, label.public); @@ -1229,25 +1321,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 format: assembler.Format, expected: ExpectedNopInstr, actual: Token(format)) !void { + try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); + try expectEqual(PayloadTag(format).nop, @as(PayloadTag(format), 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 format: assembler.Format) type { + return struct { + dest: Token(format).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 format: assembler.Format, expected: ExpectedSetInstr(format), actual: Token(format)) !void { + try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); + try expectEqual(PayloadTag(format).set, @as(PayloadTag(format), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1258,16 +1352,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 format: assembler.Format) type { + return struct { + cond: Token(format).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 format: assembler.Format, expected: ExpectedJmpInstr(format), actual: Token(format)) !void { + try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); + try expectEqual(PayloadTag(format).jmp, @as(PayloadTag(format), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1278,19 +1374,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 format: assembler.Format) type { + return struct { + polarity: u1, + source: Token(format).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 format: assembler.Format, expected: ExpectedWaitInstr(format), actual: Token(format)) !void { + try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); + try expectEqual(PayloadTag(format).wait, @as(PayloadTag(format), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1302,16 +1400,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 format: assembler.Format) type { + return struct { + source: Token(format).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 format: assembler.Format, expected: ExpectedInInstr(format), actual: Token(format)) !void { + try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); + try expectEqual(PayloadTag(format).in, @as(PayloadTag(format), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1322,16 +1422,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 format: assembler.Format) type { + return struct { + destination: Token(format).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 format: assembler.Format, expected: ExpectedOutInstr(format), actual: Token(format)) !void { + try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); + try expectEqual(PayloadTag(format).out, @as(PayloadTag(format), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1349,9 +1451,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 format: assembler.Format, expected: ExpectedPushInstr, actual: Token(format)) !void { + try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); + try expectEqual(PayloadTag(format).push, @as(PayloadTag(format), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1369,9 +1471,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 format: assembler.Format, expected: ExpectedPullInstr, actual: Token(format)) !void { + try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); + try expectEqual(PayloadTag(format).pull, @as(PayloadTag(format), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1382,17 +1484,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 format: assembler.Format) type { + return struct { + source: Token(format).Instruction.Mov.Source, + destination: Token(format).Instruction.Mov.Destination, + operation: Token(format).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 format: assembler.Format, expected: ExpectedMovInstr(format), actual: Token(format)) !void { + try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); + try expectEqual(PayloadTag(format).mov, @as(PayloadTag(format), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1404,18 +1508,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 format: assembler.Format) type { + return switch (format) { + .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 format: assembler.Format, expected: ExpectedIrqInstr(format), actual: Token(format)) !void { + try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); + try expectEqual(PayloadTag(format).irq, @as(PayloadTag(format), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1424,41 +1540,48 @@ 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 (format) { + .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 format: assembler.Format, source: []const u8) !std.BoundedArray(Token(format), 256) { var diags: ?assembler.Diagnostics = null; - return tokenize(source, &diags, .{}) catch |err| if (diags) |d| blk: { + return tokenize(format, 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 "tokenize.empty string" { - const tokens = try bounded_tokenize(""); + const tokens = try bounded_tokenize(.RP2040, ""); try expectEqual(@as(usize, 0), tokens.len); } test "tokenize.whitespace" { - const tokens = try bounded_tokenize(" \t\r\n"); + const tokens = try bounded_tokenize(.RP2040, " \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"); + const tokens = try bounded_tokenize(.RP2040, "; this is a line comment"); try expectEqual(@as(usize, 0), tokens.len); } test "tokenize.slash line comment" { - const tokens = try bounded_tokenize("// this is a line comment"); + const tokens = try bounded_tokenize(.RP2040, "// this is a line comment"); try expectEqual(@as(usize, 0), tokens.len); } test "tokenize.block comment" { - const tokens = try bounded_tokenize( + const tokens = try bounded_tokenize(.RP2040, \\/* this is \\ a block comment */ ); @@ -1467,158 +1590,195 @@ test "tokenize.block comment" { } test "tokenize.code block" { - const tokens = try bounded_tokenize( - \\% c-sdk { - \\ int foo; - \\%} - ); - - try expectEqual(@as(usize, 0), tokens.len); + inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const tokens = try bounded_tokenize(format, + \\% 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 .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const tokens = try bounded_tokenize(format, ".program arst"); + try expect_program(format, "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 .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const tokens = try bounded_tokenize(format, ".define symbol_name 1"); + try expect_define(format, .{ + .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 .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const tokens = try bounded_tokenize(format, ".define public symbol_name 0x1"); + try expect_define(format, .{ + .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 .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const tokens = try bounded_tokenize(format, + \\.define symbol_name 0x1 + \\.define something (symbol_name * 2) + ); + + try expect_define(format, .{ + .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(format, .{ + .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 .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const tokens = try bounded_tokenize(format, ".origin 0x10"); + try expect_origin(format, .{ .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 .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const tokens = try bounded_tokenize(format, ".side_set 1"); + try expect_side_set(format, .{ .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 .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const tokens = try bounded_tokenize(format, ".side_set 1 opt"); + try expect_side_set(format, .{ .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 .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const tokens = try bounded_tokenize(format, ".side_set 1 pindirs"); + try expect_side_set(format, .{ .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 .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const tokens = try bounded_tokenize(format, ".wrap_target"); + try expect_wrap_target(format, tokens.get(0)); + } } test "tokenize.directive.wrap" { - const tokens = try bounded_tokenize(".wrap"); - try expect_wrap(tokens.get(0)); + inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const tokens = try bounded_tokenize(format, ".wrap"); + try expect_wrap(format, 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 .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const tokens = try bounded_tokenize(format, ".lang_opt c flag foo"); + try expect_lang_opt(format, .{ .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 .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const tokens = try bounded_tokenize(format, ".word 0xaaaa"); + try expect_word(format, .{ .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 .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const tokens = try bounded_tokenize(format, "my_label:"); + try expect_label(format, .{ .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 .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const tokens = try bounded_tokenize(format, "public my_label:"); + try expect_label(format, .{ .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 .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const tokens = try bounded_tokenize(format, "nop"); + try expect_instr_nop(format, .{}, 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 .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const tokens = try bounded_tokenize(format, "jmp my_label"); + try expect_instr_jmp(format, .{ .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 .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const tokens = try bounded_tokenize(format, "jmp 0x2"); + try expect_instr_jmp(format, .{ .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 .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const Condition = Token(format).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(format, 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(format, .{ .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 .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (.{ "gpio", "pin", "irq" }) |source| { + const tokens = try bounded_tokenize(format, comptime std.fmt.comptimePrint("wait 0 {s} 1", .{source})); + try expect_instr_wait(format, .{ + .polarity = 0, + .source = @field(Token(format).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 }, @@ -1635,13 +1795,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,67 +1818,67 @@ 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)); @@ -1734,10 +1894,10 @@ test "tokenize.instr.mov" { "isr", "osr", }) |source| { - const tokens = try bounded_tokenize(comptime std.fmt.comptimePrint("mov x {s}", .{source})); + const tokens = try bounded_tokenize(.RP2040, comptime std.fmt.comptimePrint("mov x {s}", .{source})); - try expect_instr_mov(.{ - .source = @field(Token.Instruction.Mov.Source, source), + try expect_instr_mov(.RP2040, .{ + .source = @field(Token(.RP2040).Instruction.Mov.Source, source), .destination = .x, }, tokens.get(0)); } @@ -1751,15 +1911,15 @@ test "tokenize.instr.mov" { "isr", "osr", }) |dest| { - const tokens = try bounded_tokenize(comptime std.fmt.comptimePrint("mov {s} x", .{dest})); + const tokens = try bounded_tokenize(.RP2040, comptime std.fmt.comptimePrint("mov {s} x", .{dest})); - try expect_instr_mov(.{ + try expect_instr_mov(.RP2040, .{ .source = .x, - .destination = @field(Token.Instruction.Mov.Destination, dest), + .destination = @field(Token(.RP2040).Instruction.Mov.Destination, dest), }, tokens.get(0)); } - const Operation = Token.Instruction.Mov.Operation; + const Operation = Token(.RP2040).Instruction.Mov.Operation; const operations = std.StaticStringMap(Operation).initComptime(.{ .{ "!", .invert }, .{ "~", .invert }, @@ -1768,12 +1928,12 @@ test "tokenize.instr.mov" { 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", .{ + const tokens = try bounded_tokenize(.RP2040, comptime std.fmt.comptimePrint("mov x {s}{s}y", .{ str, space, })); - try expect_instr_mov(.{ + try expect_instr_mov(.RP2040, .{ .destination = .x, .operation = operation, .source = .y, @@ -1797,22 +1957,25 @@ 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 .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + const tokens = try bounded_tokenize(format, 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(format, .{ + .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"); + // TODO: rp2350 support + try expect_instr_irq(.RP2040, .{ .clear = false, .wait = false, .num = 2, @@ -1827,17 +1990,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 +2021,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 +2040,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 +2051,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 +2062,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 +2073,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 +2086,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 +2098,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 b36fc16f..4b4d1851 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/common.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/common.zig @@ -9,11 +9,13 @@ pub const PIO0 = microzig.chip.peripherals.PIO0; pub const PIO1 = microzig.chip.peripherals.PIO1; pub const assembler = @import("assembler.zig"); +const compatibility = @import("../compatibility.zig"); const encoder = @import("assembler/encoder.zig"); const gpio = @import("../gpio.zig"); const hw = @import("../hw.zig"); -pub const Instruction = encoder.Instruction; +// TODO: This is CPU specific +pub const Instruction = encoder.Instruction(assembler.cpuToFormat(compatibility.cpu)); pub const Program = assembler.Program; // global state for keeping track of used things From 59cdab0e51dd5085ae2c62118953ded14a1106ef Mon Sep 17 00:00:00 2001 From: Hayden Riddiford Date: Thu, 19 Dec 2024 11:41:38 -0800 Subject: [PATCH 02/30] - Reorganized some of the comptime behavior surrounding CPU --- port/raspberrypi/rp2xxx/src/hal/pio.zig | 6 +- .../rp2xxx/src/hal/pio/assembler.zig | 27 +- .../rp2xxx/src/hal/pio/assembler/encoder.zig | 68 ++-- .../src/hal/pio/assembler/tokenizer.zig | 330 +++++++++--------- .../raspberrypi/rp2xxx/src/hal/pio/common.zig | 162 +++++---- .../raspberrypi/rp2xxx/src/hal/pio/rp2040.zig | 63 ++-- .../raspberrypi/rp2xxx/src/hal/pio/rp2350.zig | 64 ++-- 7 files changed, 359 insertions(+), 361 deletions(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio.zig b/port/raspberrypi/rp2xxx/src/hal/pio.zig index 76bf65b7..610bf0c5 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 (cpu) { .RP2350 => @import("pio/rp2350.zig"), }; pub const StateMachine = common.StateMachine; -pub const Instruction = common.Instruction; +pub const Instruction = common.Instruction(cpu); 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(cpu, source, options); +} pub fn num(n: u2) Pio { switch (cpu) { diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig index 949155cf..0de9635b 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig @@ -2,7 +2,6 @@ const std = @import("std"); const assert = std.debug.assert; const CPU = @import("../cpu.zig").CPU; -// TODO: Isn't this circular? const tokenizer = @import("assembler/tokenizer.zig"); const encoder = @import("assembler/encoder.zig"); @@ -73,9 +72,9 @@ pub const Diagnostics = struct { } }; -pub fn assemble_impl(comptime format: Format, comptime source: []const u8, diags: *?Diagnostics, options: AssembleOptions) !Output { - const tokens = try tokenizer.tokenize(format, source, diags, options.tokenize); - const encoder_output = try encoder.encode(format, tokens.slice(), diags, options.encode); +pub fn assemble_impl(comptime cpu: CPU, comptime source: []const u8, diags: *?Diagnostics, options: AssembleOptions) !Output { + const tokens = try tokenizer.tokenize(cpu, source, diags, options.tokenize); + const encoder_output = try encoder.encode(cpu, 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()); @@ -123,25 +122,9 @@ fn format_compile_error(comptime message: []const u8, comptime source: []const u }); } -// TODO: Place. Who should own this? -pub const Format = enum { - RP2040, - RP2350, -}; - -pub fn cpuToFormat(cpu: CPU) Format { - return switch (cpu) { - .RP2040 => .RP2040, - .RP2350 => .RP2350, - }; -} - -pub fn assemble(comptime source: []const u8, comptime options: AssembleOptions) Output { +pub fn assemble(comptime cpu: CPU, comptime source: []const u8, comptime options: AssembleOptions) Output { var diags: ?Diagnostics = null; - // TODO: We can't import compatibility & zig build test since it depends on microzig - // cpuToFormat(cpu) instead of hardcoding - // const cpu = @import("../compatibility.zig").cpu; - return assemble_impl(.RP2040, source, &diags, options) catch |err| if (diags) |d| + return assemble_impl(cpu, source, &diags, options) catch |err| if (diags) |d| @compileError(format_compile_error(d.message.slice(), source, d.index)) else @compileError(err); diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig index 5928a2a5..8f5ceac8 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig @@ -5,14 +5,11 @@ const assembler = @import("../assembler.zig"); const Diagnostics = assembler.Diagnostics; const tokenizer = @import("tokenizer.zig"); -// TODO: How do I pass in the format to this module? There is no (comptime) -// instance object -// const format: assembler.Format = .RP2350; -// const Token = tokenizer.Token(format); const Token = tokenizer.Token; const Value = tokenizer.Value; const Expression = @import("Expression.zig"); +const CPU = @import("../../cpu.zig").CPU; pub const Options = struct { max_defines: u32 = 16, @@ -20,13 +17,12 @@ pub const Options = struct { }; pub fn encode( - // We don't want the user to have to specify this - comptime format: assembler.Format, - comptime tokens: []const Token(format), + comptime cpu: CPU, + comptime tokens: []const Token(cpu), diags: *?assembler.Diagnostics, comptime options: Options, -) !Encoder(format, options).Output { - var encoder = Encoder(format, options).init(tokens); +) !Encoder(cpu, options).Output { + var encoder = Encoder(cpu, options).init(tokens); return try encoder.encode_output(diags); } @@ -47,10 +43,10 @@ pub const DefineWithIndex = struct { index: u32, }; -pub fn Encoder(comptime format: assembler.Format, comptime options: Options) type { +pub fn Encoder(comptime cpu: CPU, comptime options: Options) type { return struct { output: Self.Output, - tokens: []const Token(format), + tokens: []const Token(cpu), index: u32, const Self = @This(); @@ -62,7 +58,7 @@ pub fn Encoder(comptime format: assembler.Format, comptime options: Options) typ const BoundedDefines = std.BoundedArray(DefineWithIndex, options.max_defines); const BoundedPrograms = std.BoundedArray(BoundedProgram, options.max_programs); - const BoundedInstructions = std.BoundedArray(Instruction(format), 32); + const BoundedInstructions = std.BoundedArray(Instruction(cpu), 32); const BoundedLabels = std.BoundedArray(Label, 32); const Label = struct { name: []const u8, @@ -111,7 +107,7 @@ pub fn Encoder(comptime format: assembler.Format, comptime options: Options) typ } }; - fn init(tokens: []const Token(format)) Self { + fn init(tokens: []const Token(cpu)) Self { return Self{ .output = Self.Output{ .global_defines = BoundedDefines.init(0) catch unreachable, @@ -123,14 +119,14 @@ pub fn Encoder(comptime format: assembler.Format, comptime options: Options) typ }; } - fn peek_token(self: Self) ?Token(format) { + fn peek_token(self: Self) ?Token(cpu) { return if (self.index < self.tokens.len) self.tokens[self.index] else null; } - fn get_token(self: *Self) ?Token(format) { + fn get_token(self: *Self) ?Token(cpu) { return if (self.peek_token()) |token| blk: { self.consume(1); break :blk token; @@ -309,12 +305,12 @@ pub fn Encoder(comptime format: assembler.Format, comptime options: Options) typ fn encode_instruction( self: *Self, program: *BoundedProgram, - token: Token(format).Instruction, + token: Token(cpu).Instruction, token_index: u32, diags: *?Diagnostics, ) !void { // guaranteed to be an instruction variant - const payload: Instruction(format).Payload = switch (token.payload) { + const payload: Instruction(cpu).Payload = switch (token.payload) { .nop => .{ .mov = .{ .destination = .y, @@ -368,7 +364,7 @@ pub fn Encoder(comptime format: assembler.Format, comptime options: Options) typ }, }, .irq => |irq| blk: { - switch (format) { + switch (cpu) { .RP2040 => { const irq_num = try self.evaluate(u5, program.*, irq.num, token_index, diags); break :blk .{ @@ -403,7 +399,7 @@ pub fn Encoder(comptime format: assembler.Format, comptime options: Options) typ }, }; - const tag: Instruction(format).Tag = switch (token.payload) { + const tag: Instruction(cpu).Tag = switch (token.payload) { .nop => .mov, .jmp => .jmp, .wait => .wait, @@ -444,7 +440,7 @@ pub fn Encoder(comptime format: assembler.Format, comptime options: Options) typ delay, ); - try program.instructions.append(Instruction(format){ + try program.instructions.append(Instruction(cpu){ .tag = tag, .payload = payload, .delay_side_set = delay_side_set, @@ -507,7 +503,7 @@ pub fn Encoder(comptime format: assembler.Format, comptime options: Options) typ switch (token.data) { .instruction => |instr| try self.encode_instruction(program, instr, token.index, diags), .word => |word| try program.instructions.append( - @as(Instruction(format), @bitCast(try self.evaluate(u16, program.*, word, token.index, diags))), + @as(Instruction(cpu), @bitCast(try self.evaluate(u16, program.*, word, token.index, diags))), ), // already processed .label, .wrap_target, .wrap => {}, @@ -556,7 +552,7 @@ pub fn Encoder(comptime format: assembler.Format, comptime options: Options) typ }; } -pub fn Instruction(comptime format: assembler.Format) type { +pub fn Instruction(comptime cpu: CPU) type { return packed struct(u16) { payload: Payload, delay_side_set: u5, @@ -587,23 +583,23 @@ pub fn Instruction(comptime format: assembler.Format) type { pub const Jmp = packed struct(u8) { address: u5, - condition: Token(format).Instruction.Jmp.Condition, + condition: Token(cpu).Instruction.Jmp.Condition, }; pub const Wait = packed struct(u8) { index: u5, - source: Token(format).Instruction.Wait.Source, + source: Token(cpu).Instruction.Wait.Source, polarity: u1, }; pub const In = packed struct(u8) { bit_count: u5, - source: Token(format).Instruction.In.Source, + source: Token(cpu).Instruction.In.Source, }; pub const Out = packed struct(u8) { bit_count: u5, - destination: Token(format).Instruction.Out.Destination, + destination: Token(cpu).Instruction.Out.Destination, }; pub const Push = packed struct(u8) { @@ -621,12 +617,12 @@ pub fn Instruction(comptime format: assembler.Format) type { }; pub const Mov = packed struct(u8) { - source: Token(format).Instruction.Mov.Source, - operation: Token(format).Instruction.Mov.Operation, - destination: Token(format).Instruction.Mov.Destination, + source: Token(cpu).Instruction.Mov.Source, + operation: Token(cpu).Instruction.Mov.Operation, + destination: Token(cpu).Instruction.Mov.Destination, }; - pub const Irq = switch (format) { + pub const Irq = switch (cpu) { .RP2040 => packed struct(u8) { index: u5, wait: u1, @@ -644,7 +640,7 @@ pub fn Instruction(comptime format: assembler.Format) type { pub const Set = packed struct(u8) { data: u5, - destination: Token(format).Instruction.Set.Destination, + destination: Token(cpu).Instruction.Set.Destination, }; }; } @@ -657,15 +653,15 @@ const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; const expectEqualStrings = std.testing.expectEqualStrings; -fn encode_bounded_output_impl(comptime format: assembler.Format, source: []const u8, diags: *?assembler.Diagnostics) !Encoder(format, .{}).Output { - const tokens = try tokenizer.tokenize(format, source, diags, .{}); - var encoder = Encoder(format, .{}).init(tokens.slice()); +fn encode_bounded_output_impl(comptime cpu: CPU, source: []const u8, diags: *?assembler.Diagnostics) !Encoder(cpu, .{}).Output { + const tokens = try tokenizer.tokenize(cpu, source, diags, .{}); + var encoder = Encoder(cpu, .{}).init(tokens.slice()); return try encoder.encode_output(diags); } -fn encode_bounded_output(comptime format: assembler.Format, source: []const u8) !Encoder(format, .{}).Output { +fn encode_bounded_output(comptime cpu: CPU, source: []const u8) !Encoder(cpu, .{}).Output { var diags: ?assembler.Diagnostics = null; - return encode_bounded_output_impl(format, source, &diags) catch |err| if (diags) |d| blk: { + return encode_bounded_output_impl(cpu, 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; diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index 0867d8d5..44d95e12 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -5,19 +5,20 @@ const assembler = @import("../assembler.zig"); const Diagnostics = assembler.Diagnostics; const Expression = @import("Expression.zig"); +const CPU = @import("../../cpu.zig").CPU; pub const Options = struct { capacity: u32 = 256, }; pub fn tokenize( - comptime format: assembler.Format, + comptime cpu: CPU, source: []const u8, diags: *?assembler.Diagnostics, comptime options: Options, -) !std.BoundedArray(Token(format), options.capacity) { - var tokens = std.BoundedArray(Token(format), options.capacity).init(0) catch unreachable; - var tokenizer = Tokenizer(format).init(source); +) !std.BoundedArray(Token(cpu), options.capacity) { + var tokens = std.BoundedArray(Token(cpu), options.capacity).init(0) catch unreachable; + var tokenizer = Tokenizer(cpu).init(source); while (try tokenizer.next(diags)) |token| try tokens.append(token); @@ -63,15 +64,13 @@ pub const Value = union(enum) { // '/' -> '*' -> block comment // '%' -> -> -> -> '{' -> code block // '.' -> directive -pub fn Tokenizer(format: assembler.Format) type { +pub fn Tokenizer(cpu: CPU) type { return struct { const Self = @This(); - format: assembler.Format, source: []const u8, index: u32, - // TODO: Avoid name collision with the format field - pub fn format2( + pub fn format( self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, @@ -101,7 +100,6 @@ pub fn Tokenizer(format: assembler.Format) type { fn init(source: []const u8) Self { return Self{ - .format = format, .source = source, .index = 0, }; @@ -312,12 +310,12 @@ pub fn Tokenizer(format: assembler.Format) type { TooBig, }; - fn get_program(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(format) { + fn get_program(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(cpu) { const name = (try self.get_arg(diags)) orelse { diags.* = Diagnostics.init(index, "missing program name", .{}); return error.MissingArg; }; - return Token(format){ + return Token(cpu){ .index = index, .data = .{ .program = name }, }; @@ -340,7 +338,7 @@ pub fn Tokenizer(format: assembler.Format) type { return std.mem.eql(u8, &buf, lhs); } - fn get_define(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(format) { + fn get_define(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(cpu) { const maybe_public = try self.get_identifier(); const is_public = eql_lower("public", maybe_public.str); @@ -349,7 +347,7 @@ pub fn Tokenizer(format: assembler.Format) type { else maybe_public; - return Token(format){ + return Token(cpu){ .index = index, .data = .{ .define = .{ @@ -408,9 +406,9 @@ pub fn Tokenizer(format: assembler.Format) type { return error.NoValue; } - fn get_origin(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(format) { + fn get_origin(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(cpu) { _ = diags; - return Token(format){ + return Token(cpu){ .index = index, .data = .{ .origin = try self.get_value(), @@ -418,7 +416,7 @@ pub fn Tokenizer(format: assembler.Format) type { }; } - fn get_side_set(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(format) { + fn get_side_set(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(cpu) { const args = try self.get_args(3, diags); const count = try Value.from_string(args[0] orelse { diags.* = Diagnostics.init(index, "missing count", .{}); @@ -439,7 +437,7 @@ pub fn Tokenizer(format: assembler.Format) type { pindirs = true; } - return Token(format){ + return Token(cpu){ .index = index, .data = .{ .side_set = .{ @@ -451,23 +449,23 @@ pub fn Tokenizer(format: assembler.Format) type { }; } - fn get_wrap_target(_: *Self, index: u32, _: *?Diagnostics) TokenizeError!Token(format) { - return Token(format){ + fn get_wrap_target(_: *Self, index: u32, _: *?Diagnostics) TokenizeError!Token(cpu) { + return Token(cpu){ .index = index, .data = .{ .wrap_target = {} }, }; } - fn get_wrap(_: *Self, index: u32, _: *?Diagnostics) TokenizeError!Token(format) { - return Token(format){ + fn get_wrap(_: *Self, index: u32, _: *?Diagnostics) TokenizeError!Token(cpu) { + return Token(cpu){ .index = index, .data = .{ .wrap = {} }, }; } - fn get_lang_opt(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(format) { + fn get_lang_opt(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(cpu) { _ = diags; - return Token(format){ + return Token(cpu){ .index = index, .data = .{ .lang_opt = .{ @@ -479,15 +477,15 @@ pub fn Tokenizer(format: assembler.Format) type { }; } - fn get_word(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(format) { + fn get_word(self: *Self, index: u32, diags: *?Diagnostics) TokenizeError!Token(cpu) { _ = diags; - return Token(format){ + return Token(cpu){ .index = index, .data = .{ .word = try self.get_value() }, }; } - const directives = std.StaticStringMap(*const fn (*Self, u32, *?Diagnostics) TokenizeError!Token(format)).initComptime(.{ + const directives = std.StaticStringMap(*const fn (*Self, u32, *?Diagnostics) TokenizeError!Token(cpu)).initComptime(.{ .{ "program", get_program }, .{ "define", get_define }, .{ "origin", get_origin }, @@ -498,7 +496,7 @@ pub fn Tokenizer(format: assembler.Format) type { .{ "word", get_word }, }); - fn get_directive(self: *Self, diags: *?Diagnostics) !Token(format) { + fn get_directive(self: *Self, diags: *?Diagnostics) !Token(cpu) { const index = self.index; const identifier = try self.read_until_whitespace_or_end(); return if (directives.get(identifier)) |handler| ret: { @@ -508,22 +506,22 @@ pub fn Tokenizer(format: assembler.Format) type { } else error.InvalidDirective; } - fn get_nop(_: *Self, _: *?Diagnostics) TokenizeError!Token(format).Instruction.Payload { - return Token(format).Instruction.Payload{ + fn get_nop(_: *Self, _: *?Diagnostics) TokenizeError!Token(cpu).Instruction.Payload { + return Token(cpu).Instruction.Payload{ .nop = {}, }; } - fn target_from_string(str: []const u8) TokenizeError!Token(format).Instruction.Jmp.Target { + fn target_from_string(str: []const u8) TokenizeError!Token(cpu).Instruction.Jmp.Target { const value = Value.from_string(str); - return Token(format).Instruction.Payload{ + return Token(cpu).Instruction.Payload{ .jmp = .{ .condition = .always, .target = switch (value) { - .string => |label| Token(format).Instruction.Jmp.Target{ + .string => |label| Token(cpu).Instruction.Jmp.Target{ .label = label, }, - else => Token(format).Instruction.Jmp.Target{ + else => Token(cpu).Instruction.Jmp.Target{ .value = value, }, }, @@ -531,8 +529,8 @@ pub fn Tokenizer(format: assembler.Format) type { }; } - fn get_jmp(self: *Self, diags: *?Diagnostics) TokenizeError!Token(format).Instruction.Payload { - const Condition = Token(format).Instruction.Jmp.Condition; + fn get_jmp(self: *Self, diags: *?Diagnostics) TokenizeError!Token(cpu).Instruction.Payload { + const Condition = Token(cpu).Instruction.Jmp.Condition; const conditions = std.StaticStringMap(Condition).initComptime(.{ .{ "!x", .x_is_zero }, .{ "x--", .x_dec }, @@ -551,12 +549,12 @@ pub fn Tokenizer(format: assembler.Format) type { else (try self.get_arg(diags)) orelse return error.MissingArg; - return Token(format).Instruction.Payload{ + return Token(cpu).Instruction.Payload{ .jmp = .{ .condition = cond, .target = target_str }, }; } - fn get_wait(self: *Self, diags: *?Diagnostics) TokenizeError!Token(format).Instruction.Payload { + fn get_wait(self: *Self, diags: *?Diagnostics) TokenizeError!Token(cpu).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); @@ -566,7 +564,7 @@ pub fn Tokenizer(format: assembler.Format) type { buf[i] = std.ascii.toLower(c); const source_lower = buf[0..source_str.len]; - const source: Token(format).Instruction.Wait.Source = + const source: Token(cpu).Instruction.Wait.Source = if (std.mem.eql(u8, "gpio", source_lower)) .gpio else if (std.mem.eql(u8, "pin", source_lower)) @@ -587,7 +585,7 @@ pub fn Tokenizer(format: assembler.Format) type { else false; - return Token(format).Instruction.Payload{ + return Token(cpu).Instruction.Payload{ .wait = .{ .polarity = polarity, .source = source, @@ -613,7 +611,7 @@ pub fn Tokenizer(format: assembler.Format) type { // 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: *Self, diags: *?Diagnostics) TokenizeError!Token(format).Instruction.Payload { + fn get_in(self: *Self, diags: *?Diagnostics) TokenizeError!Token(cpu).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; @@ -624,15 +622,15 @@ pub fn Tokenizer(format: assembler.Format) type { else @as(u5, @intCast(bit_count_tmp)); - return Token(format).Instruction.Payload{ + return Token(cpu).Instruction.Payload{ .in = .{ - .source = std.meta.stringToEnum(Token(format).Instruction.In.Source, source_lower.slice()) orelse return error.InvalidSource, + .source = std.meta.stringToEnum(Token(cpu).Instruction.In.Source, source_lower.slice()) orelse return error.InvalidSource, .bit_count = bit_count, }, }; } - fn get_out(self: *Self, diags: *?Diagnostics) TokenizeError!Token(format).Instruction.Payload { + fn get_out(self: *Self, diags: *?Diagnostics) TokenizeError!Token(cpu).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; @@ -643,9 +641,9 @@ pub fn Tokenizer(format: assembler.Format) type { else @as(u5, @intCast(bit_count_tmp)); - return Token(format).Instruction.Payload{ + return Token(cpu).Instruction.Payload{ .out = .{ - .destination = std.meta.stringToEnum(Token(format).Instruction.Out.Destination, dest_lower.slice()) orelse return error.InvalidDestination, + .destination = std.meta.stringToEnum(Token(cpu).Instruction.Out.Destination, dest_lower.slice()) orelse return error.InvalidDestination, .bit_count = bit_count, }, }; @@ -667,7 +665,7 @@ pub fn Tokenizer(format: assembler.Format) type { true; } - fn get_push(self: *Self, diags: *?Diagnostics) TokenizeError!Token(format).Instruction.Payload { + fn get_push(self: *Self, diags: *?Diagnostics) TokenizeError!Token(cpu).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()); @@ -680,13 +678,13 @@ pub fn Tokenizer(format: assembler.Format) type { true; } else try self.block_from_peek(first_result); - break :ret Token(format).Instruction.Payload{ + break :ret Token(cpu).Instruction.Payload{ .push = .{ .iffull = iffull, .block = block, }, }; - } else Token(format).Instruction.Payload{ + } else Token(cpu).Instruction.Payload{ .push = .{ .iffull = false, .block = true, @@ -694,7 +692,7 @@ pub fn Tokenizer(format: assembler.Format) type { }; } - fn get_pull(self: *Self, diags: *?Diagnostics) TokenizeError!Token(format).Instruction.Payload { + fn get_pull(self: *Self, diags: *?Diagnostics) TokenizeError!Token(cpu).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()); @@ -707,13 +705,13 @@ pub fn Tokenizer(format: assembler.Format) type { true; } else try self.block_from_peek(first_result); - break :ret Token(format).Instruction.Payload{ + break :ret Token(cpu).Instruction.Payload{ .pull = .{ .ifempty = ifempty, .block = block, }, }; - } else Token(format).Instruction.Payload{ + } else Token(cpu).Instruction.Payload{ .pull = .{ .ifempty = false, .block = true, @@ -721,10 +719,10 @@ pub fn Tokenizer(format: assembler.Format) type { }; } - fn get_mov(self: *Self, diags: *?Diagnostics) TokenizeError!Token(format).Instruction.Payload { + fn get_mov(self: *Self, diags: *?Diagnostics) TokenizeError!Token(cpu).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(format).Instruction.Mov.Destination, dest_lower.slice()) orelse return error.InvalidDestination; + const destination = std.meta.stringToEnum(Token(cpu).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, "!")) @@ -745,8 +743,8 @@ pub fn Tokenizer(format: assembler.Format) type { second; const source_lower = try lowercase_bounded(256, source_str); - const source = std.meta.stringToEnum(Token(format).Instruction.Mov.Source, source_lower.slice()) orelse return error.InvalidSource; - const operation: Token(format).Instruction.Mov.Operation = if (op_prefixed) |op_str| + const source = std.meta.stringToEnum(Token(cpu).Instruction.Mov.Source, source_lower.slice()) orelse return error.InvalidSource; + const operation: Token(cpu).Instruction.Mov.Operation = if (op_prefixed) |op_str| if (std.mem.eql(u8, "!", op_str)) .invert else if (std.mem.eql(u8, "~", op_str)) @@ -758,7 +756,7 @@ pub fn Tokenizer(format: assembler.Format) type { else .none; - return Token(format).Instruction.Payload{ + return Token(cpu).Instruction.Payload{ .mov = .{ .destination = destination, .source = source, @@ -767,7 +765,7 @@ pub fn Tokenizer(format: assembler.Format) type { }; } - fn get_irq(self: *Self, diags: *?Diagnostics) TokenizeError!Token(format).Instruction.Payload { + fn get_irq(self: *Self, diags: *?Diagnostics) TokenizeError!Token(cpu).Instruction.Payload { const first = (try self.get_arg(diags)) orelse return error.MissingArg; var clear = false; @@ -798,7 +796,7 @@ pub fn Tokenizer(format: assembler.Format) type { first, }; - switch (comptime format) { + switch (comptime cpu) { .RP2040 => { const rel: bool = if (try self.peek_arg(diags)) |result| blk: { const rel_lower = try lowercase_bounded(256, result.str); @@ -809,7 +807,7 @@ pub fn Tokenizer(format: assembler.Format) type { break :blk is_rel; } else false; - return Token(format).Instruction.Payload{ + return Token(cpu).Instruction.Payload{ .irq = .{ .clear = clear, .wait = wait, @@ -847,7 +845,7 @@ pub fn Tokenizer(format: assembler.Format) type { } else 0b00; // } else .direct; - return Token(format).Instruction.Payload{ + return Token(cpu).Instruction.Payload{ .irq = .{ .clear = clear, .wait = wait, @@ -859,7 +857,7 @@ pub fn Tokenizer(format: assembler.Format) type { } } - fn get_set(self: *Self, diags: *?Diagnostics) TokenizeError!Token(format).Instruction.Payload { + fn get_set(self: *Self, diags: *?Diagnostics) TokenizeError!Token(cpu).Instruction.Payload { const dest_str = (try self.get_arg(diags)) orelse { diags.* = Diagnostics.init(0, "missing destination", .{}); return error.MissingArg; @@ -868,15 +866,15 @@ pub fn Tokenizer(format: assembler.Format) type { const dest_lower = try lowercase_bounded(256, dest_str); - return Token(format).Instruction.Payload{ + return Token(cpu).Instruction.Payload{ .set = .{ - .destination = std.meta.stringToEnum(Token(format).Instruction.Set.Destination, dest_lower.slice()) orelse return error.InvalidDestination, + .destination = std.meta.stringToEnum(Token(cpu).Instruction.Set.Destination, dest_lower.slice()) orelse return error.InvalidDestination, .value = value, }, }; } - const instructions = std.StaticStringMap(*const fn (*Self, *?Diagnostics) TokenizeError!Token(format).Instruction.Payload).initComptime(.{ + const instructions = std.StaticStringMap(*const fn (*Self, *?Diagnostics) TokenizeError!Token(cpu).Instruction.Payload).initComptime(.{ .{ "nop", get_nop }, .{ "jmp", get_jmp }, .{ "wait", get_wait }, @@ -889,7 +887,7 @@ pub fn Tokenizer(format: assembler.Format) type { .{ "set", get_set }, }); - fn get_instruction(self: *Self, name: Identifier, diags: *?Diagnostics) !Token(format) { + fn get_instruction(self: *Self, name: Identifier, diags: *?Diagnostics) !Token(cpu) { const name_lower = try lowercase_bounded(256, name.str); const payload = if (instructions.get(name_lower.slice())) |handler| try handler(self, diags) @@ -930,7 +928,7 @@ pub fn Tokenizer(format: assembler.Format) type { } self.skip_line(); - return Token(format){ + return Token(cpu){ .index = name.index, .data = .{ .instruction = .{ @@ -942,7 +940,7 @@ pub fn Tokenizer(format: assembler.Format) type { }; } - fn next(self: *Self, diags: *?assembler.Diagnostics) !?Token(format) { + fn next(self: *Self, diags: *?assembler.Diagnostics) !?Token(cpu) { while (self.peek()) |p| { switch (p) { ' ', '\t', '\n', '\r', ',' => self.consume(1), @@ -971,7 +969,7 @@ pub fn Tokenizer(format: assembler.Format) type { // definitely a label return if (eql_lower("public", first.str)) - Token(format){ + Token(cpu){ .index = first.index, .data = .{ .label = .{ @@ -984,7 +982,7 @@ pub fn Tokenizer(format: assembler.Format) type { }, } else if (std.mem.endsWith(u8, first.str, ":")) - Token(format){ + Token(cpu){ .index = first.index, .data = .{ .label = .{ @@ -1004,7 +1002,7 @@ pub fn Tokenizer(format: assembler.Format) type { }; } -pub fn Token(comptime format: assembler.Format) type { +pub fn Token(comptime cpu: CPU) type { return struct { const Self = @This(); index: u32, @@ -1021,7 +1019,7 @@ pub fn Token(comptime format: assembler.Format) type { instruction: Instruction, }, - pub const Tag = std.meta.Tag(std.meta.FieldType(Token(format), .data)); + pub const Tag = std.meta.Tag(std.meta.FieldType(Token(cpu), .data)); pub const Label = struct { name: []const u8, @@ -1071,7 +1069,7 @@ pub fn Token(comptime format: assembler.Format) type { num: Value, rel: bool, - pub const Source = switch (format) { + pub const Source = switch (cpu) { .RP2040 => enum(u2) { gpio = 0b00, pin = 0b01, @@ -1132,7 +1130,7 @@ pub fn Token(comptime format: assembler.Format) type { operation: Operation, source: Source, - pub const Destination = switch (format) { + pub const Destination = switch (cpu) { .RP2040 => enum(u3) { pins = 0b000, x = 0b001, @@ -1171,7 +1169,7 @@ pub fn Token(comptime format: assembler.Format) type { }; }; - pub const Irq = switch (format) { + pub const Irq = switch (cpu) { .RP2040 => struct { clear: bool, wait: bool, @@ -1235,15 +1233,15 @@ const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; const expectEqualStrings = std.testing.expectEqualStrings; -fn DirectiveTag(comptime format: assembler.Format) type { - return @typeInfo(Token(format).Directive).Union.tag_type.?; +fn DirectiveTag(comptime cpu: CPU) type { + return @typeInfo(Token(cpu).Directive).Union.tag_type.?; } -fn PayloadTag(comptime format: assembler.Format) type { - return @typeInfo(Token(format).Instruction.Payload).Union.tag_type.?; +fn PayloadTag(comptime cpu: CPU) type { + return @typeInfo(Token(cpu).Instruction.Payload).Union.tag_type.?; } -fn expect_program(comptime format: assembler.Format, expected: []const u8, actual: Token(format)) !void { - try expectEqual(Token(format).Tag.program, @as(Token(format).Tag, actual.data)); +fn expect_program(comptime cpu: CPU, expected: []const u8, actual: Token(cpu)) !void { + try expectEqual(Token(cpu).Tag.program, @as(Token(cpu).Tag, actual.data)); try expectEqualStrings(expected, actual.data.program); } @@ -1264,21 +1262,21 @@ fn expect_opt_value(expected: ?Value, actual: ?Value) !void { }; } -fn expect_define(comptime format: assembler.Format, expected: Token(format).Define, actual: Token(format)) !void { - try expectEqual(Token(format).Tag.define, @as(Token(format).Tag, actual.data)); +fn expect_define(comptime cpu: CPU, expected: Token(cpu).Define, actual: Token(cpu)) !void { + try expectEqual(Token(cpu).Tag.define, @as(Token(cpu).Tag, actual.data)); const define = actual.data.define; try expectEqualStrings(expected.name, define.name); try expect_value(expected.value, define.value); } -fn expect_origin(comptime format: assembler.Format, expected: Value, actual: Token(format)) !void { - try expectEqual(Token(format).Tag.origin, @as(Token(format).Tag, actual.data)); +fn expect_origin(comptime cpu: CPU, expected: Value, actual: Token(cpu)) !void { + try expectEqual(Token(cpu).Tag.origin, @as(Token(cpu).Tag, actual.data)); try expect_value(expected, actual.data.origin); } -fn expect_side_set(comptime format: assembler.Format, expected: Token(format).SideSet, actual: Token(format)) !void { - try expectEqual(Token(format).Tag.side_set, @as(Token(format).Tag, actual.data)); +fn expect_side_set(comptime cpu: CPU, expected: Token(cpu).SideSet, actual: Token(cpu)) !void { + try expectEqual(Token(cpu).Tag.side_set, @as(Token(cpu).Tag, actual.data)); const side_set = actual.data.side_set; try expect_value(expected.count, side_set.count); @@ -1286,16 +1284,16 @@ fn expect_side_set(comptime format: assembler.Format, expected: Token(format).Si try expectEqual(expected.pindir, side_set.pindir); } -fn expect_wrap_target(comptime format: assembler.Format, actual: Token(format)) !void { - try expectEqual(Token(format).Tag.wrap_target, @as(Token(format).Tag, actual.data)); +fn expect_wrap_target(comptime cpu: CPU, actual: Token(cpu)) !void { + try expectEqual(Token(cpu).Tag.wrap_target, @as(Token(cpu).Tag, actual.data)); } -fn expect_wrap(comptime format: assembler.Format, actual: Token(format)) !void { - try expectEqual(Token(format).Tag.wrap, @as(Token(format).Tag, actual.data)); +fn expect_wrap(comptime cpu: CPU, actual: Token(cpu)) !void { + try expectEqual(Token(cpu).Tag.wrap, @as(Token(cpu).Tag, actual.data)); } -fn expect_lang_opt(comptime format: assembler.Format, expected: Token(format).LangOpt, actual: Token(format)) !void { - try expectEqual(Token(format).Tag.lang_opt, @as(Token(format).Tag, actual.data)); +fn expect_lang_opt(comptime cpu: CPU, expected: Token(cpu).LangOpt, actual: Token(cpu)) !void { + try expectEqual(Token(cpu).Tag.lang_opt, @as(Token(cpu).Tag, actual.data)); const lang_opt = actual.data.lang_opt; try expectEqualStrings(expected.lang, lang_opt.lang); @@ -1303,13 +1301,13 @@ fn expect_lang_opt(comptime format: assembler.Format, expected: Token(format).La try expectEqualStrings(expected.option, lang_opt.option); } -fn expect_word(comptime format: assembler.Format, expected: Value, actual: Token(format)) !void { - try expectEqual(Token(format).Tag.word, @as(Token(format).Tag, actual.data)); +fn expect_word(comptime cpu: CPU, expected: Value, actual: Token(cpu)) !void { + try expectEqual(Token(cpu).Tag.word, @as(Token(cpu).Tag, actual.data)); try expect_value(expected, actual.data.word); } -fn expect_label(comptime format: assembler.Format, expected: Token(format).Label, actual: Token(format)) !void { - try expectEqual(Token(format).Tag.label, @as(Token(format).Tag, actual.data)); +fn expect_label(comptime cpu: CPU, expected: Token(cpu).Label, actual: Token(cpu)) !void { + try expectEqual(Token(cpu).Tag.label, @as(Token(cpu).Tag, actual.data)); const label = actual.data.label; try expectEqual(expected.public, label.public); @@ -1321,27 +1319,27 @@ const ExpectedNopInstr = struct { side_set: ?Value = null, }; -fn expect_instr_nop(comptime format: assembler.Format, expected: ExpectedNopInstr, actual: Token(format)) !void { - try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); - try expectEqual(PayloadTag(format).nop, @as(PayloadTag(format), actual.data.instruction.payload)); +fn expect_instr_nop(comptime cpu: CPU, expected: ExpectedNopInstr, actual: Token(cpu)) !void { + try expectEqual(Token(cpu).Tag.instruction, @as(Token(cpu).Tag, actual.data)); + try expectEqual(PayloadTag(cpu).nop, @as(PayloadTag(cpu), 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); } -fn ExpectedSetInstr(comptime format: assembler.Format) type { +fn ExpectedSetInstr(comptime cpu: CPU) type { return struct { - dest: Token(format).Instruction.Set.Destination, + dest: Token(cpu).Instruction.Set.Destination, value: Value, delay: ?Value = null, side_set: ?Value = null, }; } -fn expect_instr_set(comptime format: assembler.Format, expected: ExpectedSetInstr(format), actual: Token(format)) !void { - try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); - try expectEqual(PayloadTag(format).set, @as(PayloadTag(format), actual.data.instruction.payload)); +fn expect_instr_set(comptime cpu: CPU, expected: ExpectedSetInstr(cpu), actual: Token(cpu)) !void { + try expectEqual(Token(cpu).Tag.instruction, @as(Token(cpu).Tag, actual.data)); + try expectEqual(PayloadTag(cpu).set, @as(PayloadTag(cpu), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1352,18 +1350,18 @@ fn expect_instr_set(comptime format: assembler.Format, expected: ExpectedSetInst try expect_value(expected.value, set.value); } -fn ExpectedJmpInstr(comptime format: assembler.Format) type { +fn ExpectedJmpInstr(comptime cpu: CPU) type { return struct { - cond: Token(format).Instruction.Jmp.Condition = .always, + cond: Token(cpu).Instruction.Jmp.Condition = .always, target: []const u8, delay: ?Value = null, side_set: ?Value = null, }; } -fn expect_instr_jmp(comptime format: assembler.Format, expected: ExpectedJmpInstr(format), actual: Token(format)) !void { - try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); - try expectEqual(PayloadTag(format).jmp, @as(PayloadTag(format), actual.data.instruction.payload)); +fn expect_instr_jmp(comptime cpu: CPU, expected: ExpectedJmpInstr(cpu), actual: Token(cpu)) !void { + try expectEqual(Token(cpu).Tag.instruction, @as(Token(cpu).Tag, actual.data)); + try expectEqual(PayloadTag(cpu).jmp, @as(PayloadTag(cpu), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1374,10 +1372,10 @@ fn expect_instr_jmp(comptime format: assembler.Format, expected: ExpectedJmpInst try expectEqualStrings(expected.target, jmp.target); } -fn ExpectedWaitInstr(comptime format: assembler.Format) type { +fn ExpectedWaitInstr(comptime cpu: CPU) type { return struct { polarity: u1, - source: Token(format).Instruction.Wait.Source, + source: Token(cpu).Instruction.Wait.Source, num: Value, // only valid for irq source rel: bool = false, @@ -1386,9 +1384,9 @@ fn ExpectedWaitInstr(comptime format: assembler.Format) type { }; } -fn expect_instr_wait(comptime format: assembler.Format, expected: ExpectedWaitInstr(format), actual: Token(format)) !void { - try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); - try expectEqual(PayloadTag(format).wait, @as(PayloadTag(format), actual.data.instruction.payload)); +fn expect_instr_wait(comptime cpu: CPU, expected: ExpectedWaitInstr(cpu), actual: Token(cpu)) !void { + try expectEqual(Token(cpu).Tag.instruction, @as(Token(cpu).Tag, actual.data)); + try expectEqual(PayloadTag(cpu).wait, @as(PayloadTag(cpu), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1400,18 +1398,18 @@ fn expect_instr_wait(comptime format: assembler.Format, expected: ExpectedWaitIn try expect_value(expected.num, wait.num); } -fn ExpectedInInstr(comptime format: assembler.Format) type { +fn ExpectedInInstr(comptime cpu: CPU) type { return struct { - source: Token(format).Instruction.In.Source, + source: Token(cpu).Instruction.In.Source, bit_count: u5, delay: ?Value = null, side_set: ?Value = null, }; } -fn expect_instr_in(comptime format: assembler.Format, expected: ExpectedInInstr(format), actual: Token(format)) !void { - try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); - try expectEqual(PayloadTag(format).in, @as(PayloadTag(format), actual.data.instruction.payload)); +fn expect_instr_in(comptime cpu: CPU, expected: ExpectedInInstr(cpu), actual: Token(cpu)) !void { + try expectEqual(Token(cpu).Tag.instruction, @as(Token(cpu).Tag, actual.data)); + try expectEqual(PayloadTag(cpu).in, @as(PayloadTag(cpu), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1422,18 +1420,18 @@ fn expect_instr_in(comptime format: assembler.Format, expected: ExpectedInInstr( try expectEqual(expected.bit_count, in.bit_count); } -fn ExpectedOutInstr(comptime format: assembler.Format) type { +fn ExpectedOutInstr(comptime cpu: CPU) type { return struct { - destination: Token(format).Instruction.Out.Destination, + destination: Token(cpu).Instruction.Out.Destination, bit_count: u5, delay: ?Value = null, side_set: ?Value = null, }; } -fn expect_instr_out(comptime format: assembler.Format, expected: ExpectedOutInstr(format), actual: Token(format)) !void { - try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); - try expectEqual(PayloadTag(format).out, @as(PayloadTag(format), actual.data.instruction.payload)); +fn expect_instr_out(comptime cpu: CPU, expected: ExpectedOutInstr(cpu), actual: Token(cpu)) !void { + try expectEqual(Token(cpu).Tag.instruction, @as(Token(cpu).Tag, actual.data)); + try expectEqual(PayloadTag(cpu).out, @as(PayloadTag(cpu), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1451,9 +1449,9 @@ const ExpectedPushInstr = struct { side_set: ?Value = null, }; -fn expect_instr_push(comptime format: assembler.Format, expected: ExpectedPushInstr, actual: Token(format)) !void { - try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); - try expectEqual(PayloadTag(format).push, @as(PayloadTag(format), actual.data.instruction.payload)); +fn expect_instr_push(comptime cpu: CPU, expected: ExpectedPushInstr, actual: Token(cpu)) !void { + try expectEqual(Token(cpu).Tag.instruction, @as(Token(cpu).Tag, actual.data)); + try expectEqual(PayloadTag(cpu).push, @as(PayloadTag(cpu), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1471,9 +1469,9 @@ const ExpectedPullInstr = struct { side_set: ?Value = null, }; -fn expect_instr_pull(comptime format: assembler.Format, expected: ExpectedPullInstr, actual: Token(format)) !void { - try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); - try expectEqual(PayloadTag(format).pull, @as(PayloadTag(format), actual.data.instruction.payload)); +fn expect_instr_pull(comptime cpu: CPU, expected: ExpectedPullInstr, actual: Token(cpu)) !void { + try expectEqual(Token(cpu).Tag.instruction, @as(Token(cpu).Tag, actual.data)); + try expectEqual(PayloadTag(cpu).pull, @as(PayloadTag(cpu), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1484,19 +1482,19 @@ fn expect_instr_pull(comptime format: assembler.Format, expected: ExpectedPullIn try expectEqual(expected.ifempty, pull.ifempty); } -fn ExpectedMovInstr(comptime format: assembler.Format) type { +fn ExpectedMovInstr(comptime cpu: CPU) type { return struct { - source: Token(format).Instruction.Mov.Source, - destination: Token(format).Instruction.Mov.Destination, - operation: Token(format).Instruction.Mov.Operation = .none, + source: Token(cpu).Instruction.Mov.Source, + destination: Token(cpu).Instruction.Mov.Destination, + operation: Token(cpu).Instruction.Mov.Operation = .none, delay: ?Value = null, side_set: ?Value = null, }; } -fn expect_instr_mov(comptime format: assembler.Format, expected: ExpectedMovInstr(format), actual: Token(format)) !void { - try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); - try expectEqual(PayloadTag(format).mov, @as(PayloadTag(format), actual.data.instruction.payload)); +fn expect_instr_mov(comptime cpu: CPU, expected: ExpectedMovInstr(cpu), actual: Token(cpu)) !void { + try expectEqual(Token(cpu).Tag.instruction, @as(Token(cpu).Tag, actual.data)); + try expectEqual(PayloadTag(cpu).mov, @as(PayloadTag(cpu), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1508,8 +1506,8 @@ fn expect_instr_mov(comptime format: assembler.Format, expected: ExpectedMovInst try expectEqual(expected.destination, mov.destination); } -fn ExpectedIrqInstr(comptime format: assembler.Format) type { - return switch (format) { +fn ExpectedIrqInstr(comptime cpu: CPU) type { + return switch (cpu) { .RP2040 => struct { clear: bool, wait: bool, @@ -1529,9 +1527,9 @@ fn ExpectedIrqInstr(comptime format: assembler.Format) type { }; } -fn expect_instr_irq(comptime format: assembler.Format, expected: ExpectedIrqInstr(format), actual: Token(format)) !void { - try expectEqual(Token(format).Tag.instruction, @as(Token(format).Tag, actual.data)); - try expectEqual(PayloadTag(format).irq, @as(PayloadTag(format), actual.data.instruction.payload)); +fn expect_instr_irq(comptime cpu: CPU, expected: ExpectedIrqInstr(cpu), actual: Token(cpu)) !void { + try expectEqual(Token(cpu).Tag.instruction, @as(Token(cpu).Tag, actual.data)); + try expectEqual(PayloadTag(cpu).irq, @as(PayloadTag(cpu), actual.data.instruction.payload)); const instr = actual.data.instruction; try expect_opt_value(expected.delay, instr.delay); @@ -1540,7 +1538,7 @@ fn expect_instr_irq(comptime format: assembler.Format, expected: ExpectedIrqInst const irq = instr.payload.irq; try expectEqual(expected.clear, irq.clear); try expectEqual(expected.wait, irq.wait); - switch (format) { + switch (cpu) { .RP2040 => { try expectEqual(expected.rel, irq.rel); }, @@ -1550,9 +1548,9 @@ fn expect_instr_irq(comptime format: assembler.Format, expected: ExpectedIrqInst } } -fn bounded_tokenize(comptime format: assembler.Format, source: []const u8) !std.BoundedArray(Token(format), 256) { +fn bounded_tokenize(comptime cpu: CPU, source: []const u8) !std.BoundedArray(Token(cpu), 256) { var diags: ?assembler.Diagnostics = null; - return tokenize(format, source, &diags, .{}) catch |err| if (diags) |d| blk: { + return tokenize(cpu, 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; @@ -1590,7 +1588,7 @@ test "tokenize.block comment" { } test "tokenize.code block" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const tokens = try bounded_tokenize(format, \\% c-sdk { \\ int foo; @@ -1602,14 +1600,14 @@ test "tokenize.code block" { } test "tokenize.directive.program" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const tokens = try bounded_tokenize(format, ".program arst"); try expect_program(format, "arst", tokens.get(0)); } } test "tokenize.directive.define" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const tokens = try bounded_tokenize(format, ".define symbol_name 1"); try expect_define(format, .{ .name = "symbol_name", @@ -1620,7 +1618,7 @@ test "tokenize.directive.define" { } test "tokenize.directive.define.public" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const tokens = try bounded_tokenize(format, ".define public symbol_name 0x1"); try expect_define(format, .{ .name = "symbol_name", @@ -1631,7 +1629,7 @@ test "tokenize.directive.define.public" { } test "tokenize.directive.define.with expression" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const tokens = try bounded_tokenize(format, \\.define symbol_name 0x1 \\.define something (symbol_name * 2) @@ -1652,98 +1650,98 @@ test "tokenize.directive.define.with expression" { } test "tokenize.directive.origin" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const tokens = try bounded_tokenize(format, ".origin 0x10"); try expect_origin(format, .{ .integer = 0x10 }, tokens.get(0)); } } test "tokenize.directive.side_set" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const tokens = try bounded_tokenize(format, ".side_set 1"); try expect_side_set(format, .{ .count = .{ .integer = 1 } }, tokens.get(0)); } } test "tokenize.directive.side_set.opt" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const tokens = try bounded_tokenize(format, ".side_set 1 opt"); try expect_side_set(format, .{ .count = .{ .integer = 1 }, .opt = true }, tokens.get(0)); } } test "tokenize.directive.side_set.pindirs" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const tokens = try bounded_tokenize(format, ".side_set 1 pindirs"); try expect_side_set(format, .{ .count = .{ .integer = 1 }, .pindir = true }, tokens.get(0)); } } test "tokenize.directive.wrap_target" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const tokens = try bounded_tokenize(format, ".wrap_target"); try expect_wrap_target(format, tokens.get(0)); } } test "tokenize.directive.wrap" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const tokens = try bounded_tokenize(format, ".wrap"); try expect_wrap(format, tokens.get(0)); } } test "tokenize.directive.lang_opt" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const tokens = try bounded_tokenize(format, ".lang_opt c flag foo"); try expect_lang_opt(format, .{ .lang = "c", .name = "flag", .option = "foo" }, tokens.get(0)); } } test "tokenize.directive.word" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const tokens = try bounded_tokenize(format, ".word 0xaaaa"); try expect_word(format, .{ .integer = 0xaaaa }, tokens.get(0)); } } test "tokenize.label" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const tokens = try bounded_tokenize(format, "my_label:"); try expect_label(format, .{ .name = "my_label" }, tokens.get(0)); } } test "tokenize.label.public" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const tokens = try bounded_tokenize(format, "public my_label:"); try expect_label(format, .{ .name = "my_label", .public = true }, tokens.get(0)); } } test "tokenize.instr.nop" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const tokens = try bounded_tokenize(format, "nop"); try expect_instr_nop(format, .{}, tokens.get(0)); } } test "tokenize.instr.jmp.label" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const tokens = try bounded_tokenize(format, "jmp my_label"); try expect_instr_jmp(format, .{ .target = "my_label" }, tokens.get(0)); } } test "tokenize.instr.jmp.value" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const tokens = try bounded_tokenize(format, "jmp 0x2"); try expect_instr_jmp(format, .{ .target = "0x2" }, tokens.get(0)); } } test "tokenize.instr.jmp.conditions" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const Condition = Token(format).Instruction.Jmp.Condition; const cases = std.StaticStringMap(Condition).initComptime(.{ .{ "!x", .x_is_zero }, @@ -1764,7 +1762,7 @@ test "tokenize.instr.jmp.conditions" { } test "tokenize.instr.wait" { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { inline for (.{ "gpio", "pin", "irq" }) |source| { const tokens = try bounded_tokenize(format, comptime std.fmt.comptimePrint("wait 0 {s} 1", .{source})); try expect_instr_wait(format, .{ @@ -1957,7 +1955,7 @@ test "tokenize.instr.irq" { }); inline for (comptime modes.keys(), comptime modes.values(), 0..) |key, value, num| { - inline for (comptime .{ assembler.Format.RP2040, assembler.Format.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { const tokens = try bounded_tokenize(format, comptime std.fmt.comptimePrint("irq {s} {}", .{ key, num, diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/common.zig b/port/raspberrypi/rp2xxx/src/hal/pio/common.zig index 4b4d1851..86113356 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/common.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/common.zig @@ -2,31 +2,41 @@ const std = @import("std"); const assert = std.debug.assert; const microzig = @import("microzig"); -const cpu = @import("../compatibility.zig").cpu; +const CPU = @import("../cpu.zig").CPU; pub const PIO = microzig.chip.types.peripherals.PIO0; pub const PIO0 = microzig.chip.peripherals.PIO0; pub const PIO1 = microzig.chip.peripherals.PIO1; pub const assembler = @import("assembler.zig"); -const compatibility = @import("../compatibility.zig"); const encoder = @import("assembler/encoder.zig"); const gpio = @import("../gpio.zig"); const hw = @import("../hw.zig"); -// TODO: This is CPU specific -pub const Instruction = encoder.Instruction(assembler.cpuToFormat(compatibility.cpu)); +const Instruction = encoder.Instruction; pub const Program = assembler.Program; // global state for keeping track of used things -var used_instruction_space = switch (cpu) { - .RP2040 => [_]u32{ 0, 0 }, - .RP2350 => [_]u32{ 0, 0, 0 }, -}; -var claimed_state_machines = switch (cpu) { - .RP2040 => [_]u4{ 0, 0 }, - .RP2350 => [_]u4{ 0, 0, 0 }, -}; +fn UsedInstructionSpace(cpu: CPU) type { + switch (cpu) { + .RP2040 => return struct { + pub var val = [_]u32{ 0, 0 }; + }, + .RP2350 => return struct { + pub var val = [_]u32{ 0, 0, 0 }; + }, + } +} +fn ClaimedStateMachines(cpu: CPU) type { + switch (cpu) { + .RP2040 => return struct { + pub var val = [_]u4{ 0, 0 }; + }, + .RP2350 => return struct { + pub var val = [_]u4{ 0, 0, 0 }; + }, + } +} pub const Fifo = enum { tx, @@ -111,7 +121,7 @@ pub const PinMappingOptions = struct { in_base: u5 = 0, }; -pub fn PioImpl(EnumType: type) type { +pub fn PioImpl(EnumType: type, cpu: CPU) type { return struct { pub fn get_instruction_memory(self: EnumType) *volatile [32]u32 { const regs = self.get_regs(); @@ -123,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(cpu).val[@intFromEnum(self)]; const program_mask = program.get_mask(); // We can add the program if the masks don't overlap, if there is @@ -153,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(cpu).val[@intFromEnum(self)] |= program_mask << offset; } /// Public functions will need to lock independently, so only exposing this function for now @@ -170,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(cpu).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(cpu).val[@intFromEnum(self)] |= sm_mask; break @as(StateMachine, @enumFromInt(i)); } } else error.NoSpace; @@ -471,7 +481,7 @@ pub fn PioImpl(EnumType: type) type { self: EnumType, sm: StateMachine, initial_pc: u5, - options: StateMachineInitOptions, + options: StateMachineInitOptions(cpu), ) void { // Halt the machine, set some sensible defaults self.sm_set_enabled(sm, false); @@ -486,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(cpu){ .tag = .jmp, .delay_side_set = 0, @@ -499,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(cpu)) void { const sm_regs = self.get_sm_regs(sm); sm_regs.instr.raw = @as(u16, @bitCast(instruction)); } @@ -508,7 +518,7 @@ pub fn PioImpl(EnumType: type) type { self: EnumType, sm: StateMachine, program: Program, - options: LoadAndStartProgramOptions, + options: LoadAndStartProgramOptions(cpu), ) !void { const expected_side_set_pins = if (program.side_set) |side_set| if (side_set.optional) @@ -554,57 +564,63 @@ pub fn PioImpl(EnumType: type) type { }; } -pub const ShiftOptions = switch (cpu) { - .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(cpu: CPU) type { + return switch (cpu) { + .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(cpu: CPU) type { + return struct { + clkdiv: ClkDivOptions = .{}, + pin_mappings: PinMappingOptions = .{}, + exec: ExecOptions = .{}, + shift: ShiftOptions(cpu) = .{}, + }; +} -pub const LoadAndStartProgramOptions = struct { - clkdiv: ClkDivOptions, - shift: ShiftOptions = .{}, - pin_mappings: PinMappingOptions = .{}, - exec: ExecOptions = .{}, -}; +pub fn LoadAndStartProgramOptions(cpu: CPU) type { + return struct { + clkdiv: ClkDivOptions, + shift: ShiftOptions(cpu) = .{}, + 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..62c87f28 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,15 +30,15 @@ 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 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) void { const sm_regs = self.get_sm_regs(sm); @@ -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..49e9f8b9 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,15 +36,15 @@ 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 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) void { const sm_regs = self.get_sm_regs(sm); @@ -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; }; From 35f945abf4e1f79337580ce94c91c44b92792075 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Thu, 19 Dec 2024 15:54:23 -0500 Subject: [PATCH 03/30] Get pio comparison tests running on both cpu types --- .../src/hal/pio/assembler/comparison_tests.zig | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) 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 9325a34a..9fa15b2d 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 CPU = @import("../../cpu.zig").CPU; + const c = @cImport({ @cDefine("PICO_NO_HARDWARE", "1"); @cInclude("stdint.h"); @@ -32,8 +34,14 @@ const c = @cImport({ }); fn pio_comparison(comptime source: []const u8) !void { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + try pio_comparison_cpu(cpu, source); + } +} + +fn pio_comparison_cpu(comptime cpu: CPU, comptime source: []const u8) !void { comptime var diags: ?assembler.Diagnostics = null; - const output = try comptime assembler.assemble_impl(.RP2040, source, &diags, .{}); + const output = try comptime assembler.assemble_impl(cpu, source, &diags, .{}); try std.testing.expect(output.programs.len > 0); inline for (output.programs) |program| { @@ -91,7 +99,7 @@ test "pio.comparison.i2c" { test "pio.comparison.irq" { @setEvalBranchQuota(22000); - try pio_comparison(@embedFile("comparison_tests/irq.pio")); + try pio_comparison_cpu(.RP2350, @embedFile("comparison_tests/irq.pio")); } test "pio.comparison.manchester_encoding" { From fd8fbf2d6ceb230435e4b2d1fd6efffdea6e18fa Mon Sep 17 00:00:00 2001 From: Grazfather Date: Thu, 19 Dec 2024 15:54:37 -0500 Subject: [PATCH 04/30] Fix PIO sm_set_shift_options --- port/raspberrypi/rp2xxx/src/hal/pio/rp2040.zig | 2 +- port/raspberrypi/rp2xxx/src/hal/pio/rp2350.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/rp2040.zig b/port/raspberrypi/rp2xxx/src/hal/pio/rp2040.zig index 62c87f28..61187890 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/rp2040.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/rp2040.zig @@ -40,7 +40,7 @@ pub const Pio = enum(u1) { 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) void { + 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), diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/rp2350.zig b/port/raspberrypi/rp2xxx/src/hal/pio/rp2350.zig index 49e9f8b9..21ff4957 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/rp2350.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/rp2350.zig @@ -46,7 +46,7 @@ pub const Pio = enum(u2) { 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) void { + 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), From 6926292c0c4384d29fba31477d454198e7ff5894 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Thu, 19 Dec 2024 16:09:18 -0500 Subject: [PATCH 05/30] s/format/cpu --- .../src/hal/pio/assembler/tokenizer.zig | 130 +++++++++--------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index 44d95e12..ab80435a 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -1588,8 +1588,8 @@ test "tokenize.block comment" { } test "tokenize.code block" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const tokens = try bounded_tokenize(format, + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, \\% c-sdk { \\ int foo; \\%} @@ -1600,16 +1600,16 @@ test "tokenize.code block" { } test "tokenize.directive.program" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const tokens = try bounded_tokenize(format, ".program arst"); - try expect_program(format, "arst", tokens.get(0)); + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, ".program arst"); + try expect_program(cpu, "arst", tokens.get(0)); } } test "tokenize.directive.define" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const tokens = try bounded_tokenize(format, ".define symbol_name 1"); - try expect_define(format, .{ + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, ".define symbol_name 1"); + try expect_define(cpu, .{ .name = "symbol_name", .value = .{ .expression = "1" }, .index = 8, @@ -1618,9 +1618,9 @@ test "tokenize.directive.define" { } test "tokenize.directive.define.public" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const tokens = try bounded_tokenize(format, ".define public symbol_name 0x1"); - try expect_define(format, .{ + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, ".define public symbol_name 0x1"); + try expect_define(cpu, .{ .name = "symbol_name", .value = .{ .expression = "0x1" }, .index = 15, @@ -1629,19 +1629,19 @@ test "tokenize.directive.define.public" { } test "tokenize.directive.define.with expression" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const tokens = try bounded_tokenize(format, + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, \\.define symbol_name 0x1 \\.define something (symbol_name * 2) ); - try expect_define(format, .{ + try expect_define(cpu, .{ .name = "symbol_name", .value = .{ .expression = "0x1" }, .index = 8, }, tokens.get(0)); - try expect_define(format, .{ + try expect_define(cpu, .{ .name = "something", .value = .{ .expression = "(symbol_name * 2)" }, .index = 32, @@ -1650,99 +1650,99 @@ test "tokenize.directive.define.with expression" { } test "tokenize.directive.origin" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const tokens = try bounded_tokenize(format, ".origin 0x10"); - try expect_origin(format, .{ .integer = 0x10 }, tokens.get(0)); + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, ".origin 0x10"); + try expect_origin(cpu, .{ .integer = 0x10 }, tokens.get(0)); } } test "tokenize.directive.side_set" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const tokens = try bounded_tokenize(format, ".side_set 1"); - try expect_side_set(format, .{ .count = .{ .integer = 1 } }, tokens.get(0)); + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, ".side_set 1"); + try expect_side_set(cpu, .{ .count = .{ .integer = 1 } }, tokens.get(0)); } } test "tokenize.directive.side_set.opt" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const tokens = try bounded_tokenize(format, ".side_set 1 opt"); - try expect_side_set(format, .{ .count = .{ .integer = 1 }, .opt = true }, tokens.get(0)); + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, ".side_set 1 opt"); + try expect_side_set(cpu, .{ .count = .{ .integer = 1 }, .opt = true }, tokens.get(0)); } } test "tokenize.directive.side_set.pindirs" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const tokens = try bounded_tokenize(format, ".side_set 1 pindirs"); - try expect_side_set(format, .{ .count = .{ .integer = 1 }, .pindir = true }, tokens.get(0)); + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, ".side_set 1 pindirs"); + try expect_side_set(cpu, .{ .count = .{ .integer = 1 }, .pindir = true }, tokens.get(0)); } } test "tokenize.directive.wrap_target" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const tokens = try bounded_tokenize(format, ".wrap_target"); - try expect_wrap_target(format, tokens.get(0)); + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, ".wrap_target"); + try expect_wrap_target(cpu, tokens.get(0)); } } test "tokenize.directive.wrap" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const tokens = try bounded_tokenize(format, ".wrap"); - try expect_wrap(format, tokens.get(0)); + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, ".wrap"); + try expect_wrap(cpu, tokens.get(0)); } } test "tokenize.directive.lang_opt" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const tokens = try bounded_tokenize(format, ".lang_opt c flag foo"); - try expect_lang_opt(format, .{ .lang = "c", .name = "flag", .option = "foo" }, tokens.get(0)); + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, ".lang_opt c flag foo"); + try expect_lang_opt(cpu, .{ .lang = "c", .name = "flag", .option = "foo" }, tokens.get(0)); } } test "tokenize.directive.word" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const tokens = try bounded_tokenize(format, ".word 0xaaaa"); - try expect_word(format, .{ .integer = 0xaaaa }, tokens.get(0)); + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, ".word 0xaaaa"); + try expect_word(cpu, .{ .integer = 0xaaaa }, tokens.get(0)); } } test "tokenize.label" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const tokens = try bounded_tokenize(format, "my_label:"); - try expect_label(format, .{ .name = "my_label" }, tokens.get(0)); + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, "my_label:"); + try expect_label(cpu, .{ .name = "my_label" }, tokens.get(0)); } } test "tokenize.label.public" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const tokens = try bounded_tokenize(format, "public my_label:"); - try expect_label(format, .{ .name = "my_label", .public = true }, tokens.get(0)); + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, "public my_label:"); + try expect_label(cpu, .{ .name = "my_label", .public = true }, tokens.get(0)); } } test "tokenize.instr.nop" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const tokens = try bounded_tokenize(format, "nop"); - try expect_instr_nop(format, .{}, tokens.get(0)); + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, "nop"); + try expect_instr_nop(cpu, .{}, tokens.get(0)); } } test "tokenize.instr.jmp.label" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const tokens = try bounded_tokenize(format, "jmp my_label"); - try expect_instr_jmp(format, .{ .target = "my_label" }, tokens.get(0)); + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, "jmp my_label"); + try expect_instr_jmp(cpu, .{ .target = "my_label" }, tokens.get(0)); } } test "tokenize.instr.jmp.value" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const tokens = try bounded_tokenize(format, "jmp 0x2"); - try expect_instr_jmp(format, .{ .target = "0x2" }, tokens.get(0)); + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, "jmp 0x2"); + try expect_instr_jmp(cpu, .{ .target = "0x2" }, tokens.get(0)); } } test "tokenize.instr.jmp.conditions" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const Condition = Token(format).Instruction.Jmp.Condition; + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const Condition = Token(cpu).Instruction.Jmp.Condition; const cases = std.StaticStringMap(Condition).initComptime(.{ .{ "!x", .x_is_zero }, .{ "x--", .x_dec }, @@ -1754,20 +1754,20 @@ test "tokenize.instr.jmp.conditions" { }); inline for (comptime cases.keys(), comptime cases.values()) |op, cond| { - const tokens = try bounded_tokenize(format, comptime std.fmt.comptimePrint("jmp {s} my_label", .{op})); + const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("jmp {s} my_label", .{op})); - try expect_instr_jmp(format, .{ .cond = cond, .target = "my_label" }, tokens.get(0)); + try expect_instr_jmp(cpu, .{ .cond = cond, .target = "my_label" }, tokens.get(0)); } } } test "tokenize.instr.wait" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { inline for (.{ "gpio", "pin", "irq" }) |source| { - const tokens = try bounded_tokenize(format, comptime std.fmt.comptimePrint("wait 0 {s} 1", .{source})); - try expect_instr_wait(format, .{ + const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("wait 0 {s} 1", .{source})); + try expect_instr_wait(cpu, .{ .polarity = 0, - .source = @field(Token(format).Instruction.Wait.Source, source), + .source = @field(Token(cpu).Instruction.Wait.Source, source), .num = .{ .integer = 1 }, }, tokens.get(0)); } @@ -1955,13 +1955,13 @@ test "tokenize.instr.irq" { }); inline for (comptime modes.keys(), comptime modes.values(), 0..) |key, value, num| { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |format| { - const tokens = try bounded_tokenize(format, comptime std.fmt.comptimePrint("irq {s} {}", .{ + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("irq {s} {}", .{ key, num, })); - try expect_instr_irq(format, .{ + try expect_instr_irq(cpu, .{ .clear = value.clear, .wait = value.wait, .num = num, From dc2b0126397f0dcfdd3cf9bfa109e2c6b66751fc Mon Sep 17 00:00:00 2001 From: Grazfather Date: Thu, 19 Dec 2024 16:11:48 -0500 Subject: [PATCH 06/30] better error in tokenizer tests --- port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index ab80435a..329f54da 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -1551,7 +1551,7 @@ fn expect_instr_irq(comptime cpu: CPU, expected: ExpectedIrqInstr(cpu), actual: fn bounded_tokenize(comptime cpu: CPU, source: []const u8) !std.BoundedArray(Token(cpu), 256) { var diags: ?assembler.Diagnostics = null; return tokenize(cpu, source, &diags, .{}) catch |err| if (diags) |d| blk: { - std.log.err("error at index {}: {s}", .{ d.index, d.message.slice() }); + std.log.err("error with cpu {s} at index {}: {s}", .{ @tagName(cpu), d.index, d.message.slice() }); break :blk err; } else err; } From 15474cd3297af77c0a973e494def0410c7b39397 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Thu, 19 Dec 2024 16:16:08 -0500 Subject: [PATCH 07/30] Test expected index in define expect_define --- port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index 329f54da..dff2d652 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -1268,6 +1268,7 @@ fn expect_define(comptime cpu: CPU, expected: Token(cpu).Define, actual: Token(c 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(comptime cpu: CPU, expected: Value, actual: Token(cpu)) !void { From 0fc40b42a6284aa9bd6c3aa5bc1a5a847311d10f Mon Sep 17 00:00:00 2001 From: Grazfather Date: Thu, 19 Dec 2024 16:20:56 -0500 Subject: [PATCH 08/30] More tests for both cpus --- .../src/hal/pio/assembler/tokenizer.zig | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index dff2d652..27fc04eb 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -1558,34 +1558,44 @@ fn bounded_tokenize(comptime cpu: CPU, source: []const u8) !std.BoundedArray(Tok } test "tokenize.empty string" { - const tokens = try bounded_tokenize(.RP2040, ""); - try expectEqual(@as(usize, 0), tokens.len); + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, ""); + try expectEqual(@as(usize, 0), tokens.len); + } } test "tokenize.whitespace" { - const tokens = try bounded_tokenize(.RP2040, " \t\r\n"); - try expectEqual(@as(usize, 0), tokens.len); + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, " \t\r\n"); + try expectEqual(@as(usize, 0), tokens.len); + } } test "tokenize.comma line comment" { - const tokens = try bounded_tokenize(.RP2040, "; this is a line comment"); + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, "; 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(.RP2040, "// this is a line comment"); + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, "// 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(.RP2040, - \\/* this is - \\ a block comment */ - ); + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, + \\/* this is + \\ a block comment */ + ); - try expectEqual(@as(usize, 0), tokens.len); + try expectEqual(@as(usize, 0), tokens.len); + } } test "tokenize.code block" { From 0a45bd6c658b2208b5ef1e1b3ba03b9c46c89130 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Thu, 19 Dec 2024 16:33:03 -0500 Subject: [PATCH 09/30] Add jmppin as valid source for wait --- .../src/hal/pio/assembler/tokenizer.zig | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index 27fc04eb..eef50479 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -564,15 +564,31 @@ pub fn Tokenizer(cpu: CPU) type { buf[i] = std.ascii.toLower(c); const source_lower = buf[0..source_str.len]; - const source: Token(cpu).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; + var source: Token(cpu).Instruction.Wait.Source = undefined; + switch (comptime cpu) { + .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; + }, + } const rel: bool = if (source == .irq) if (try self.peek_arg(diags)) |rel_result| blk: { @@ -1795,6 +1811,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", From 97feb30372c70a4fafba285e4af1bf8faff0a9fc Mon Sep 17 00:00:00 2001 From: Grazfather Date: Thu, 19 Dec 2024 16:41:01 -0500 Subject: [PATCH 10/30] Test mov to pindirs --- .../src/hal/pio/assembler/tokenizer.zig | 107 ++++++++++-------- 1 file changed, 59 insertions(+), 48 deletions(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index eef50479..8f5aa94c 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -1921,59 +1921,70 @@ test "tokenize.instr.pull.ifempty" { } test "tokenize.instr.mov" { - inline for (.{ - "pins", - "x", - "y", - "null", - "status", - "isr", - "osr", - }) |source| { - const tokens = try bounded_tokenize(.RP2040, comptime std.fmt.comptimePrint("mov x {s}", .{source})); - - try expect_instr_mov(.RP2040, .{ - .source = @field(Token(.RP2040).Instruction.Mov.Source, source), - .destination = .x, - }, tokens.get(0)); - } + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + inline for (.{ + "pins", + "x", + "y", + "null", + "status", + "isr", + "osr", + }) |source| { + const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("mov x {s}", .{source})); + + try expect_instr_mov(cpu, .{ + .source = @field(Token(cpu).Instruction.Mov.Source, source), + .destination = .x, + }, tokens.get(0)); + } - inline for (.{ - "pins", - "x", - "y", - "exec", - "pc", - "isr", - "osr", - }) |dest| { - const tokens = try bounded_tokenize(.RP2040, comptime std.fmt.comptimePrint("mov {s} x", .{dest})); + inline for (.{ + "pins", + "x", + "y", + "exec", + "pc", + "isr", + "osr", + }) |dest| { + const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("mov {s} x", .{dest})); + + try expect_instr_mov(cpu, .{ + .source = .x, + .destination = @field(Token(cpu).Instruction.Mov.Destination, dest), + }, tokens.get(0)); + } + // RP2350 also supports pindirs as dest + { + const tokens = try bounded_tokenize(.RP2350, "mov pindirs x"); - try expect_instr_mov(.RP2040, .{ - .source = .x, - .destination = @field(Token(.RP2040).Instruction.Mov.Destination, dest), - }, tokens.get(0)); - } + try expect_instr_mov(.RP2350, .{ + .source = .x, + .destination = @field(Token(.RP2350).Instruction.Mov.Destination, "pindirs"), + }, tokens.get(0)); + } - const Operation = Token(.RP2040).Instruction.Mov.Operation; - const operations = std.StaticStringMap(Operation).initComptime(.{ - .{ "!", .invert }, - .{ "~", .invert }, - .{ "::", .bit_reverse }, - }); + const Operation = Token(cpu).Instruction.Mov.Operation; + const operations = std.StaticStringMap(Operation).initComptime(.{ + .{ "!", .invert }, + .{ "~", .invert }, + .{ "::", .bit_reverse }, + }); - inline for (.{ "", " " }) |space| { - inline for (comptime operations.keys(), comptime operations.values()) |str, operation| { - const tokens = try bounded_tokenize(.RP2040, comptime std.fmt.comptimePrint("mov x {s}{s}y", .{ - str, - space, - })); + inline for (.{ "", " " }) |space| { + inline for (comptime operations.keys(), comptime operations.values()) |str, operation| { + const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("mov x {s}{s}y", .{ + str, + space, + })); - try expect_instr_mov(.RP2040, .{ - .destination = .x, - .operation = operation, - .source = .y, - }, tokens.get(0)); + try expect_instr_mov(cpu, .{ + .destination = .x, + .operation = operation, + .source = .y, + }, tokens.get(0)); + } } } } From 0742044d0b9387b85a166879421f0a711ad8d59d Mon Sep 17 00:00:00 2001 From: Grazfather Date: Thu, 19 Dec 2024 17:01:19 -0500 Subject: [PATCH 11/30] cleanup irq comp test --- .../hal/pio/assembler/comparison_tests/irq.pio | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) 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 index a3b87b63..a1192a3c 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/irq.pio +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/irq.pio @@ -2,13 +2,13 @@ .side_set 1 .wrap_target - irq set 1 prev side 0 ; DELETEME - irq set 1 rel side 0 ; DELETEME - irq set 1 next side 0 ; DELETEME - irq wait 1 prev side 0 ; DELETEME - irq wait 1 rel side 0 ; DELETEME - irq wait 1 next side 0 ; DELETEME - irq clear 1 prev side 0 ; DELETEME - irq clear 1 rel side 0 ; DELETEME - irq clear 1 next side 0 ; DELETEME + 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 From 7e8f48708cd92c0491a424d27bda7bf7e4780449 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Fri, 20 Dec 2024 15:23:36 -0500 Subject: [PATCH 12/30] wip movrx --- .../rp2xxx/src/hal/pio/assembler/encoder.zig | 36 ++++++++- .../src/hal/pio/assembler/tokenizer.zig | 76 ++++++++++++++++++- 2 files changed, 105 insertions(+), 7 deletions(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig index 8f5ceac8..296277ad 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig @@ -355,7 +355,6 @@ pub fn Encoder(comptime cpu: CPU, comptime options: Options) type { .block = @intFromBool(pull.block), }, }, - // TODO: Make sure this is OK? We just added one more encoding .mov => |mov| .{ .mov = .{ .destination = mov.destination, @@ -363,6 +362,19 @@ pub fn Encoder(comptime cpu: CPU, comptime options: Options) type { .source = mov.source, }, }, + // NOTE: These instructions values only exist for RP2350 + .movtorx => |mov| .{ + .movtorx = .{ + .idxl = mov.idxl, + .idx = mov.idx, + }, + }, + .movfromrx => |mov| .{ + .movfromrx = .{ + .idxl = mov.idxl, + .idx = mov.idx, + }, + }, .irq => |irq| blk: { switch (cpu) { .RP2040 => { @@ -405,8 +417,10 @@ pub fn Encoder(comptime cpu: CPU, comptime options: Options) type { .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, @@ -566,6 +580,8 @@ pub fn Instruction(comptime cpu: CPU) type { push: Push, pull: Pull, mov: Mov, + movtorx: MovToRx, + movfromrx: MovFromRx, irq: Irq, set: Set, }; @@ -575,7 +591,7 @@ pub fn Instruction(comptime cpu: CPU) type { wait, in, out, - push_pull, + push_pull_mov_rx, mov, irq, set, @@ -638,6 +654,18 @@ pub fn Instruction(comptime cpu: CPU) type { }, }; + // RP2350 only, but we need them for the switch case + pub const MovToRx = packed struct(u8) { + _reserved0: u4 = 1, + idxl: bool, + idx: u3, + }; + pub const MovFromRx = packed struct(u8) { + _reserved0: u4 = 0, + idxl: bool, + idx: u3, + }; + pub const Set = packed struct(u8) { data: u5, destination: Token(cpu).Instruction.Set.Destination, diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index 8f5aa94c..454ffec2 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -736,8 +736,16 @@ pub fn Tokenizer(cpu: CPU) type { } fn get_mov(self: *Self, diags: *?Diagnostics) TokenizeError!Token(cpu).Instruction.Payload { - const dest_str = (try self.get_arg(diags)) orelse return error.MissingArg; - const dest_lower = try lowercase_bounded(256, dest_str); + // 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 (cpu == .RP2350 and std.mem.startsWith(u8, dest_lower.slice(), "rxfifo")) { + return try self.get_movrx(diags); + } + + self.consume_peek(dest_str); + const destination = std.meta.stringToEnum(Token(cpu).Instruction.Mov.Destination, dest_lower.slice()) orelse return error.InvalidDestination; const second = try self.get_arg(diags) orelse return error.MissingArg; @@ -781,6 +789,58 @@ pub fn Tokenizer(cpu: CPU) type { }; } + fn get_movrx(self: *Self, diags: *?Diagnostics) TokenizeError!Token(cpu).Instruction.Payload { + const dest_str = (try self.get_arg(diags)) orelse return error.MissingArg; + const dest_lower = try lowercase_bounded(256, dest_str); + const source_str = (try self.get_arg(diags)) orelse return error.MissingArg; + const source_lower = try lowercase_bounded(256, source_str); + if (std.mem.eql(u8, dest_lower.slice(), "rxfifoy")) { + // MOV (to RX) + // Second arg must be isr + if (!std.mem.eql(u8, source_lower.slice(), "isr")) + return error.InvalidSource; + + return Token(cpu).Instruction.Payload{ + .movtorx = .{ + .idxl = true, + .idx = 0, + }, + }; + } else if (std.mem.startsWith(u8, dest_lower.slice(), "rxfifo")) { + // MOV (to RX) + // TODO: Parse out the index + const idx = 0; + return Token(cpu).Instruction.Payload{ + .movtorx = .{ + .idxl = false, + .idx = idx, + }, + }; + } else if (std.mem.eql(u8, dest_lower.slice(), "osr")) { + // MOV (from RX) + var idxl: bool = false; + var idx: u3 = 0; + if (std.mem.eql(u8, source_lower.slice(), "rxfifoy")) { + idxl = true; + idx = 0; + } else if (std.mem.startsWith(u8, source_lower.slice(), "rxfifo")) { + // TODO: Parse out the index + idxl = false; + idx = 0; + } else { + return error.InvalidSource; + } + return Token(cpu).Instruction.Payload{ + .movfromrx = .{ + .idxl = idxl, + .idx = idx, + }, + }; + } else { + return error.InvalidDestination; + } + } + fn get_irq(self: *Self, diags: *?Diagnostics) TokenizeError!Token(cpu).Instruction.Payload { const first = (try self.get_arg(diags)) orelse return error.MissingArg; @@ -1059,6 +1119,8 @@ pub fn Token(comptime cpu: CPU) type { push: Push, pull: Pull, mov: Mov, + movtorx: if (cpu == .RP2350) MovToRx else noreturn, + movfromrx: if (cpu == .RP2350) MovFromRx else noreturn, irq: Irq, set: Set, }; @@ -1140,7 +1202,6 @@ pub fn Token(comptime cpu: CPU) type { ifempty: bool, }; - // TODO: Add mov to RX for rp2350 pub const Mov = struct { destination: Destination, operation: Operation, @@ -1185,6 +1246,15 @@ pub fn Token(comptime cpu: CPU) type { }; }; + pub const MovToRx = struct { + idxl: bool, + idx: u3, + }; + pub const MovFromRx = struct { + idxl: bool, + idx: u3, + }; + pub const Irq = switch (cpu) { .RP2040 => struct { clear: bool, From 7a0450153a6cac42d2469a61bcb5d62b0012d019 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Fri, 20 Dec 2024 15:54:27 -0500 Subject: [PATCH 13/30] mov to rx working --- .../rp2xxx/src/hal/pio/assembler/encoder.zig | 12 ++++++------ .../rp2xxx/src/hal/pio/assembler/tokenizer.zig | 10 +++++++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig index 296277ad..43623339 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig @@ -365,14 +365,14 @@ pub fn Encoder(comptime cpu: CPU, comptime options: Options) type { // NOTE: These instructions values only exist for RP2350 .movtorx => |mov| .{ .movtorx = .{ - .idxl = mov.idxl, .idx = mov.idx, + .idxl = @intFromBool(mov.idxl), }, }, .movfromrx => |mov| .{ .movfromrx = .{ - .idxl = mov.idxl, .idx = mov.idx, + .idxl = @intFromBool(mov.idxl), }, }, .irq => |irq| blk: { @@ -656,14 +656,14 @@ pub fn Instruction(comptime cpu: CPU) type { // RP2350 only, but we need them for the switch case pub const MovToRx = packed struct(u8) { - _reserved0: u4 = 1, - idxl: bool, idx: u3, + idxl: u1, + _reserved0: u4 = 1, }; pub const MovFromRx = packed struct(u8) { - _reserved0: u4 = 0, - idxl: bool, idx: u3, + idxl: u1, + _reserved0: u4 = 0, }; pub const Set = packed struct(u8) { diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index 454ffec2..6bfea942 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -808,12 +808,16 @@ pub fn Tokenizer(cpu: CPU) type { }; } else if (std.mem.startsWith(u8, dest_lower.slice(), "rxfifo")) { // MOV (to RX) - // TODO: Parse out the index - const idx = 0; + // -- Parse out the index + const dest_index = dest_lower.slice()["rxfifo".len - 0 ..]; + const value = try std.fmt.parseInt(u8, dest_index, 11); + if (value > 3) { + return error.InvalidDestination; + } return Token(cpu).Instruction.Payload{ .movtorx = .{ .idxl = false, - .idx = idx, + .idx = @intCast(value), }, }; } else if (std.mem.eql(u8, dest_lower.slice(), "osr")) { From 7ca9cc24a223af46972f592d0f15269b9697003c Mon Sep 17 00:00:00 2001 From: Grazfather Date: Fri, 20 Dec 2024 15:54:49 -0500 Subject: [PATCH 14/30] Add movrx comparison tests --- .../src/hal/pio/assembler/comparison_tests.zig | 6 ++++++ .../hal/pio/assembler/comparison_tests/movrx.pio | 14 ++++++++++++++ .../pio/assembler/comparison_tests/movrx.pio.h | 16 ++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio create mode 100644 port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio.h 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 9fa15b2d..0be9dfbc 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests.zig @@ -16,6 +16,7 @@ const c = @cImport({ @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"); @@ -107,6 +108,11 @@ test "pio.comparison.manchester_encoding" { try pio_comparison(@embedFile("comparison_tests/manchester_encoding.pio")); } +test "pio.comparison.movrx" { + @setEvalBranchQuota(11000); + try pio_comparison_cpu(.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/movrx.pio b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio new file mode 100644 index 00000000..fdd12de4 --- /dev/null +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio @@ -0,0 +1,14 @@ +.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..40b5c562 --- /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 +}; From 4117ce3af93ea421cbdbcee90c88c1feb0b919f4 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Mon, 6 Jan 2025 11:54:39 -0500 Subject: [PATCH 15/30] put ws2812 in both chips --- examples/raspberrypi/rp2xxx/build.zig | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/raspberrypi/rp2xxx/build.zig b/examples/raspberrypi/rp2xxx/build.zig index 18a53707..3ce0c950 100644 --- a/examples/raspberrypi/rp2xxx/build.zig +++ b/examples/raspberrypi/rp2xxx/build.zig @@ -36,12 +36,10 @@ pub fn build(b: *std.Build) void { // .{ .target = "board:waveshare/rp2040_plus_16m", .name = "rp2040-plus-16m" }, }; - const rp2350_only_examples: []const Example = &.{ - // TODO: No RP2350 feature specific examples to show off yet - .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico2_arm, .name = "ws2812", .file = "src/ws2812.zig" }, - }; + const rp2350_only_examples: []const Example = &.{}; const chip_agnostic_examples: []const ChipAgnosticExample = &.{ + .{ .name = "ws2812", .file = "src/ws2812.zig" }, // .{ .name = "squarewave", .file = "src/squarewave.zig" }, // .{ .name = "blinky", .file = "src/blinky.zig" }, // .{ .name = "gpio-clock-output", .file = "src/gpio_clock_output.zig" }, From ac5383618d9841e02ae6adad404dad2f9b418f60 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Mon, 6 Jan 2025 11:55:09 -0500 Subject: [PATCH 16/30] wip: print diag and error --- .../src/hal/pio/assembler/comparison_tests.zig | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) 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 0be9dfbc..95add43c 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests.zig @@ -41,8 +41,20 @@ fn pio_comparison(comptime source: []const u8) !void { } fn pio_comparison_cpu(comptime cpu: CPU, comptime source: []const u8) !void { - comptime var diags: ?assembler.Diagnostics = null; - const output = try comptime assembler.assemble_impl(cpu, source, &diags, .{}); + // comptime var diags: ?assembler.Diagnostics = null; + // const output = comptime blk: { + // const v = assembler.assemble_impl(cpu, source, &diags, .{}) catch |err| { + // @compileLog("err {}", err); + // if (diags != null) { + // std.log.debug("diag {}", .{diags}); + // @compileLog("Diag", diags); + // } + // // I want to return an error here but this is comptime? + // break :blk err; + // }; + // break :blk v; + // }; + const output = comptime assembler.assemble(cpu, source, .{}); try std.testing.expect(output.programs.len > 0); inline for (output.programs) |program| { From 4852d4b11a33066a67c5b99298b2ccea97c94562 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Mon, 6 Jan 2025 12:04:21 -0500 Subject: [PATCH 17/30] Fix error handling in rp2xxx pio assembler --- port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig index 0de9635b..3379d1c2 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig @@ -124,10 +124,11 @@ fn format_compile_error(comptime message: []const u8, comptime source: []const u pub fn assemble(comptime cpu: CPU, comptime source: []const u8, comptime options: AssembleOptions) Output { var diags: ?Diagnostics = null; - return assemble_impl(cpu, source, &diags, options) catch |err| if (diags) |d| - @compileError(format_compile_error(d.message.slice(), source, d.index)) - else - @compileError(err); + return assemble_impl(cpu, source, &diags, options) catch |err| { + if (diags) |d| + @compileError(format_compile_error(d.message.slice(), source, d.index)); + @compileError(@errorName(err)); + }; } test "tokenizer and encoder" { From 3f6d84c23ee2c699cdd31343abf912c97aa96379 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Tue, 7 Jan 2025 08:52:16 -0500 Subject: [PATCH 18/30] Repro issue in diags --- port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig | 2 ++ .../rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio | 3 ++- .../rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio.h | 4 ++-- port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig | 2 ++ 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig index 3379d1c2..da429994 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig @@ -100,6 +100,8 @@ fn format_compile_error(comptime message: []const u8, comptime source: []const u var line_it = std.mem.tokenize(u8, source, "\n\r"); while (line_it.next()) |line| : (line_num += 1) { line_str = line_str ++ "\n" ++ line; + @compileLog(line.len, line_it.index, index); + // Calculate the column if (line_it.index > index) { column = line.len - (line_it.index - index); line_str = line; 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 index fdd12de4..5244bd0e 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio @@ -6,7 +6,8 @@ mov rxfifo1, isr mov rxfifo2, isr mov rxfifo3, isr -; mov osr, rxfifoy + + mov osr, rxfifoy ; mov osr, rxfifo0 ; mov osr, rxfifo1 ; mov osr, rxfifo2 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 index 40b5c562..92ea7e16 100644 --- 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 @@ -7,8 +7,8 @@ static const uint16_t movrx_program_instructions[] = { 0x8011, // mov rxfifo1, isr 0x8012, // mov rxfifo2, isr 0x8013, // mov rxfifo3, isr - // 0b1000_ssss_1001_yiii - // 0x8098, // mov osr, rxfifoy + // 0b1000_ssss_1001_yiii + 0x8098, // mov osr, rxfifoy // 0x8090, // mov osr, rxfifo0 // 0x8091, // mov osr, rxfifo1 // 0x8092, // mov osr, rxfifo2 diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index 6bfea942..d3b2ad4c 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -745,6 +745,7 @@ pub fn Tokenizer(cpu: CPU) type { } self.consume_peek(dest_str); + diags.* = Diagnostics.init(self.index, "hello {}", .{self.index}); const destination = std.meta.stringToEnum(Token(cpu).Instruction.Mov.Destination, dest_lower.slice()) orelse return error.InvalidDestination; @@ -767,6 +768,7 @@ pub fn Tokenizer(cpu: CPU) type { second; const source_lower = try lowercase_bounded(256, source_str); + diags.* = Diagnostics.init(self.index, "hello {}", .{self.index}); const source = std.meta.stringToEnum(Token(cpu).Instruction.Mov.Source, source_lower.slice()) orelse return error.InvalidSource; const operation: Token(cpu).Instruction.Mov.Operation = if (op_prefixed) |op_str| if (std.mem.eql(u8, "!", op_str)) From b06210830e01228994b624e459a14ffa2d0dbf49 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Tue, 7 Jan 2025 09:18:30 -0500 Subject: [PATCH 19/30] Fix diag issue --- port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig index da429994..36f877ec 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig @@ -100,9 +100,9 @@ fn format_compile_error(comptime message: []const u8, comptime source: []const u var line_it = std.mem.tokenize(u8, source, "\n\r"); while (line_it.next()) |line| : (line_num += 1) { line_str = line_str ++ "\n" ++ line; - @compileLog(line.len, line_it.index, index); + // If the line iterator is overlapping the provided index, then we are on the correct line + if (line_it.index >= index) { // Calculate the column - if (line_it.index > index) { column = line.len - (line_it.index - index); line_str = line; break; From 1a416802556510a1f8353932ce678d5682174e0c Mon Sep 17 00:00:00 2001 From: Grazfather Date: Tue, 7 Jan 2025 09:44:39 -0500 Subject: [PATCH 20/30] Cleanup diags --- port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig | 2 -- 1 file changed, 2 deletions(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index d3b2ad4c..6bfea942 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -745,7 +745,6 @@ pub fn Tokenizer(cpu: CPU) type { } self.consume_peek(dest_str); - diags.* = Diagnostics.init(self.index, "hello {}", .{self.index}); const destination = std.meta.stringToEnum(Token(cpu).Instruction.Mov.Destination, dest_lower.slice()) orelse return error.InvalidDestination; @@ -768,7 +767,6 @@ pub fn Tokenizer(cpu: CPU) type { second; const source_lower = try lowercase_bounded(256, source_str); - diags.* = Diagnostics.init(self.index, "hello {}", .{self.index}); const source = std.meta.stringToEnum(Token(cpu).Instruction.Mov.Source, source_lower.slice()) orelse return error.InvalidSource; const operation: Token(cpu).Instruction.Mov.Operation = if (op_prefixed) |op_str| if (std.mem.eql(u8, "!", op_str)) From d12afddf5ec96bf848b3fcebb7dbf709be504c25 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Tue, 7 Jan 2025 09:51:03 -0500 Subject: [PATCH 21/30] improve movtorx parsing and diag --- .../src/hal/pio/assembler/tokenizer.zig | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index 6bfea942..970bf2ee 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -794,32 +794,39 @@ pub fn Tokenizer(cpu: CPU) type { const dest_lower = try lowercase_bounded(256, dest_str); const source_str = (try self.get_arg(diags)) orelse return error.MissingArg; const source_lower = try lowercase_bounded(256, source_str); - if (std.mem.eql(u8, dest_lower.slice(), "rxfifoy")) { + if (std.mem.startsWith(u8, dest_lower.slice(), "rxfifo")) { // MOV (to RX) - // Second arg must be isr - if (!std.mem.eql(u8, source_lower.slice(), "isr")) + // -- Source must be isr + if (!std.mem.eql(u8, source_lower.slice(), "isr")) { + diags.* = Diagnostics.init(self.index, "mov (to rx): source must be isr", .{}); return error.InvalidSource; - - return Token(cpu).Instruction.Payload{ - .movtorx = .{ - .idxl = true, - .idx = 0, - }, - }; - } else if (std.mem.startsWith(u8, dest_lower.slice(), "rxfifo")) { - // MOV (to RX) - // -- Parse out the index + } const dest_index = dest_lower.slice()["rxfifo".len - 0 ..]; - const value = try std.fmt.parseInt(u8, dest_index, 11); - if (value > 3) { - return error.InvalidDestination; + if (dest_index[0] == 'y') { + return Token(cpu).Instruction.Payload{ + .movtorx = .{ + .idxl = true, + .idx = 0, + }, + }; + } else { + // -- Parse out the index + const value = try std.fmt.parseInt(u8, dest_index, 10); + if (value > 3) { + diags.* = Diagnostics.init( + self.index - dest_str.len + 1, + "mov (to rx): destination must be rxfifoy or rxfifo[]", + .{}, + ); + return error.InvalidDestination; + } + return Token(cpu).Instruction.Payload{ + .movtorx = .{ + .idxl = false, + .idx = @intCast(value), + }, + }; } - return Token(cpu).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; From bb248a0707f3c80016f63d1e6a9118a583eeaf67 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Tue, 7 Jan 2025 10:32:20 -0500 Subject: [PATCH 22/30] Get movfromrx encoding and most tokenization working --- .../rp2xxx/src/hal/pio/assembler/encoder.zig | 2 +- .../src/hal/pio/assembler/tokenizer.zig | 61 ++++++++++++++----- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig index 43623339..a029ef0a 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig @@ -663,7 +663,7 @@ pub fn Instruction(comptime cpu: CPU) type { pub const MovFromRx = packed struct(u8) { idx: u3, idxl: u1, - _reserved0: u4 = 0, + _reserved0: u4 = 9, }; pub const Set = packed struct(u8) { diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index 970bf2ee..b124a971 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -790,19 +790,23 @@ pub fn Tokenizer(cpu: CPU) type { } fn get_movrx(self: *Self, diags: *?Diagnostics) TokenizeError!Token(cpu).Instruction.Payload { + // TODO: Assuming 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); + // TODO: Assuming 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(self.index, "mov (to rx): source must be isr", .{}); + diags.* = Diagnostics.init(source_idx, "mov (to rx): source must be isr", .{}); return error.InvalidSource; } - const dest_index = dest_lower.slice()["rxfifo".len - 0 ..]; - if (dest_index[0] == 'y') { + const dest_index_char = dest_lower.slice()["rxfifo".len - 0 ..]; + if (dest_index_char[0] == 'y') { return Token(cpu).Instruction.Payload{ .movtorx = .{ .idxl = true, @@ -811,15 +815,17 @@ pub fn Tokenizer(cpu: CPU) type { }; } else { // -- Parse out the index - const value = try std.fmt.parseInt(u8, dest_index, 10); + diags.* = Diagnostics.init( + dest_idx, + // @intCast(self.index - dest_str.len + 1), + "mov (to rx): destination must be rxfifoy or rxfifo[]", + .{}, + ); + const value = try std.fmt.parseInt(u8, dest_index_char, 10); if (value > 3) { - diags.* = Diagnostics.init( - self.index - dest_str.len + 1, - "mov (to rx): destination must be rxfifoy or rxfifo[]", - .{}, - ); return error.InvalidDestination; } + diags.* = null; return Token(cpu).Instruction.Payload{ .movtorx = .{ .idxl = false, @@ -831,16 +837,34 @@ pub fn Tokenizer(cpu: CPU) type { // MOV (from RX) var idxl: bool = false; var idx: u3 = 0; - if (std.mem.eql(u8, source_lower.slice(), "rxfifoy")) { - idxl = true; - idx = 0; - } else if (std.mem.startsWith(u8, source_lower.slice(), "rxfifo")) { - // TODO: Parse out the index - idxl = false; - idx = 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): x source must be rxfifoy or rxfifo[]", + .{}, + ); + const value = try std.fmt.parseInt(u8, src_index_char, 10); + if (value > 3) { + return error.InvalidSource; + } + } } else { + diags.* = Diagnostics.init( + @intCast(self.index - source_str.len), + "mov (from rx): y source must be rxfifoy or rxfifo[]", + .{}, + ); return error.InvalidSource; } + diags.* = null; return Token(cpu).Instruction.Payload{ .movfromrx = .{ .idxl = idxl, @@ -848,6 +872,11 @@ pub fn Tokenizer(cpu: CPU) type { }, }; } else { + diags.* = Diagnostics.init( + dest_idx, + "unknown destination", + .{}, + ); return error.InvalidDestination; } } From d66e9b8d014021108aad3057d3530a5ce28e3a50 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Tue, 7 Jan 2025 12:46:35 -0500 Subject: [PATCH 23/30] Get tokenizing mov from rx to work --- .../src/hal/pio/assembler/tokenizer.zig | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index b124a971..115b8d7f 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -206,6 +206,11 @@ pub fn Tokenizer(cpu: CPU) type { self.index = result.start + @as(u32, @intCast(result.str.len)); } + fn unconsume(self: *Self, result: PeekResult) void { + assert(self.index > result.start); + self.index = result.start; + } + /// gets next arg without consuming the stream fn peek_arg_impl( self: *Self, @@ -744,7 +749,24 @@ pub fn Tokenizer(cpu: CPU) type { 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 to + // the source and look for rxfifo. To peek twice we have to consume + // the first peek, but if we find it's mov from rx, then we need to + // unconsume the first arg, so that the tokenizer is in the right + // state when we call get_movrx + // + // 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 (cpu == .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(cpu).Instruction.Mov.Destination, dest_lower.slice()) orelse return error.InvalidDestination; @@ -848,7 +870,7 @@ pub fn Tokenizer(cpu: CPU) type { idx = 0; diags.* = Diagnostics.init( @intCast(source_idx + "rxfifo".len), - "mov (from rx): x source must be rxfifoy or rxfifo[]", + "mov (from rx): source must be rxfifoy or rxfifo[]", .{}, ); const value = try std.fmt.parseInt(u8, src_index_char, 10); @@ -859,7 +881,7 @@ pub fn Tokenizer(cpu: CPU) type { } else { diags.* = Diagnostics.init( @intCast(self.index - source_str.len), - "mov (from rx): y source must be rxfifoy or rxfifo[]", + "mov (from rx): source must be rxfifoy or rxfifo[]", .{}, ); return error.InvalidSource; From dc3b8578a308ccad55ae5688796c4a93d18aa146 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Tue, 7 Jan 2025 12:47:51 -0500 Subject: [PATCH 24/30] wip: Allow pio assemble to work at runtime --- .../rp2xxx/src/hal/pio/assembler.zig | 16 +- .../hal/pio/assembler/comparison_tests.zig | 283 ++++++----- .../pio/assembler/comparison_tests/movrx.pio | 7 + .../assembler/comparison_tests/movrx.pio.h | 4 +- .../rp2xxx/src/hal/pio/assembler/encoder.zig | 38 +- .../src/hal/pio/assembler/tokenizer.zig | 463 +++++++++--------- 6 files changed, 416 insertions(+), 395 deletions(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig index 36f877ec..ce27927d 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig @@ -10,7 +10,9 @@ pub const EncodeOptions = encoder.Options; pub const Define = struct { name: []const u8, - value: i64, + // NO idea why this has to change + value: i128, + // value: i64, }; pub const Program = struct { @@ -72,12 +74,16 @@ pub const Diagnostics = struct { } }; -pub fn assemble_impl(comptime cpu: CPU, comptime source: []const u8, diags: *?Diagnostics, options: AssembleOptions) !Output { +pub fn assemble_impl(comptime cpu: CPU, source: []const u8, diags: *?Diagnostics, comptime options: AssembleOptions) !Output { const tokens = try tokenizer.tokenize(cpu, source, diags, options.tokenize); + for (tokens.slice()) |t| // DELETEME + std.debug.print("Got tokens: {any}\n", .{t}); // DELETEME const encoder_output = try encoder.encode(cpu, 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()); + for (encoder_output.programs.slice()) |bounded| { + const prog = try bounded.to_exported_program(); + try programs.append(prog); + } return Output{ .defines = blk: { @@ -102,7 +108,7 @@ fn format_compile_error(comptime message: []const u8, comptime source: []const u line_str = line_str ++ "\n" ++ line; // If the line iterator is overlapping the provided index, then we are on the correct line if (line_it.index >= index) { - // Calculate the column + // Calculate the column column = line.len - (line_it.index - index); line_str = line; break; 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 95add43c..bfc786be 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests.zig @@ -40,25 +40,20 @@ fn pio_comparison(comptime source: []const u8) !void { } } -fn pio_comparison_cpu(comptime cpu: CPU, comptime source: []const u8) !void { - // comptime var diags: ?assembler.Diagnostics = null; - // const output = comptime blk: { - // const v = assembler.assemble_impl(cpu, source, &diags, .{}) catch |err| { - // @compileLog("err {}", err); - // if (diags != null) { - // std.log.debug("diag {}", .{diags}); - // @compileLog("Diag", diags); - // } - // // I want to return an error here but this is comptime? - // break :blk err; - // }; - // break :blk v; - // }; - const output = comptime assembler.assemble(cpu, source, .{}); +fn pio_comparison_cpu(comptime cpu: CPU, source: []const u8) !void { + var diags: ?assembler.Diagnostics = null; + const output = assembler.assemble_impl(cpu, source, &diags, .{}) catch |e| { + std.log.debug("diags: {any}\n", .{diags}); + std.log.debug("could not assemble: {}\n", .{e}); + return e; + }; try std.testing.expect(output.programs.len > 0); - inline for (output.programs) |program| { - const expected_insns = @field(c, program.name ++ "_program_instructions"); + for (output.programs) |program| { + std.debug.print("Program: {any}\n", .{program.instructions}); + // const expected_insns = @field(c, program.name ++ "_program_instructions"); + // Hardcoded for now since program.name is runtime for me + const expected_insns = @field(c, "movrx_program_instructions"); for (program.instructions, expected_insns) |actual, expected| { std.log.debug("expected: 0x{x}", .{expected}); std.log.debug(" actual: 0x{x}", .{actual}); @@ -70,132 +65,134 @@ fn pio_comparison_cpu(comptime cpu: CPU, comptime source: []const u8) !void { } } -test "pio.comparison.addition" { - @setEvalBranchQuota(4000); - try pio_comparison(@embedFile("comparison_tests/addition.pio")); -} - -test "pio.comparison.apa102" { - @setEvalBranchQuota(11000); - try pio_comparison(@embedFile("comparison_tests/apa102.pio")); -} - -test "pio.comparison.blink" { - @setEvalBranchQuota(4000); - try pio_comparison(@embedFile("comparison_tests/blink.pio")); -} - -test "pio.comparison.clocked_input" { - @setEvalBranchQuota(5000); - try pio_comparison(@embedFile("comparison_tests/clocked_input.pio")); -} - -test "pio.comparison.differential_manchester" { - @setEvalBranchQuota(14000); - try pio_comparison(@embedFile("comparison_tests/differential_manchester.pio")); -} - -test "pio.comparison.hello" { - @setEvalBranchQuota(3000); - try pio_comparison(@embedFile("comparison_tests/hello.pio")); -} - -test "pio.comparison.hub75" { - @setEvalBranchQuota(17000); - try pio_comparison(@embedFile("comparison_tests/hub75.pio")); -} - -test "pio.comparison.i2c" { - @setEvalBranchQuota(17000); - try pio_comparison(@embedFile("comparison_tests/i2c.pio")); -} - -test "pio.comparison.irq" { - @setEvalBranchQuota(22000); - try pio_comparison_cpu(.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.addition" { +// @setEvalBranchQuota(4000); +// try pio_comparison(@embedFile("comparison_tests/addition.pio")); +// } +// +// test "pio.comparison.apa102" { +// @setEvalBranchQuota(11000); +// try pio_comparison(@embedFile("comparison_tests/apa102.pio")); +// } +// +// test "pio.comparison.blink" { +// @setEvalBranchQuota(4000); +// try pio_comparison(@embedFile("comparison_tests/blink.pio")); +// } +// +// test "pio.comparison.clocked_input" { +// @setEvalBranchQuota(5000); +// try pio_comparison(@embedFile("comparison_tests/clocked_input.pio")); +// } +// +// test "pio.comparison.differential_manchester" { +// @setEvalBranchQuota(14000); +// try pio_comparison(@embedFile("comparison_tests/differential_manchester.pio")); +// } +// +// test "pio.comparison.hello" { +// @setEvalBranchQuota(3000); +// try pio_comparison(@embedFile("comparison_tests/hello.pio")); +// } +// +// test "pio.comparison.hub75" { +// @setEvalBranchQuota(17000); +// try pio_comparison(@embedFile("comparison_tests/hub75.pio")); +// } +// +// test "pio.comparison.i2c" { +// @setEvalBranchQuota(17000); +// try pio_comparison(@embedFile("comparison_tests/i2c.pio")); +// } + +// test "pio.comparison.irq" { +// @setEvalBranchQuota(22000); +// try pio_comparison_cpu(.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); + // @compileLog("movrx test"); // DELETEME try pio_comparison_cpu(.RP2350, @embedFile("comparison_tests/movrx.pio")); -} - -test "pio.comparison.nec_carrier_burst" { - @setEvalBranchQuota(6000); - try pio_comparison(@embedFile("comparison_tests/nec_carrier_burst.pio")); -} - -test "pio.comparison.nec_carrier_control" { - @setEvalBranchQuota(9000); - try pio_comparison(@embedFile("comparison_tests/nec_carrier_control.pio")); -} - -test "pio.comparison.nec_receive" { - @setEvalBranchQuota(11000); - try pio_comparison(@embedFile("comparison_tests/nec_receive.pio")); -} - -test "pio.comparison.pio_serialiser" { - @setEvalBranchQuota(3000); - try pio_comparison(@embedFile("comparison_tests/pio_serialiser.pio")); -} - -test "pio.comparison.pwm" { - @setEvalBranchQuota(4000); - try pio_comparison(@embedFile("comparison_tests/pwm.pio")); -} - -test "pio.comparison.quadrature_encoder" { - @setEvalBranchQuota(17000); - try pio_comparison(@embedFile("comparison_tests/quadrature_encoder.pio")); -} - -test "pio.comparison.resistor_dac" { - @setEvalBranchQuota(3000); - try pio_comparison(@embedFile("comparison_tests/resistor_dac.pio")); -} - -test "pio.comparison.spi" { - @setEvalBranchQuota(22000); - try pio_comparison(@embedFile("comparison_tests/spi.pio")); -} - -test "pio.comparison.squarewave" { - @setEvalBranchQuota(2000); - try pio_comparison(@embedFile("comparison_tests/squarewave.pio")); -} - -test "pio.comparison.squarewave_fast" { - @setEvalBranchQuota(2000); - try pio_comparison(@embedFile("comparison_tests/squarewave_fast.pio")); -} - -test "pio.comparison.squarewave_wrap" { - @setEvalBranchQuota(3000); - try pio_comparison(@embedFile("comparison_tests/squarewave_wrap.pio")); -} - -test "pio.comparison.st7789_lcd" { - @setEvalBranchQuota(5000); - try pio_comparison(@embedFile("comparison_tests/st7789_lcd.pio")); -} - -test "pio.comparison.uart_rx" { - @setEvalBranchQuota(11000); - try pio_comparison(@embedFile("comparison_tests/uart_rx.pio")); -} - -test "pio.comparison.uart_tx" { - @setEvalBranchQuota(6000); - try pio_comparison(@embedFile("comparison_tests/uart_tx.pio")); -} - -test "pio.comparison.ws2812" { - @setEvalBranchQuota(11000); - try pio_comparison(@embedFile("comparison_tests/ws2812.pio")); -} + // @compileLog("movrx test done"); // DELETEME +} +// +// test "pio.comparison.nec_carrier_burst" { +// @setEvalBranchQuota(6000); +// try pio_comparison(@embedFile("comparison_tests/nec_carrier_burst.pio")); +// } +// +// test "pio.comparison.nec_carrier_control" { +// @setEvalBranchQuota(9000); +// try pio_comparison(@embedFile("comparison_tests/nec_carrier_control.pio")); +// } +// +// test "pio.comparison.nec_receive" { +// @setEvalBranchQuota(11000); +// try pio_comparison(@embedFile("comparison_tests/nec_receive.pio")); +// } +// +// test "pio.comparison.pio_serialiser" { +// @setEvalBranchQuota(3000); +// try pio_comparison(@embedFile("comparison_tests/pio_serialiser.pio")); +// } +// +// test "pio.comparison.pwm" { +// @setEvalBranchQuota(4000); +// try pio_comparison(@embedFile("comparison_tests/pwm.pio")); +// } +// +// test "pio.comparison.quadrature_encoder" { +// @setEvalBranchQuota(17000); +// try pio_comparison(@embedFile("comparison_tests/quadrature_encoder.pio")); +// } +// +// test "pio.comparison.resistor_dac" { +// @setEvalBranchQuota(3000); +// try pio_comparison(@embedFile("comparison_tests/resistor_dac.pio")); +// } +// +// test "pio.comparison.spi" { +// @setEvalBranchQuota(22000); +// try pio_comparison(@embedFile("comparison_tests/spi.pio")); +// } +// +// test "pio.comparison.squarewave" { +// @setEvalBranchQuota(2000); +// try pio_comparison(@embedFile("comparison_tests/squarewave.pio")); +// } +// +// test "pio.comparison.squarewave_fast" { +// @setEvalBranchQuota(2000); +// try pio_comparison(@embedFile("comparison_tests/squarewave_fast.pio")); +// } +// +// test "pio.comparison.squarewave_wrap" { +// @setEvalBranchQuota(3000); +// try pio_comparison(@embedFile("comparison_tests/squarewave_wrap.pio")); +// } +// +// test "pio.comparison.st7789_lcd" { +// @setEvalBranchQuota(5000); +// try pio_comparison(@embedFile("comparison_tests/st7789_lcd.pio")); +// } +// +// test "pio.comparison.uart_rx" { +// @setEvalBranchQuota(11000); +// try pio_comparison(@embedFile("comparison_tests/uart_rx.pio")); +// } +// +// test "pio.comparison.uart_tx" { +// @setEvalBranchQuota(6000); +// try pio_comparison(@embedFile("comparison_tests/uart_tx.pio")); +// } + +// test "pio.comparison.ws2812" { +// @setEvalBranchQuota(11000); +// try pio_comparison(@embedFile("comparison_tests/ws2812.pio")); +// } 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 index 5244bd0e..8a12a022 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio @@ -1,13 +1,20 @@ .program movrx .wrap_target + ; mov to rx mov rxfifoy, isr mov rxfifo0, isr mov rxfifo1, isr mov rxfifo2, isr mov rxfifo3, isr + ;mov rxfifo4, isr ; bad index + ;mov rxfifo3, xsr ; bad source + ; mov from rx mov osr, rxfifoy + ;mov osr, rxfifog ; bad source index (unparseable) + ;mov osr, rxfifo4 ; bad source index (>3) + ;mov osr, xxfifoy ; bad source ; mov osr, rxfifo0 ; mov osr, rxfifo1 ; mov osr, rxfifo2 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 index 92ea7e16..a1afebfe 100644 --- 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 @@ -1,13 +1,13 @@ #pragma once static const uint16_t movrx_program_instructions[] = { - // 0b1000_ssss_0001_yiii + // mov to rx encoding: 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 + // from from rx encoding 0b1000_ssss_1001_yiii 0x8098, // mov osr, rxfifoy // 0x8090, // mov osr, rxfifo0 // 0x8091, // mov osr, rxfifo1 diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig index a029ef0a..c6c353c7 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig @@ -18,7 +18,7 @@ pub const Options = struct { pub fn encode( comptime cpu: CPU, - comptime tokens: []const Token(cpu), + tokens: []const Token(cpu), diags: *?assembler.Diagnostics, comptime options: Options, ) !Encoder(cpu, options).Output { @@ -77,20 +77,24 @@ pub fn Encoder(comptime cpu: CPU, comptime options: Options) type { wrap_target: ?u5, wrap: ?u5, - pub fn to_exported_program(comptime bounded: BoundedProgram) assembler.Program { - comptime var program_name: [bounded.name.len]u8 = undefined; - std.mem.copyForwards(u8, &program_name, bounded.name); + pub fn to_exported_program(bounded: BoundedProgram) !assembler.Program { + const program_name = try std.heap.page_allocator.alloc(u8, bounded.name.len); + defer std.heap.page_allocator.free(program_name); + // var program_name: [bounded.name.len]u8 = undefined; + std.mem.copyForwards(u8, program_name, bounded.name); const name_const = program_name; return assembler.Program{ - .name = &name_const, + .name = name_const, .defines = blk: { var tmp = std.BoundedArray(assembler.Define, options.max_defines).init(0) catch unreachable; for (bounded.defines.slice()) |define| { - comptime var define_name: [define.name.len]u8 = undefined; - std.mem.copyForwards(u8, &define_name, define.name); + // comptime var define_name: [define.name.len]u8 = undefined; + const define_name = try std.heap.page_allocator.alloc(u8, define.name.len); + defer std.heap.page_allocator.free(define_name); + std.mem.copyForwards(u8, define_name, define.name); const const_def_name = define_name; tmp.append(.{ - .name = &const_def_name, + .name = const_def_name, .value = @as(i64, @intCast(define.value)), }) catch unreachable; } @@ -362,12 +366,16 @@ pub fn Encoder(comptime cpu: CPU, comptime options: Options) type { .source = mov.source, }, }, + // TODO: Can we make it s/t these prongs only exist for .RP2350? // NOTE: These instructions values only exist for RP2350 - .movtorx => |mov| .{ - .movtorx = .{ - .idx = mov.idx, - .idxl = @intFromBool(mov.idxl), - }, + .movtorx => |mov| blk: { + std.debug.print("Got movtorx\n", .{}); + break :blk .{ + .movtorx = .{ + .idx = mov.idx, + .idxl = @intFromBool(mov.idxl), + }, + }; }, .movfromrx => |mov| .{ .movfromrx = .{ @@ -536,6 +544,7 @@ pub fn Encoder(comptime cpu: CPU, comptime options: Options) type { const program_token = self.get_token() orelse return null; if (program_token.data != .program) return error.ExpectedProgramToken; + std.debug.print("Program token {s}\n", .{program_token.data.program}); var program = BoundedProgram{ .name = program_token.data.program, @@ -551,6 +560,7 @@ pub fn Encoder(comptime cpu: CPU, comptime options: Options) type { try self.encode_program_init(&program, diags); try self.encode_instruction_body(&program, diags); + std.debug.print("Program token {any}\n", .{program.instructions}); return program; } @@ -580,6 +590,8 @@ pub fn Instruction(comptime cpu: CPU) type { push: Push, pull: Pull, mov: Mov, + // TODO: If CPU stuff else noreturn (doesn't work?) + // packed unions cannot contain fields of type 'noreturn' movtorx: MovToRx, movfromrx: MovFromRx, irq: Irq, diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index 115b8d7f..0ad960bf 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -839,7 +839,6 @@ pub fn Tokenizer(cpu: CPU) type { // -- Parse out the index diags.* = Diagnostics.init( dest_idx, - // @intCast(self.index - dest_str.len + 1), "mov (to rx): destination must be rxfifoy or rxfifo[]", .{}, ); @@ -2052,234 +2051,234 @@ test "tokenize.instr.pull.ifempty" { }, tokens.get(0)); } -test "tokenize.instr.mov" { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { - inline for (.{ - "pins", - "x", - "y", - "null", - "status", - "isr", - "osr", - }) |source| { - const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("mov x {s}", .{source})); - - try expect_instr_mov(cpu, .{ - .source = @field(Token(cpu).Instruction.Mov.Source, source), - .destination = .x, - }, tokens.get(0)); - } - - inline for (.{ - "pins", - "x", - "y", - "exec", - "pc", - "isr", - "osr", - }) |dest| { - const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("mov {s} x", .{dest})); - - try expect_instr_mov(cpu, .{ - .source = .x, - .destination = @field(Token(cpu).Instruction.Mov.Destination, dest), - }, tokens.get(0)); - } - // RP2350 also supports pindirs as dest - { - const tokens = try bounded_tokenize(.RP2350, "mov pindirs x"); - - try expect_instr_mov(.RP2350, .{ - .source = .x, - .destination = @field(Token(.RP2350).Instruction.Mov.Destination, "pindirs"), - }, tokens.get(0)); - } - - const Operation = Token(cpu).Instruction.Mov.Operation; - const operations = std.StaticStringMap(Operation).initComptime(.{ - .{ "!", .invert }, - .{ "~", .invert }, - .{ "::", .bit_reverse }, - }); - - inline for (.{ "", " " }) |space| { - inline for (comptime operations.keys(), comptime operations.values()) |str, operation| { - const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("mov x {s}{s}y", .{ - str, - space, - })); - - try expect_instr_mov(cpu, .{ - .destination = .x, - .operation = operation, - .source = .y, - }, tokens.get(0)); - } - } - } -} - -test "tokenize.instr.irq" { - const ClearWait = struct { - clear: bool, - wait: bool, - }; - - const modes = std.StaticStringMap(ClearWait).initComptime(.{ - .{ "", .{ .clear = false, .wait = false } }, - .{ "set", .{ .clear = false, .wait = false } }, - .{ "nowait", .{ .clear = false, .wait = false } }, - .{ "wait", .{ .clear = false, .wait = true } }, - .{ "clear", .{ .clear = true, .wait = false } }, - }); - - inline for (comptime modes.keys(), comptime modes.values(), 0..) |key, value, num| { - inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { - const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("irq {s} {}", .{ - key, - num, - })); - - try expect_instr_irq(cpu, .{ - .clear = value.clear, - .wait = value.wait, - .num = num, - }, tokens.get(0)); - } - } -} - -test "tokenize.instr.irq.rel" { - const tokens = try bounded_tokenize(.RP2040, "irq set 2 rel"); - // TODO: rp2350 support - try expect_instr_irq(.RP2040, .{ - .clear = false, - .wait = false, - .num = 2, - .rel = true, - }, tokens.get(0)); -} - -test "tokenize.instr.set" { - inline for (.{ - "pins", - "x", - "y", - "pindirs", - }) |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(.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)); -} - -const instruction_examples = .{ - "nop", - "jmp arst", - "wait 0 gpio 1", - "in pins, 2", - "out pc, 1", - "push", - "pull", - "mov x y", - "irq 1", - "set pins 2", -}; - -test "tokenize.instr.label prefixed" { - inline for (instruction_examples) |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(.RP2040, .{ .name = "my_label" }, tokens.get(0)); - } -} - -test "tokenize.instr.side_set" { - inline for (instruction_examples) |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", - }, token.data.instruction.side_set.?); - try expectEqual(@as(?Value, null), token.data.instruction.delay); - } -} - -test "tokenize.instr.delay" { - inline for (instruction_examples) |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(.{ - .expression = "1", - }, token.data.instruction.delay.?); - } -} - -test "tokenize.instr.delay.expression" { - inline for (instruction_examples) |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(.{ - .expression = "T-1", - }, token.data.instruction.delay.?); - } -} - -test "tokenize.instr.side_set.expression" { - inline for (instruction_examples) |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)", - }, token.data.instruction.side_set.?); - try expectEqual(@as(?Value, null), token.data.instruction.delay); - } -} - -test "tokenize.instr.side_set and delay" { - inline for (instruction_examples) |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", - }, token.data.instruction.side_set.?); - try expect_value(.{ - .expression = "2", - }, token.data.instruction.delay.?); - } -} - -test "tokenize.instr.side_set and delay reversed" { - inline for (instruction_examples) |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", - }, token.data.instruction.side_set.?); - try expect_value(.{ - .expression = "2", - }, token.data.instruction.delay.?); - } -} - -test "tokenize.instr.comment with no whitespace" { - 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)); -} +// test "tokenize.instr.mov" { +// inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { +// inline for (.{ +// "pins", +// "x", +// "y", +// "null", +// "status", +// "isr", +// "osr", +// }) |source| { +// const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("mov x {s}", .{source})); +// +// try expect_instr_mov(cpu, .{ +// .source = @field(Token(cpu).Instruction.Mov.Source, source), +// .destination = .x, +// }, tokens.get(0)); +// } +// +// inline for (.{ +// "pins", +// "x", +// "y", +// "exec", +// "pc", +// "isr", +// "osr", +// }) |dest| { +// const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("mov {s} x", .{dest})); +// +// try expect_instr_mov(cpu, .{ +// .source = .x, +// .destination = @field(Token(cpu).Instruction.Mov.Destination, dest), +// }, tokens.get(0)); +// } +// // RP2350 also supports pindirs as dest +// { +// const tokens = try bounded_tokenize(.RP2350, "mov pindirs x"); +// +// try expect_instr_mov(.RP2350, .{ +// .source = .x, +// .destination = @field(Token(.RP2350).Instruction.Mov.Destination, "pindirs"), +// }, tokens.get(0)); +// } +// +// const Operation = Token(cpu).Instruction.Mov.Operation; +// const operations = std.StaticStringMap(Operation).initComptime(.{ +// .{ "!", .invert }, +// .{ "~", .invert }, +// .{ "::", .bit_reverse }, +// }); +// +// inline for (.{ "", " " }) |space| { +// inline for (comptime operations.keys(), comptime operations.values()) |str, operation| { +// const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("mov x {s}{s}y", .{ +// str, +// space, +// })); +// +// try expect_instr_mov(cpu, .{ +// .destination = .x, +// .operation = operation, +// .source = .y, +// }, tokens.get(0)); +// } +// } +// } +// } +// +// test "tokenize.instr.irq" { +// const ClearWait = struct { +// clear: bool, +// wait: bool, +// }; +// +// const modes = std.StaticStringMap(ClearWait).initComptime(.{ +// .{ "", .{ .clear = false, .wait = false } }, +// .{ "set", .{ .clear = false, .wait = false } }, +// .{ "nowait", .{ .clear = false, .wait = false } }, +// .{ "wait", .{ .clear = false, .wait = true } }, +// .{ "clear", .{ .clear = true, .wait = false } }, +// }); +// +// inline for (comptime modes.keys(), comptime modes.values(), 0..) |key, value, num| { +// inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { +// const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("irq {s} {}", .{ +// key, +// num, +// })); +// +// try expect_instr_irq(cpu, .{ +// .clear = value.clear, +// .wait = value.wait, +// .num = num, +// }, tokens.get(0)); +// } +// } +// } +// +// test "tokenize.instr.irq.rel" { +// const tokens = try bounded_tokenize(.RP2040, "irq set 2 rel"); +// // TODO: rp2350 support +// try expect_instr_irq(.RP2040, .{ +// .clear = false, +// .wait = false, +// .num = 2, +// .rel = true, +// }, tokens.get(0)); +// } +// +// test "tokenize.instr.set" { +// inline for (.{ +// "pins", +// "x", +// "y", +// "pindirs", +// }) |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(.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)); +// } +// +// const instruction_examples = .{ +// "nop", +// "jmp arst", +// "wait 0 gpio 1", +// "in pins, 2", +// "out pc, 1", +// "push", +// "pull", +// "mov x y", +// "irq 1", +// "set pins 2", +// }; +// +// test "tokenize.instr.label prefixed" { +// inline for (instruction_examples) |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(.RP2040, .{ .name = "my_label" }, tokens.get(0)); +// } +// } +// +// test "tokenize.instr.side_set" { +// inline for (instruction_examples) |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", +// }, token.data.instruction.side_set.?); +// try expectEqual(@as(?Value, null), token.data.instruction.delay); +// } +// } +// +// test "tokenize.instr.delay" { +// inline for (instruction_examples) |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(.{ +// .expression = "1", +// }, token.data.instruction.delay.?); +// } +// } +// +// test "tokenize.instr.delay.expression" { +// inline for (instruction_examples) |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(.{ +// .expression = "T-1", +// }, token.data.instruction.delay.?); +// } +// } +// +// test "tokenize.instr.side_set.expression" { +// inline for (instruction_examples) |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)", +// }, token.data.instruction.side_set.?); +// try expectEqual(@as(?Value, null), token.data.instruction.delay); +// } +// } +// +// test "tokenize.instr.side_set and delay" { +// inline for (instruction_examples) |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", +// }, token.data.instruction.side_set.?); +// try expect_value(.{ +// .expression = "2", +// }, token.data.instruction.delay.?); +// } +// } +// +// test "tokenize.instr.side_set and delay reversed" { +// inline for (instruction_examples) |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", +// }, token.data.instruction.side_set.?); +// try expect_value(.{ +// .expression = "2", +// }, token.data.instruction.delay.?); +// } +// } +// +// test "tokenize.instr.comment with no whitespace" { +// 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)); +// } From eef3e79b66319133ee94d0c6477402a88574b891 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Wed, 15 Jan 2025 19:15:58 -0500 Subject: [PATCH 25/30] Revert "wip: Allow pio assemble to work at runtime" We currently depend on this stuff being comptime, would have to manage the memory if we wanted to support it running at runtime This reverts commit dc3b8578a308ccad55ae5688796c4a93d18aa146. --- .../rp2xxx/src/hal/pio/assembler.zig | 16 +- .../hal/pio/assembler/comparison_tests.zig | 283 +++++------ .../pio/assembler/comparison_tests/movrx.pio | 7 - .../assembler/comparison_tests/movrx.pio.h | 4 +- .../rp2xxx/src/hal/pio/assembler/encoder.zig | 38 +- .../src/hal/pio/assembler/tokenizer.zig | 463 +++++++++--------- 6 files changed, 395 insertions(+), 416 deletions(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig index ce27927d..36f877ec 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig @@ -10,9 +10,7 @@ pub const EncodeOptions = encoder.Options; pub const Define = struct { name: []const u8, - // NO idea why this has to change - value: i128, - // value: i64, + value: i64, }; pub const Program = struct { @@ -74,16 +72,12 @@ pub const Diagnostics = struct { } }; -pub fn assemble_impl(comptime cpu: CPU, source: []const u8, diags: *?Diagnostics, comptime options: AssembleOptions) !Output { +pub fn assemble_impl(comptime cpu: CPU, comptime source: []const u8, diags: *?Diagnostics, options: AssembleOptions) !Output { const tokens = try tokenizer.tokenize(cpu, source, diags, options.tokenize); - for (tokens.slice()) |t| // DELETEME - std.debug.print("Got tokens: {any}\n", .{t}); // DELETEME const encoder_output = try encoder.encode(cpu, tokens.slice(), diags, options.encode); var programs = std.BoundedArray(Program, options.encode.max_programs).init(0) catch unreachable; - for (encoder_output.programs.slice()) |bounded| { - const prog = try bounded.to_exported_program(); - try programs.append(prog); - } + for (encoder_output.programs.slice()) |bounded| + try programs.append(bounded.to_exported_program()); return Output{ .defines = blk: { @@ -108,7 +102,7 @@ fn format_compile_error(comptime message: []const u8, comptime source: []const u line_str = line_str ++ "\n" ++ line; // If the line iterator is overlapping the provided index, then we are on the correct line if (line_it.index >= index) { - // Calculate the column + // Calculate the column column = line.len - (line_it.index - index); line_str = line; break; 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 bfc786be..95add43c 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests.zig @@ -40,20 +40,25 @@ fn pio_comparison(comptime source: []const u8) !void { } } -fn pio_comparison_cpu(comptime cpu: CPU, source: []const u8) !void { - var diags: ?assembler.Diagnostics = null; - const output = assembler.assemble_impl(cpu, source, &diags, .{}) catch |e| { - std.log.debug("diags: {any}\n", .{diags}); - std.log.debug("could not assemble: {}\n", .{e}); - return e; - }; +fn pio_comparison_cpu(comptime cpu: CPU, comptime source: []const u8) !void { + // comptime var diags: ?assembler.Diagnostics = null; + // const output = comptime blk: { + // const v = assembler.assemble_impl(cpu, source, &diags, .{}) catch |err| { + // @compileLog("err {}", err); + // if (diags != null) { + // std.log.debug("diag {}", .{diags}); + // @compileLog("Diag", diags); + // } + // // I want to return an error here but this is comptime? + // break :blk err; + // }; + // break :blk v; + // }; + const output = comptime assembler.assemble(cpu, source, .{}); try std.testing.expect(output.programs.len > 0); - for (output.programs) |program| { - std.debug.print("Program: {any}\n", .{program.instructions}); - // const expected_insns = @field(c, program.name ++ "_program_instructions"); - // Hardcoded for now since program.name is runtime for me - const expected_insns = @field(c, "movrx_program_instructions"); + inline for (output.programs) |program| { + const expected_insns = @field(c, program.name ++ "_program_instructions"); for (program.instructions, expected_insns) |actual, expected| { std.log.debug("expected: 0x{x}", .{expected}); std.log.debug(" actual: 0x{x}", .{actual}); @@ -65,134 +70,132 @@ fn pio_comparison_cpu(comptime cpu: CPU, source: []const u8) !void { } } -// test "pio.comparison.addition" { -// @setEvalBranchQuota(4000); -// try pio_comparison(@embedFile("comparison_tests/addition.pio")); -// } -// -// test "pio.comparison.apa102" { -// @setEvalBranchQuota(11000); -// try pio_comparison(@embedFile("comparison_tests/apa102.pio")); -// } -// -// test "pio.comparison.blink" { -// @setEvalBranchQuota(4000); -// try pio_comparison(@embedFile("comparison_tests/blink.pio")); -// } -// -// test "pio.comparison.clocked_input" { -// @setEvalBranchQuota(5000); -// try pio_comparison(@embedFile("comparison_tests/clocked_input.pio")); -// } -// -// test "pio.comparison.differential_manchester" { -// @setEvalBranchQuota(14000); -// try pio_comparison(@embedFile("comparison_tests/differential_manchester.pio")); -// } -// -// test "pio.comparison.hello" { -// @setEvalBranchQuota(3000); -// try pio_comparison(@embedFile("comparison_tests/hello.pio")); -// } -// -// test "pio.comparison.hub75" { -// @setEvalBranchQuota(17000); -// try pio_comparison(@embedFile("comparison_tests/hub75.pio")); -// } -// -// test "pio.comparison.i2c" { -// @setEvalBranchQuota(17000); -// try pio_comparison(@embedFile("comparison_tests/i2c.pio")); -// } - -// test "pio.comparison.irq" { -// @setEvalBranchQuota(22000); -// try pio_comparison_cpu(.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.addition" { + @setEvalBranchQuota(4000); + try pio_comparison(@embedFile("comparison_tests/addition.pio")); +} + +test "pio.comparison.apa102" { + @setEvalBranchQuota(11000); + try pio_comparison(@embedFile("comparison_tests/apa102.pio")); +} + +test "pio.comparison.blink" { + @setEvalBranchQuota(4000); + try pio_comparison(@embedFile("comparison_tests/blink.pio")); +} + +test "pio.comparison.clocked_input" { + @setEvalBranchQuota(5000); + try pio_comparison(@embedFile("comparison_tests/clocked_input.pio")); +} + +test "pio.comparison.differential_manchester" { + @setEvalBranchQuota(14000); + try pio_comparison(@embedFile("comparison_tests/differential_manchester.pio")); +} + +test "pio.comparison.hello" { + @setEvalBranchQuota(3000); + try pio_comparison(@embedFile("comparison_tests/hello.pio")); +} + +test "pio.comparison.hub75" { + @setEvalBranchQuota(17000); + try pio_comparison(@embedFile("comparison_tests/hub75.pio")); +} + +test "pio.comparison.i2c" { + @setEvalBranchQuota(17000); + try pio_comparison(@embedFile("comparison_tests/i2c.pio")); +} + +test "pio.comparison.irq" { + @setEvalBranchQuota(22000); + try pio_comparison_cpu(.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); - // @compileLog("movrx test"); // DELETEME try pio_comparison_cpu(.RP2350, @embedFile("comparison_tests/movrx.pio")); - // @compileLog("movrx test done"); // DELETEME -} -// -// test "pio.comparison.nec_carrier_burst" { -// @setEvalBranchQuota(6000); -// try pio_comparison(@embedFile("comparison_tests/nec_carrier_burst.pio")); -// } -// -// test "pio.comparison.nec_carrier_control" { -// @setEvalBranchQuota(9000); -// try pio_comparison(@embedFile("comparison_tests/nec_carrier_control.pio")); -// } -// -// test "pio.comparison.nec_receive" { -// @setEvalBranchQuota(11000); -// try pio_comparison(@embedFile("comparison_tests/nec_receive.pio")); -// } -// -// test "pio.comparison.pio_serialiser" { -// @setEvalBranchQuota(3000); -// try pio_comparison(@embedFile("comparison_tests/pio_serialiser.pio")); -// } -// -// test "pio.comparison.pwm" { -// @setEvalBranchQuota(4000); -// try pio_comparison(@embedFile("comparison_tests/pwm.pio")); -// } -// -// test "pio.comparison.quadrature_encoder" { -// @setEvalBranchQuota(17000); -// try pio_comparison(@embedFile("comparison_tests/quadrature_encoder.pio")); -// } -// -// test "pio.comparison.resistor_dac" { -// @setEvalBranchQuota(3000); -// try pio_comparison(@embedFile("comparison_tests/resistor_dac.pio")); -// } -// -// test "pio.comparison.spi" { -// @setEvalBranchQuota(22000); -// try pio_comparison(@embedFile("comparison_tests/spi.pio")); -// } -// -// test "pio.comparison.squarewave" { -// @setEvalBranchQuota(2000); -// try pio_comparison(@embedFile("comparison_tests/squarewave.pio")); -// } -// -// test "pio.comparison.squarewave_fast" { -// @setEvalBranchQuota(2000); -// try pio_comparison(@embedFile("comparison_tests/squarewave_fast.pio")); -// } -// -// test "pio.comparison.squarewave_wrap" { -// @setEvalBranchQuota(3000); -// try pio_comparison(@embedFile("comparison_tests/squarewave_wrap.pio")); -// } -// -// test "pio.comparison.st7789_lcd" { -// @setEvalBranchQuota(5000); -// try pio_comparison(@embedFile("comparison_tests/st7789_lcd.pio")); -// } -// -// test "pio.comparison.uart_rx" { -// @setEvalBranchQuota(11000); -// try pio_comparison(@embedFile("comparison_tests/uart_rx.pio")); -// } -// -// test "pio.comparison.uart_tx" { -// @setEvalBranchQuota(6000); -// try pio_comparison(@embedFile("comparison_tests/uart_tx.pio")); -// } - -// test "pio.comparison.ws2812" { -// @setEvalBranchQuota(11000); -// try pio_comparison(@embedFile("comparison_tests/ws2812.pio")); -// } +} + +test "pio.comparison.nec_carrier_burst" { + @setEvalBranchQuota(6000); + try pio_comparison(@embedFile("comparison_tests/nec_carrier_burst.pio")); +} + +test "pio.comparison.nec_carrier_control" { + @setEvalBranchQuota(9000); + try pio_comparison(@embedFile("comparison_tests/nec_carrier_control.pio")); +} + +test "pio.comparison.nec_receive" { + @setEvalBranchQuota(11000); + try pio_comparison(@embedFile("comparison_tests/nec_receive.pio")); +} + +test "pio.comparison.pio_serialiser" { + @setEvalBranchQuota(3000); + try pio_comparison(@embedFile("comparison_tests/pio_serialiser.pio")); +} + +test "pio.comparison.pwm" { + @setEvalBranchQuota(4000); + try pio_comparison(@embedFile("comparison_tests/pwm.pio")); +} + +test "pio.comparison.quadrature_encoder" { + @setEvalBranchQuota(17000); + try pio_comparison(@embedFile("comparison_tests/quadrature_encoder.pio")); +} + +test "pio.comparison.resistor_dac" { + @setEvalBranchQuota(3000); + try pio_comparison(@embedFile("comparison_tests/resistor_dac.pio")); +} + +test "pio.comparison.spi" { + @setEvalBranchQuota(22000); + try pio_comparison(@embedFile("comparison_tests/spi.pio")); +} + +test "pio.comparison.squarewave" { + @setEvalBranchQuota(2000); + try pio_comparison(@embedFile("comparison_tests/squarewave.pio")); +} + +test "pio.comparison.squarewave_fast" { + @setEvalBranchQuota(2000); + try pio_comparison(@embedFile("comparison_tests/squarewave_fast.pio")); +} + +test "pio.comparison.squarewave_wrap" { + @setEvalBranchQuota(3000); + try pio_comparison(@embedFile("comparison_tests/squarewave_wrap.pio")); +} + +test "pio.comparison.st7789_lcd" { + @setEvalBranchQuota(5000); + try pio_comparison(@embedFile("comparison_tests/st7789_lcd.pio")); +} + +test "pio.comparison.uart_rx" { + @setEvalBranchQuota(11000); + try pio_comparison(@embedFile("comparison_tests/uart_rx.pio")); +} + +test "pio.comparison.uart_tx" { + @setEvalBranchQuota(6000); + try pio_comparison(@embedFile("comparison_tests/uart_tx.pio")); +} + +test "pio.comparison.ws2812" { + @setEvalBranchQuota(11000); + try pio_comparison(@embedFile("comparison_tests/ws2812.pio")); +} 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 index 8a12a022..5244bd0e 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio @@ -1,20 +1,13 @@ .program movrx .wrap_target - ; mov to rx mov rxfifoy, isr mov rxfifo0, isr mov rxfifo1, isr mov rxfifo2, isr mov rxfifo3, isr - ;mov rxfifo4, isr ; bad index - ;mov rxfifo3, xsr ; bad source - ; mov from rx mov osr, rxfifoy - ;mov osr, rxfifog ; bad source index (unparseable) - ;mov osr, rxfifo4 ; bad source index (>3) - ;mov osr, xxfifoy ; bad source ; mov osr, rxfifo0 ; mov osr, rxfifo1 ; mov osr, rxfifo2 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 index a1afebfe..92ea7e16 100644 --- 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 @@ -1,13 +1,13 @@ #pragma once static const uint16_t movrx_program_instructions[] = { - // mov to rx encoding: 0b1000_ssss_0001_yiii + // 0b1000_ssss_0001_yiii 0x8018, // mov rxfifoy, isr 0x8010, // mov rxfifo0, isr 0x8011, // mov rxfifo1, isr 0x8012, // mov rxfifo2, isr 0x8013, // mov rxfifo3, isr - // from from rx encoding 0b1000_ssss_1001_yiii + // 0b1000_ssss_1001_yiii 0x8098, // mov osr, rxfifoy // 0x8090, // mov osr, rxfifo0 // 0x8091, // mov osr, rxfifo1 diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig index c6c353c7..a029ef0a 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/encoder.zig @@ -18,7 +18,7 @@ pub const Options = struct { pub fn encode( comptime cpu: CPU, - tokens: []const Token(cpu), + comptime tokens: []const Token(cpu), diags: *?assembler.Diagnostics, comptime options: Options, ) !Encoder(cpu, options).Output { @@ -77,24 +77,20 @@ pub fn Encoder(comptime cpu: CPU, comptime options: Options) type { wrap_target: ?u5, wrap: ?u5, - pub fn to_exported_program(bounded: BoundedProgram) !assembler.Program { - const program_name = try std.heap.page_allocator.alloc(u8, bounded.name.len); - defer std.heap.page_allocator.free(program_name); - // var program_name: [bounded.name.len]u8 = undefined; - std.mem.copyForwards(u8, program_name, bounded.name); + pub fn to_exported_program(comptime bounded: BoundedProgram) assembler.Program { + comptime var program_name: [bounded.name.len]u8 = undefined; + std.mem.copyForwards(u8, &program_name, bounded.name); const name_const = program_name; return assembler.Program{ - .name = name_const, + .name = &name_const, .defines = blk: { var tmp = std.BoundedArray(assembler.Define, options.max_defines).init(0) catch unreachable; for (bounded.defines.slice()) |define| { - // comptime var define_name: [define.name.len]u8 = undefined; - const define_name = try std.heap.page_allocator.alloc(u8, define.name.len); - defer std.heap.page_allocator.free(define_name); - std.mem.copyForwards(u8, define_name, define.name); + comptime var define_name: [define.name.len]u8 = undefined; + std.mem.copyForwards(u8, &define_name, define.name); const const_def_name = define_name; tmp.append(.{ - .name = const_def_name, + .name = &const_def_name, .value = @as(i64, @intCast(define.value)), }) catch unreachable; } @@ -366,16 +362,12 @@ pub fn Encoder(comptime cpu: CPU, comptime options: Options) type { .source = mov.source, }, }, - // TODO: Can we make it s/t these prongs only exist for .RP2350? // NOTE: These instructions values only exist for RP2350 - .movtorx => |mov| blk: { - std.debug.print("Got movtorx\n", .{}); - break :blk .{ - .movtorx = .{ - .idx = mov.idx, - .idxl = @intFromBool(mov.idxl), - }, - }; + .movtorx => |mov| .{ + .movtorx = .{ + .idx = mov.idx, + .idxl = @intFromBool(mov.idxl), + }, }, .movfromrx => |mov| .{ .movfromrx = .{ @@ -544,7 +536,6 @@ pub fn Encoder(comptime cpu: CPU, comptime options: Options) type { const program_token = self.get_token() orelse return null; if (program_token.data != .program) return error.ExpectedProgramToken; - std.debug.print("Program token {s}\n", .{program_token.data.program}); var program = BoundedProgram{ .name = program_token.data.program, @@ -560,7 +551,6 @@ pub fn Encoder(comptime cpu: CPU, comptime options: Options) type { try self.encode_program_init(&program, diags); try self.encode_instruction_body(&program, diags); - std.debug.print("Program token {any}\n", .{program.instructions}); return program; } @@ -590,8 +580,6 @@ pub fn Instruction(comptime cpu: CPU) type { push: Push, pull: Pull, mov: Mov, - // TODO: If CPU stuff else noreturn (doesn't work?) - // packed unions cannot contain fields of type 'noreturn' movtorx: MovToRx, movfromrx: MovFromRx, irq: Irq, diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index 0ad960bf..115b8d7f 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -839,6 +839,7 @@ pub fn Tokenizer(cpu: CPU) type { // -- Parse out the index diags.* = Diagnostics.init( dest_idx, + // @intCast(self.index - dest_str.len + 1), "mov (to rx): destination must be rxfifoy or rxfifo[]", .{}, ); @@ -2051,234 +2052,234 @@ test "tokenize.instr.pull.ifempty" { }, tokens.get(0)); } -// test "tokenize.instr.mov" { -// inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { -// inline for (.{ -// "pins", -// "x", -// "y", -// "null", -// "status", -// "isr", -// "osr", -// }) |source| { -// const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("mov x {s}", .{source})); -// -// try expect_instr_mov(cpu, .{ -// .source = @field(Token(cpu).Instruction.Mov.Source, source), -// .destination = .x, -// }, tokens.get(0)); -// } -// -// inline for (.{ -// "pins", -// "x", -// "y", -// "exec", -// "pc", -// "isr", -// "osr", -// }) |dest| { -// const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("mov {s} x", .{dest})); -// -// try expect_instr_mov(cpu, .{ -// .source = .x, -// .destination = @field(Token(cpu).Instruction.Mov.Destination, dest), -// }, tokens.get(0)); -// } -// // RP2350 also supports pindirs as dest -// { -// const tokens = try bounded_tokenize(.RP2350, "mov pindirs x"); -// -// try expect_instr_mov(.RP2350, .{ -// .source = .x, -// .destination = @field(Token(.RP2350).Instruction.Mov.Destination, "pindirs"), -// }, tokens.get(0)); -// } -// -// const Operation = Token(cpu).Instruction.Mov.Operation; -// const operations = std.StaticStringMap(Operation).initComptime(.{ -// .{ "!", .invert }, -// .{ "~", .invert }, -// .{ "::", .bit_reverse }, -// }); -// -// inline for (.{ "", " " }) |space| { -// inline for (comptime operations.keys(), comptime operations.values()) |str, operation| { -// const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("mov x {s}{s}y", .{ -// str, -// space, -// })); -// -// try expect_instr_mov(cpu, .{ -// .destination = .x, -// .operation = operation, -// .source = .y, -// }, tokens.get(0)); -// } -// } -// } -// } -// -// test "tokenize.instr.irq" { -// const ClearWait = struct { -// clear: bool, -// wait: bool, -// }; -// -// const modes = std.StaticStringMap(ClearWait).initComptime(.{ -// .{ "", .{ .clear = false, .wait = false } }, -// .{ "set", .{ .clear = false, .wait = false } }, -// .{ "nowait", .{ .clear = false, .wait = false } }, -// .{ "wait", .{ .clear = false, .wait = true } }, -// .{ "clear", .{ .clear = true, .wait = false } }, -// }); -// -// inline for (comptime modes.keys(), comptime modes.values(), 0..) |key, value, num| { -// inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { -// const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("irq {s} {}", .{ -// key, -// num, -// })); -// -// try expect_instr_irq(cpu, .{ -// .clear = value.clear, -// .wait = value.wait, -// .num = num, -// }, tokens.get(0)); -// } -// } -// } -// -// test "tokenize.instr.irq.rel" { -// const tokens = try bounded_tokenize(.RP2040, "irq set 2 rel"); -// // TODO: rp2350 support -// try expect_instr_irq(.RP2040, .{ -// .clear = false, -// .wait = false, -// .num = 2, -// .rel = true, -// }, tokens.get(0)); -// } -// -// test "tokenize.instr.set" { -// inline for (.{ -// "pins", -// "x", -// "y", -// "pindirs", -// }) |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(.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)); -// } -// -// const instruction_examples = .{ -// "nop", -// "jmp arst", -// "wait 0 gpio 1", -// "in pins, 2", -// "out pc, 1", -// "push", -// "pull", -// "mov x y", -// "irq 1", -// "set pins 2", -// }; -// -// test "tokenize.instr.label prefixed" { -// inline for (instruction_examples) |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(.RP2040, .{ .name = "my_label" }, tokens.get(0)); -// } -// } -// -// test "tokenize.instr.side_set" { -// inline for (instruction_examples) |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", -// }, token.data.instruction.side_set.?); -// try expectEqual(@as(?Value, null), token.data.instruction.delay); -// } -// } -// -// test "tokenize.instr.delay" { -// inline for (instruction_examples) |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(.{ -// .expression = "1", -// }, token.data.instruction.delay.?); -// } -// } -// -// test "tokenize.instr.delay.expression" { -// inline for (instruction_examples) |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(.{ -// .expression = "T-1", -// }, token.data.instruction.delay.?); -// } -// } -// -// test "tokenize.instr.side_set.expression" { -// inline for (instruction_examples) |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)", -// }, token.data.instruction.side_set.?); -// try expectEqual(@as(?Value, null), token.data.instruction.delay); -// } -// } -// -// test "tokenize.instr.side_set and delay" { -// inline for (instruction_examples) |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", -// }, token.data.instruction.side_set.?); -// try expect_value(.{ -// .expression = "2", -// }, token.data.instruction.delay.?); -// } -// } -// -// test "tokenize.instr.side_set and delay reversed" { -// inline for (instruction_examples) |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", -// }, token.data.instruction.side_set.?); -// try expect_value(.{ -// .expression = "2", -// }, token.data.instruction.delay.?); -// } -// } -// -// test "tokenize.instr.comment with no whitespace" { -// 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)); -// } +test "tokenize.instr.mov" { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + inline for (.{ + "pins", + "x", + "y", + "null", + "status", + "isr", + "osr", + }) |source| { + const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("mov x {s}", .{source})); + + try expect_instr_mov(cpu, .{ + .source = @field(Token(cpu).Instruction.Mov.Source, source), + .destination = .x, + }, tokens.get(0)); + } + + inline for (.{ + "pins", + "x", + "y", + "exec", + "pc", + "isr", + "osr", + }) |dest| { + const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("mov {s} x", .{dest})); + + try expect_instr_mov(cpu, .{ + .source = .x, + .destination = @field(Token(cpu).Instruction.Mov.Destination, dest), + }, tokens.get(0)); + } + // RP2350 also supports pindirs as dest + { + const tokens = try bounded_tokenize(.RP2350, "mov pindirs x"); + + try expect_instr_mov(.RP2350, .{ + .source = .x, + .destination = @field(Token(.RP2350).Instruction.Mov.Destination, "pindirs"), + }, tokens.get(0)); + } + + const Operation = Token(cpu).Instruction.Mov.Operation; + const operations = std.StaticStringMap(Operation).initComptime(.{ + .{ "!", .invert }, + .{ "~", .invert }, + .{ "::", .bit_reverse }, + }); + + inline for (.{ "", " " }) |space| { + inline for (comptime operations.keys(), comptime operations.values()) |str, operation| { + const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("mov x {s}{s}y", .{ + str, + space, + })); + + try expect_instr_mov(cpu, .{ + .destination = .x, + .operation = operation, + .source = .y, + }, tokens.get(0)); + } + } + } +} + +test "tokenize.instr.irq" { + const ClearWait = struct { + clear: bool, + wait: bool, + }; + + const modes = std.StaticStringMap(ClearWait).initComptime(.{ + .{ "", .{ .clear = false, .wait = false } }, + .{ "set", .{ .clear = false, .wait = false } }, + .{ "nowait", .{ .clear = false, .wait = false } }, + .{ "wait", .{ .clear = false, .wait = true } }, + .{ "clear", .{ .clear = true, .wait = false } }, + }); + + inline for (comptime modes.keys(), comptime modes.values(), 0..) |key, value, num| { + inline for (comptime .{ CPU.RP2040, CPU.RP2350 }) |cpu| { + const tokens = try bounded_tokenize(cpu, comptime std.fmt.comptimePrint("irq {s} {}", .{ + key, + num, + })); + + try expect_instr_irq(cpu, .{ + .clear = value.clear, + .wait = value.wait, + .num = num, + }, tokens.get(0)); + } + } +} + +test "tokenize.instr.irq.rel" { + const tokens = try bounded_tokenize(.RP2040, "irq set 2 rel"); + // TODO: rp2350 support + try expect_instr_irq(.RP2040, .{ + .clear = false, + .wait = false, + .num = 2, + .rel = true, + }, tokens.get(0)); +} + +test "tokenize.instr.set" { + inline for (.{ + "pins", + "x", + "y", + "pindirs", + }) |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(.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)); +} + +const instruction_examples = .{ + "nop", + "jmp arst", + "wait 0 gpio 1", + "in pins, 2", + "out pc, 1", + "push", + "pull", + "mov x y", + "irq 1", + "set pins 2", +}; + +test "tokenize.instr.label prefixed" { + inline for (instruction_examples) |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(.RP2040, .{ .name = "my_label" }, tokens.get(0)); + } +} + +test "tokenize.instr.side_set" { + inline for (instruction_examples) |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", + }, token.data.instruction.side_set.?); + try expectEqual(@as(?Value, null), token.data.instruction.delay); + } +} + +test "tokenize.instr.delay" { + inline for (instruction_examples) |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(.{ + .expression = "1", + }, token.data.instruction.delay.?); + } +} + +test "tokenize.instr.delay.expression" { + inline for (instruction_examples) |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(.{ + .expression = "T-1", + }, token.data.instruction.delay.?); + } +} + +test "tokenize.instr.side_set.expression" { + inline for (instruction_examples) |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)", + }, token.data.instruction.side_set.?); + try expectEqual(@as(?Value, null), token.data.instruction.delay); + } +} + +test "tokenize.instr.side_set and delay" { + inline for (instruction_examples) |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", + }, token.data.instruction.side_set.?); + try expect_value(.{ + .expression = "2", + }, token.data.instruction.delay.?); + } +} + +test "tokenize.instr.side_set and delay reversed" { + inline for (instruction_examples) |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", + }, token.data.instruction.side_set.?); + try expect_value(.{ + .expression = "2", + }, token.data.instruction.delay.?); + } +} + +test "tokenize.instr.comment with no whitespace" { + 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)); +} From cc38cd0d06743ab79bc444e25916a77b427dc785 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Wed, 15 Jan 2025 19:48:53 -0500 Subject: [PATCH 26/30] irq rel --- .../src/hal/pio/assembler/tokenizer.zig | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index 4763c304..fc4e7db0 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -750,11 +750,8 @@ pub fn Tokenizer(chip: Chip) type { } // 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 to - // the source and look for rxfifo. To peek twice we have to consume - // the first peek, but if we find it's mov from rx, then we need to - // unconsume the first arg, so that the tokenizer is in the right - // state when we call get_movrx + // 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); @@ -958,37 +955,30 @@ pub fn Tokenizer(chip: Chip) type { // This doesn't work: idx_mode's type only exists at comptime, // even though this is a comptime-only switch :/ // Hardcoding the type - const idx_mode: u2 = if (try self.peek_arg(diags)) |result| blk: { + 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())) { - // @compileLog("got rel"); // DELETEME self.consume_peek(result); - break :blk 0b10; - // break :blk .rel; + break :blk .rel; } else if (std.mem.eql(u8, "prev", idxmode_lower.slice())) { - // @compileLog("got prev"); // DELETEME self.consume_peek(result); - break :blk 0b01; - // break :blk .prev; + break :blk .prev; } else if (std.mem.eql(u8, "next", idxmode_lower.slice())) { - // @compileLog("got next"); // DELETEME self.consume_peek(result); - break :blk 0b11; - // break :blk .next; + break :blk .next; } else { // Not specified: direct - break :blk 0b00; - // break :blk .direct; + break :blk .direct; } - } else 0b00; - // } else .direct; + } else .direct; return Token(chip).Instruction.Payload{ .irq = .{ .clear = clear, .wait = wait, .num = num, - .idxmode = @enumFromInt(idx_mode), + .idxmode = idx_mode, }, }; }, @@ -1298,7 +1288,7 @@ pub fn Token(comptime chip: Chip) type { }; pub const Source = enum(u3) { - pins = 0b00, + pins = 0b000, x = 0b001, y = 0b010, null = 0b011, From 148ae167dd4750f21303ea59dcbe4ca5f7983ca0 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Wed, 15 Jan 2025 19:54:52 -0500 Subject: [PATCH 27/30] Fix mov from idx --- .../src/hal/pio/assembler/comparison_tests/movrx.pio | 8 ++++---- .../src/hal/pio/assembler/comparison_tests/movrx.pio.h | 8 ++++---- .../rp2xxx/src/hal/pio/assembler/tokenizer.zig | 1 + 3 files changed, 9 insertions(+), 8 deletions(-) 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 index 5244bd0e..5ecfed80 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests/movrx.pio @@ -8,8 +8,8 @@ mov rxfifo3, isr mov osr, rxfifoy -; mov osr, rxfifo0 -; mov osr, rxfifo1 -; mov osr, rxfifo2 -; mov osr, rxfifo3 + 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 index 92ea7e16..09f9ca44 100644 --- 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 @@ -9,8 +9,8 @@ static const uint16_t movrx_program_instructions[] = { 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 + 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/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index fc4e7db0..d53a24e3 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -874,6 +874,7 @@ pub fn Tokenizer(chip: Chip) type { if (value > 3) { return error.InvalidSource; } + idx = @intCast(value); } } else { diags.* = Diagnostics.init( From 9186848a68adb19ff21bc8ab291685b62f6e9427 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Wed, 15 Jan 2025 20:16:42 -0500 Subject: [PATCH 28/30] cleanup --- examples/raspberrypi/rp2xxx/build.zig | 53 ++++++++++--------- .../rp2xxx/src/hal/pio/assembler.zig | 2 - .../hal/pio/assembler/comparison_tests.zig | 13 ----- .../src/hal/pio/assembler/tokenizer.zig | 15 ++++-- 4 files changed, 38 insertions(+), 45 deletions(-) diff --git a/examples/raspberrypi/rp2xxx/build.zig b/examples/raspberrypi/rp2xxx/build.zig index 25aeeb56..e26f3d85 100644 --- a/examples/raspberrypi/rp2xxx/build.zig +++ b/examples/raspberrypi/rp2xxx/build.zig @@ -13,38 +13,41 @@ pub fn build(b: *std.Build) void { const rp2040_only_examples: []const Example = &.{ // RaspberryPi Boards: - // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_adc", .file = "src/rp2040_only/adc.zig" }, - // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_flash-program", .file = "src/rp2040_only/flash_program.zig" }, - // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_flash-id", .file = "src/rp2040_only/flash_id.zig" }, - // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_i2c-bus-scan", .file = "src/rp2040_only/i2c_bus_scan.zig" }, - // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_pwm", .file = "src/rp2040_only/pwm.zig" }, - // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_random", .file = "src/rp2040_only/random.zig" }, - // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_rtc", .file = "src/rp2040_only/rtc.zig" }, - // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_spi-host", .file = "src/rp2040_only/spi_host.zig" }, - // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_uart-echo", .file = "src/rp2040_only/uart_echo.zig" }, - // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_uart-log", .file = "src/rp2040_only/uart_log.zig" }, - // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_usb-hid", .file = "src/rp2040_only/usb_hid.zig" }, - // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_usb-cdc", .file = "src/rp2040_only/usb_cdc.zig" }, - // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_multicore", .file = "src/rp2040_only/blinky_core1.zig" }, - // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_hd44780", .file = "src/rp2040_only/hd44780.zig" }, - // .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_pcf8574", .file = "src/rp2040_only/pcf8574.zig" }, - // - // // WaveShare Boards: - // .{ .target = mb.ports.rp2xxx.boards.waveshare.rp2040_matrix, .name = "rp2040-matrix_tiles", .file = "src/rp2040_only/tiles.zig" }, + .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_adc", .file = "src/rp2040_only/adc.zig" }, + .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_flash-program", .file = "src/rp2040_only/flash_program.zig" }, + .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_flash-id", .file = "src/rp2040_only/flash_id.zig" }, + .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_i2c-bus-scan", .file = "src/rp2040_only/i2c_bus_scan.zig" }, + .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_pwm", .file = "src/rp2040_only/pwm.zig" }, + .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_random", .file = "src/rp2040_only/random.zig" }, + .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_rtc", .file = "src/rp2040_only/rtc.zig" }, + .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_spi-host", .file = "src/rp2040_only/spi_host.zig" }, + .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_uart-echo", .file = "src/rp2040_only/uart_echo.zig" }, + .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_uart-log", .file = "src/rp2040_only/uart_log.zig" }, + .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_usb-hid", .file = "src/rp2040_only/usb_hid.zig" }, + .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_usb-cdc", .file = "src/rp2040_only/usb_cdc.zig" }, + .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_multicore", .file = "src/rp2040_only/blinky_core1.zig" }, + .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_hd44780", .file = "src/rp2040_only/hd44780.zig" }, + .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_pcf8574", .file = "src/rp2040_only/pcf8574.zig" }, + + // WaveShare Boards: + .{ .target = mb.ports.rp2xxx.boards.waveshare.rp2040_matrix, .name = "rp2040-matrix_tiles", .file = "src/rp2040_only/tiles.zig" }, // .{ .target = "board:waveshare/rp2040_eth", .name = "rp2040-eth" }, // .{ .target = "board:waveshare/rp2040_plus_4m", .name = "rp2040-plus-4m" }, // .{ .target = "board:waveshare/rp2040_plus_16m", .name = "rp2040-plus-16m" }, }; - const rp2350_only_examples: []const Example = &.{}; + const rp2350_only_examples: []const Example = &.{ + // TODO: No RP2350 feature specific examples to show off yet + }; const chip_agnostic_examples: []const ChipAgnosticExample = &.{ - // .{ .name = "ws2812", .file = "src/ws2812.zig" }, - // .{ .name = "blinky", .file = "src/blinky.zig" }, - // .{ .name = "gpio-clock-output", .file = "src/gpio_clock_output.zig" }, - // .{ .name = "changing-system-clocks", .file = "src/changing_system_clocks.zig" }, - // .{ .name = "custom-clock-config", .file = "src/custom_clock_config.zig" }, - // .{ .name = "watchdog-timer", .file = "src/watchdog_timer.zig" }, + .{ .name = "squarewave", .file = "src/squarewave.zig" }, + .{ .name = "ws2812", .file = "src/ws2812.zig" }, + .{ .name = "blinky", .file = "src/blinky.zig" }, + .{ .name = "gpio-clock-output", .file = "src/gpio_clock_output.zig" }, + .{ .name = "changing-system-clocks", .file = "src/changing_system_clocks.zig" }, + .{ .name = "custom-clock-config", .file = "src/custom_clock_config.zig" }, + .{ .name = "watchdog-timer", .file = "src/watchdog_timer.zig" }, }; var available_examples = std.ArrayList(Example).init(b.allocator); diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig index 34229c9e..1f11a610 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler.zig @@ -100,9 +100,7 @@ fn format_compile_error(comptime message: []const u8, comptime source: []const u var line_it = std.mem.tokenize(u8, source, "\n\r"); while (line_it.next()) |line| : (line_num += 1) { line_str = line_str ++ "\n" ++ line; - // If the line iterator is overlapping the provided index, then we are on the correct line if (line_it.index >= index) { - // Calculate the column column = line.len - (line_it.index - index); line_str = line; break; 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 cc99263e..dea3b96d 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/comparison_tests.zig @@ -41,19 +41,6 @@ fn pio_comparison(comptime source: []const u8) !void { } fn pio_comparison_chip(comptime chip: Chip, comptime source: []const u8) !void { - // comptime var diags: ?assembler.Diagnostics = null; - // const output = comptime blk: { - // const v = assembler.assemble_impl(chip, source, &diags, .{}) catch |err| { - // @compileLog("err {}", err); - // if (diags != null) { - // std.log.debug("diag {}", .{diags}); - // @compileLog("Diag", diags); - // } - // // I want to return an error here but this is comptime? - // break :blk err; - // }; - // break :blk v; - // }; const output = comptime assembler.assemble(chip, source, .{}); try std.testing.expect(output.programs.len > 0); diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index d53a24e3..27a45ff7 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -953,9 +953,6 @@ pub fn Tokenizer(chip: Chip) type { }; }, .RP2350 => { - // This doesn't work: idx_mode's type only exists at comptime, - // even though this is a comptime-only switch :/ - // Hardcoding the type 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); @@ -969,7 +966,6 @@ pub fn Tokenizer(chip: Chip) type { self.consume_peek(result); break :blk .next; } else { - // Not specified: direct break :blk .direct; } } else .direct; @@ -2144,7 +2140,6 @@ test "tokenize.instr.irq" { test "tokenize.instr.irq.rel" { const tokens = try bounded_tokenize(.RP2040, "irq set 2 rel"); - // TODO: rp2350 support try expect_instr_irq(.RP2040, .{ .clear = false, .wait = false, @@ -2153,6 +2148,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", From 52ed8a15d523f688057c6ed319953c0c4a26bcd4 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Wed, 15 Jan 2025 20:24:07 -0500 Subject: [PATCH 29/30] fix --- port/raspberrypi/rp2xxx/src/hal/pio/common.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/common.zig b/port/raspberrypi/rp2xxx/src/hal/pio/common.zig index 5cdc28dc..1def10ee 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/common.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/common.zig @@ -13,7 +13,7 @@ const encoder = @import("assembler/encoder.zig"); const gpio = @import("../gpio.zig"); const hw = @import("../hw.zig"); -const Instruction = encoder.Instruction; +pub const Instruction = encoder.Instruction; pub const Program = assembler.Program; // global state for keeping track of used things From 2757bffae07a05ddd2db7d4fc5b4c88fad334249 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Thu, 16 Jan 2025 20:44:29 -0500 Subject: [PATCH 30/30] Some cleanup --- port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig index 27a45ff7..ee7be59b 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pio/assembler/tokenizer.zig @@ -809,11 +809,11 @@ pub fn Tokenizer(chip: Chip) type { } fn get_movrx(self: *Self, diags: *?Diagnostics) TokenizeError!Token(chip).Instruction.Payload { - // TODO: Assuming one space after opcode + // 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); - // TODO: Assuming comma and one space + // 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); @@ -836,7 +836,6 @@ pub fn Tokenizer(chip: Chip) type { // -- Parse out the index diags.* = Diagnostics.init( dest_idx, - // @intCast(self.index - dest_str.len + 1), "mov (to rx): destination must be rxfifoy or rxfifo[]", .{}, );