From 755d5c61977011cc85d74e0f057af44453c64945 Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 9 Aug 2024 14:26:54 -0700 Subject: [PATCH 01/38] add codec example --- examples/pioasm_i2s_codec.py | 210 +++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 examples/pioasm_i2s_codec.py diff --git a/examples/pioasm_i2s_codec.py b/examples/pioasm_i2s_codec.py new file mode 100644 index 0000000..d9cd74b --- /dev/null +++ b/examples/pioasm_i2s_codec.py @@ -0,0 +1,210 @@ +# SPDX-FileCopyrightText: 2024 Tim Chinowsky +# SPDX-License-Identifier: MIT + +import array +import board +import rp2pio + +import adafruit_pioasm + +# Implement extended multichannel I2S interface like that used by audio codecs +# such as the TAC5212. In extended I2S, "Left" and "Right" can each contain +# multiple channels, so for instance 8 channels of audio can be sent as a "left" +# containing 4 channels and a "right" containing 4 channels. + +# In this implementation the number of bits per sample, sample rate, and +# number of channels can be independently specified. The number of channels must +# be even, to divide evenly between left and right. + +# Ramped test data containing the requested number of sample sets (one set = one +# sample for each channel) and spanning the specified number of bits will be generated +# and sent out over I2S on the specified pins. + +# Data will be preceded and followed by a set of zeros for convenience. +# (Some protocol analyzers have trouble analyzing serial data at the the beginning +# and end of a data set) + +# At the same time that I2S data is sent out the out_pin, I2S data will be received +# on the in_pin. If the output is looped back (connected) to the input, the data +# received should be the same as the data sent. + +# Some samples run in loopback configuration: + +# bits per sample: 16 +# sample rate: 48000 +# channels: 4 +# sample sets: 4 + +# actual sample frequency 47984.6 Hz +# bit clock 3071017.0 Hz + +# write: 00000000 00000000 00000000 00000000 +# read: 00000000 00000000 00000000 00000000 + +# write: 00000000 00001111 00002222 00003333 +# read: 00000000 00001111 00002222 00003333 + +# write: 00004444 00005555 00006666 00007777 +# read: 00004444 00005555 00006666 00007777 + +# write: 00008888 00009999 0000aaaa 0000bbbb +# read: 00008888 00009999 0000aaaa 0000bbbb + +# write: 0000cccc 0000dddd 0000eeee 0000ffff +# read: 0000cccc 0000dddd 0000eeee 0000ffff + +# write: 00000000 00000000 00000000 00000000 +# read: 00000000 00000000 00000000 00000000 + +# bits per sample: 24 +# sample rate: 24000 +# channels: 8 +# sample sets: 5 + +# actual sample frequency 23987.7 Hz +# bit clock 4605642.0 Hz + +# write: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 +# read: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 + +# write: 00000000 00069069 000d20d2 0013b13b 001a41a4 0020d20d 00276276 002df2df +# read: 00000000 00069069 000d20d2 0013b13b 001a41a4 0020d20d 00276276 002df2df + +# write: 00348348 003b13b1 0041a41a 00483482 004ec4ec 00555554 005be5be 00627626 +# read: 00348348 003b13b1 0041a41a 00483482 004ec4ec 00555554 005be5be 00627626 + +# write: 00690690 006f96f8 00762762 007cb7ca 00834834 0089d89c 00906904 0096f96c +# read: 00690690 006f96f8 00762762 007cb7ca 00834834 0089d89c 00906904 0096f96c + +# write: 009d89d8 00a41a40 00aaaaa8 00b13b10 00b7cb7c 00be5be4 00c4ec4c 00cb7cb4 +# read: 009d89d8 00a41a40 00aaaaa8 00b13b10 00b7cb7c 00be5be4 00c4ec4c 00cb7cb4 + +# write: 00d20d20 00d89d88 00df2df0 00e5be58 00ec4ec4 00f2df2c 00f96f94 00ffffff +# read: 00d20d20 00d89d88 00df2df0 00e5be58 00ec4ec4 00f2df2c 00f96f94 00ffffff + +# write: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 +# read: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 + + +def i2s_codec( + channels=2, + sample_rate=48000, + bits=16, + bclk_pin=None, + out_pin=None, + in_pin=None, +): + i2s_clock = sample_rate * channels * bits + pio_clock = 4 * i2s_clock + pio_code = """ + .program i2s_codec + .side_set 2 + ; at program start we initialize the bit count top + ; (which may be >32) with data + ; pulled from the input fifo + pull noblock ; first empty the input fifo + pull noblock + pull noblock + pull noblock + out null, 32 ; then clear OSR so we can get a new value + pull block ; then get the bit count top value from the fifo + ; /--- LRCLK + ; |/-- BCLK + ; || + mov x, osr; side 0b01 [1] ; save it in x + out null, 32 side 0b00 [1] + mov y, x side 0b01 [1] ; start of main loop (wrap target=8) + bitloop1: + out pins 1 side 0b00 + in pins 1 side 0b00 + jmp y-- bitloop1 side 0b01 [1] + out pins 1 side 0b10 + in pins 1 side 0b10 + mov y, x side 0b11 [1] + bitloop0: + out pins 1 side 0b10 + in pins 1 side 0b10 + jmp y-- bitloop0 side 0b11 [1] + out pins 1 side 0b00 + in pins 1 side 0b00 + """ + pio_params = { + "frequency": pio_clock, + "first_out_pin": out_pin, + "first_in_pin": in_pin, + "first_sideset_pin": bclk_pin, + "sideset_pin_count": 2, + "auto_pull": True, + "auto_push": True, + "out_shift_right": False, + "in_shift_right": False, + "pull_threshold": bits, + "push_threshold": bits, + "wait_for_txstall": False, + "wrap_target": 8, + } + pio_instructions = adafruit_pioasm.assemble(pio_code) + i2s_clock = sample_rate * channels * bits + pio_clock = 4 * i2s_clock + pio = rp2pio.StateMachine(pio_instructions, **pio_params) + return pio + + +def spaced_samples(length, bits): + max_int = (1 << bits) - 1 + if length == 1: + return [0] + step = max_int / (length - 1) + result = [round(i * step) for i in range(length)] + result[0] = 0 + result[-1] = max_int + return result + + +while True: + print() + BITS = int(input("# bits per sample: ")) + SAMPLE_RATE = int(input("# sample rate: ")) + CHANNELS = int(input("# channels: ")) + SAMPLE_SETS = int(input("# sample sets: ")) + + n_samples = CHANNELS * SAMPLE_SETS + buffer_type = "L" + buffer_width = 32 + data = [0] * CHANNELS + spaced_samples(n_samples, BITS) + [0] * CHANNELS + # initialize pio bit count top value by sending it at the start of output data + bit_count_top = BITS * (CHANNELS // 2) - 2 + buffer_out = array.array( + buffer_type, [bit_count_top] + [d << (buffer_width - BITS) for d in data] + ) + buffer_in = array.array(buffer_type, [0] * len(data)) + + PIO = i2s_codec( + channels=CHANNELS, + bits=BITS, + sample_rate=SAMPLE_RATE, + out_pin=board.D9, + in_pin=board.D10, + bclk_pin=board.D5, # L/R signal will be one pin higher, i.e. D6 + ) + print() + print(f"# actual sample frequency {PIO.frequency/4/CHANNELS/BITS:9.1f} Hz") + print(f"# bit clock {PIO.frequency/4:9.1f} Hz") + print() + PIO.write_readinto(buffer_out, buffer_in) + start = 0 + line_length = CHANNELS + + while start < len(buffer_in): + print("# write: ", end="") + for i in range(start, min(len(data), start + line_length)): + print(f"{data[i]:0{buffer_width/4}x} ", end=" ") + print() + print("# read: ", end="") + for i in range(start, min(len(buffer_in), start + line_length)): + print(f"{buffer_in[i]:0{buffer_width/4}x} ", end=" ") + print() + print() + start += line_length + PIO.deinit() + del PIO From a8fa218e451d80e480ecd256b54d86faf9e7f5cc Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 5 Sep 2024 08:37:43 -0500 Subject: [PATCH 02/38] Recognize .pio_version directive (integer arguments only) --- adafruit_pioasm.py | 7 +++++++ tests/pytest_helpers.py | 4 +++- tests/test_version.py | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/test_version.py diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index fa5a529..cf7f7e5 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -58,6 +58,8 @@ def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: wrap = None wrap_target = None offset = -1 + pio_version = 0 + for i, line in enumerate(text_program.split("\n")): line = line.strip() if not line: @@ -68,6 +70,8 @@ def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: if program_name: raise RuntimeError("Multiple programs not supported") program_name = line.split()[1] + elif line.startswith(".pio_version"): + pio_version = int(line.split()[1], 0) elif line.startswith(".origin"): offset = int(line.split()[1], 0) elif line.startswith(".wrap_target"): @@ -247,6 +251,9 @@ def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: if offset != -1: self.pio_kwargs["offset"] = offset + if pio_version != 0: + self.pio_kwargs["pio_version"] = pio_version + if sideset_count != 0: self.pio_kwargs["sideset_pin_count"] = sideset_count diff --git a/tests/pytest_helpers.py b/tests/pytest_helpers.py index 72ac7cf..8425a5b 100644 --- a/tests/pytest_helpers.py +++ b/tests/pytest_helpers.py @@ -45,4 +45,6 @@ def assert_assembly_fails( def assert_pio_kwargs(source: str, **kw: Any) -> None: program = adafruit_pioasm.Program(source) - assert kw == program.pio_kwargs + assert ( + kw == program.pio_kwargs + ), f"Assembling {source!r}: Expected {kw}, got {program.pio_kwargs}" diff --git a/tests/test_version.py b/tests/test_version.py new file mode 100644 index 0000000..82717e8 --- /dev/null +++ b/tests/test_version.py @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2024 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +Tests version dependent instructions +""" + +from pytest_helpers import assert_pio_kwargs, assert_assembly_fails + + +def test_version() -> None: + assert_pio_kwargs(".pio_version 0", sideset_enable=0) + assert_pio_kwargs(".pio_version 1", pio_version=1, sideset_enable=0) + assert_assembly_fails(".pio_version muffin", errtype=ValueError) From 3da0f6cea239c4af373d5ace9effe0f247d2889b Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 5 Sep 2024 09:05:47 -0500 Subject: [PATCH 03/38] Add .fifo directive --- adafruit_pioasm.py | 19 +++++++++++++++++++ tests/test_version.py | 12 ++++++++++++ 2 files changed, 31 insertions(+) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index cf7f7e5..c22de11 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -33,6 +33,7 @@ MOV_SOURCES = ["pins", "x", "y", "null", None, "status", "isr", "osr"] MOV_OPS = [None, "~", "::", None] SET_DESTINATIONS = ["pins", "x", "y", None, "pindirs", None, None, None] +FIFO_TYPES = {"txrx": 0, "tx": 0, "rx": 0, "txput": 1, "txget": 1, "putget": 1} class Program: # pylint: disable=too-few-public-methods @@ -59,6 +60,13 @@ def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: wrap_target = None offset = -1 pio_version = 0 + fifo_type = None + + def require_version(required_version, instruction): + if pio_version < required_version: + raise RuntimeError( + f"{instruction} requires .pio_version {required_version}" + ) for i, line in enumerate(text_program.split("\n")): line = line.strip() @@ -72,6 +80,8 @@ def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: program_name = line.split()[1] elif line.startswith(".pio_version"): pio_version = int(line.split()[1], 0) + if not 0 <= pio_version <= 1: + raise RuntimeError(f"Invalid .pio_version {pio_version}") elif line.startswith(".origin"): offset = int(line.split()[1], 0) elif line.startswith(".wrap_target"): @@ -83,6 +93,12 @@ def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: elif line.startswith(".side_set"): sideset_count = int(line.split()[1], 0) sideset_enable = "opt" in line + elif line.startswith(".fifo"): + fifo_type = line.split()[1] + required_version = FIFO_TYPES.get(fifo_type) + if required_version is None: + raise RuntimeError(f"Invalid fifo type {fifo_type}") + require_version(required_version, line) elif line.endswith(":"): label = line[:-1] if label in labels: @@ -262,6 +278,9 @@ def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: if wrap_target is not None: self.pio_kwargs["wrap_target"] = wrap_target + if FIFO_TYPES.get(fifo_type): + self.pio_kwargs["fifo_type"] = fifo_type + self.assembled = array.array("H", assembled) self.debuginfo = (linemap, text_program) if build_debuginfo else None diff --git a/tests/test_version.py b/tests/test_version.py index 82717e8..cf0e63b 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -13,3 +13,15 @@ def test_version() -> None: assert_pio_kwargs(".pio_version 0", sideset_enable=0) assert_pio_kwargs(".pio_version 1", pio_version=1, sideset_enable=0) assert_assembly_fails(".pio_version muffin", errtype=ValueError) + assert_assembly_fails(".pio_version -1") + + +def test_fifo() -> None: + assert_pio_kwargs(".fifo txrx", sideset_enable=0) + assert_assembly_fails(".fifo txput") + assert_pio_kwargs( + ".pio_version 1\n.fifo txput", + pio_version=1, + sideset_enable=0, + fifo_type="txput", + ) From cfee88e040aa3bcd09de130de515ce5d474bad8d Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 5 Sep 2024 09:49:50 -0500 Subject: [PATCH 04/38] Add .mov_status --- adafruit_pioasm.py | 33 +++++++++++++++++++++++++++++++++ tests/test_version.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index c22de11..aea3030 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -61,6 +61,9 @@ def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: offset = -1 pio_version = 0 fifo_type = None + mov_status_type = None + mov_status_count = None + mov_status_param = None def require_version(required_version, instruction): if pio_version < required_version: @@ -99,6 +102,31 @@ def require_version(required_version, instruction): if required_version is None: raise RuntimeError(f"Invalid fifo type {fifo_type}") require_version(required_version, line) + elif line.startswith(".mov_status"): + words = line.split() + required_version = 0 + mov_status_param = 0 + mov_status_type = words[1] + if words[1] in ("txfifo", "rxfifo"): + if words[2] != "<": + raise RuntimeError(f"Invalid {line}") + mov_status_count = int(words[3]) + elif words[1] == "irq": + required_version = 1 + idx = 2 + if words[idx] == "next": + mov_status_param = 2 + idx += 1 + if words[idx] == "next": + mov_status_param = 1 + idx += 1 + if words[idx] != "set": + raise RuntimeError(f"Invalid {line})") + mov_status_count = int(words[idx + 1]) + + if not 0 <= mov_status_count < 16: + raise RuntimeError(f"Invalid mov_status count {mov_status_count}") + require_version(required_version, line) elif line.endswith(":"): label = line[:-1] if label in labels: @@ -281,6 +309,11 @@ def require_version(required_version, instruction): if FIFO_TYPES.get(fifo_type): self.pio_kwargs["fifo_type"] = fifo_type + if mov_status_type is not None: + self.pio_kwargs["mov_status_type"] = mov_status_type + self.pio_kwargs["mov_status_count"] = mov_status_count + self.pio_kwargs["mov_status_param"] = mov_status_param + self.assembled = array.array("H", assembled) self.debuginfo = (linemap, text_program) if build_debuginfo else None diff --git a/tests/test_version.py b/tests/test_version.py index cf0e63b..d21eb35 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -25,3 +25,39 @@ def test_fifo() -> None: sideset_enable=0, fifo_type="txput", ) + + +def test_mov_status() -> None: + assert_pio_kwargs( + ".mov_status txfifo < 5", + sideset_enable=0, + mov_status_type="txfifo", + mov_status_count=5, + mov_status_param=0, + ) + assert_pio_kwargs( + ".mov_status rxfifo < 8", + sideset_enable=0, + mov_status_type="rxfifo", + mov_status_count=8, + mov_status_param=0, + ) + assert_assembly_fails(".mov_status rxfifo < -1") + assert_assembly_fails(".mov_status rxfifo < 16") + assert_assembly_fails(".mov_status irq next set 3") + assert_pio_kwargs( + ".pio_version 1\n.mov_status irq next set 3", + pio_version=1, + sideset_enable=0, + mov_status_type="irq", + mov_status_count=3, + mov_status_param=2, + ) + assert_pio_kwargs( + ".pio_version 1\n.mov_status irq set 3", + pio_version=1, + sideset_enable=0, + mov_status_type="irq", + mov_status_count=3, + mov_status_param=0, + ) From cfa740077fbdf1a14e4b4e2f1471337c48dda22a Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 5 Sep 2024 09:55:08 -0500 Subject: [PATCH 05/38] Improve line splitting --- adafruit_pioasm.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index aea3030..f5e0ac5 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -72,11 +72,10 @@ def require_version(required_version, instruction): ) for i, line in enumerate(text_program.split("\n")): - line = line.strip() + line = line.split(";")[0].strip() if not line: continue - if ";" in line: - line = line.split(";")[0].strip() + words = line.split() if line.startswith(".program"): if program_name: raise RuntimeError("Multiple programs not supported") @@ -103,7 +102,6 @@ def require_version(required_version, instruction): raise RuntimeError(f"Invalid fifo type {fifo_type}") require_version(required_version, line) elif line.startswith(".mov_status"): - words = line.split() required_version = 0 mov_status_param = 0 mov_status_type = words[1] From 9874ce3f51083f94794e59e351ce27d54e1472be Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 5 Sep 2024 09:56:47 -0500 Subject: [PATCH 06/38] Start to consolidate argument range checking --- adafruit_pioasm.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index f5e0ac5..df53d1a 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -71,6 +71,14 @@ def require_version(required_version, instruction): f"{instruction} requires .pio_version {required_version}" ) + def int_in_range(arg, low, high, what, radix=0): + result = int(arg, radix) + if low <= result < high: + return result + raise RuntimeError( + f"{what} must be at least {low} and no greater than {high}, got {result}" + ) + for i, line in enumerate(text_program.split("\n")): line = line.split(";")[0].strip() if not line: @@ -81,9 +89,7 @@ def require_version(required_version, instruction): raise RuntimeError("Multiple programs not supported") program_name = line.split()[1] elif line.startswith(".pio_version"): - pio_version = int(line.split()[1], 0) - if not 0 <= pio_version <= 1: - raise RuntimeError(f"Invalid .pio_version {pio_version}") + pio_version = int_in_range(words[1], 0, 2, ".pio_version") elif line.startswith(".origin"): offset = int(line.split()[1], 0) elif line.startswith(".wrap_target"): From ff1f57dfb19570461d5d88995d8880e9211ff337 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 5 Sep 2024 13:12:19 -0500 Subject: [PATCH 07/38] Check directives that must come before an instruction --- adafruit_pioasm.py | 8 ++++++++ tests/test_pseudo.py | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index df53d1a..c2d4cab 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -65,6 +65,10 @@ def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: mov_status_count = None mov_status_param = None + def require_before_instruction(): + if len(instructions) != 0: + raise RuntimeError(f"{words[0]} must be before first instruction") + def require_version(required_version, instruction): if pio_version < required_version: raise RuntimeError( @@ -89,8 +93,10 @@ def int_in_range(arg, low, high, what, radix=0): raise RuntimeError("Multiple programs not supported") program_name = line.split()[1] elif line.startswith(".pio_version"): + require_before_instruction() pio_version = int_in_range(words[1], 0, 2, ".pio_version") elif line.startswith(".origin"): + require_before_instruction() offset = int(line.split()[1], 0) elif line.startswith(".wrap_target"): wrap_target = len(instructions) @@ -102,12 +108,14 @@ def int_in_range(arg, low, high, what, radix=0): sideset_count = int(line.split()[1], 0) sideset_enable = "opt" in line elif line.startswith(".fifo"): + require_before_instruction() fifo_type = line.split()[1] required_version = FIFO_TYPES.get(fifo_type) if required_version is None: raise RuntimeError(f"Invalid fifo type {fifo_type}") require_version(required_version, line) elif line.startswith(".mov_status"): + require_before_instruction() required_version = 0 mov_status_param = 0 mov_status_type = words[1] diff --git a/tests/test_pseudo.py b/tests/test_pseudo.py index 66d93c2..1bf89f8 100644 --- a/tests/test_pseudo.py +++ b/tests/test_pseudo.py @@ -6,8 +6,9 @@ Tests pseudo-ops """ -from pytest_helpers import assert_pio_kwargs +from pytest_helpers import assert_pio_kwargs, assert_assembly_fails def test_offset() -> None: assert_pio_kwargs(".origin 7", offset=7, sideset_enable=False) + assert_assembly_fails("nop\n.origin 7") From 4deafa6c6539c589a4950fcbfea5efe34a3ea782 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 5 Sep 2024 13:18:26 -0500 Subject: [PATCH 08/38] Add and test .in directive --- adafruit_pioasm.py | 39 +++++++++++++++++++++++++++++++++++++++ tests/test_version.py | 19 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index c2d4cab..4b2308b 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -64,6 +64,10 @@ def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: mov_status_type = None mov_status_count = None mov_status_param = None + in_count = None + in_shift_right = None + auto_push = None + push_threshold = None def require_before_instruction(): if len(instructions) != 0: @@ -139,6 +143,29 @@ def int_in_range(arg, low, high, what, radix=0): if not 0 <= mov_status_count < 16: raise RuntimeError(f"Invalid mov_status count {mov_status_count}") require_version(required_version, line) + elif words[0] == ".in": + require_before_instruction() + in_count = int_in_range( + words[1], 32 if pio_version == 0 else 1, 33, ".in count" + ) + auto_push = False + + idx = 2 + if idx < len(words) and words[idx] == "left": + in_shift_right = False + idx += 1 + elif idx < len(words) and words[idx] == "right": + in_shift_right = True + idx += 1 + + if idx < len(words) and words[idx] == "auto": + auto_push = True + idx += 1 + + if idx < len(words): + push_threshold = int_in_range(words[idx], 1, 33, ".in threshold") + idx += 1 + elif line.endswith(":"): label = line[:-1] if label in labels: @@ -326,6 +353,18 @@ def int_in_range(arg, low, high, what, radix=0): self.pio_kwargs["mov_status_count"] = mov_status_count self.pio_kwargs["mov_status_param"] = mov_status_param + if in_count not in (None, 32): + self.pio_kwargs["in_count"] = in_count + + if in_shift_right is not None: + self.pio_kwargs["in_shift_right"] = in_shift_right + + if auto_push is not None: + self.pio_kwargs["auto_push"] = auto_push + + if push_threshold is not None: + self.pio_kwargs["push_threshold"] = push_threshold + self.assembled = array.array("H", assembled) self.debuginfo = (linemap, text_program) if build_debuginfo else None diff --git a/tests/test_version.py b/tests/test_version.py index d21eb35..50de635 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -61,3 +61,22 @@ def test_mov_status() -> None: mov_status_count=3, mov_status_param=0, ) + + +def test_dot_in() -> None: + assert_pio_kwargs( + ".in 32 left auto 11", + sideset_enable=0, + auto_push=True, + push_threshold=11, + in_shift_right=False, + ) + assert_assembly_fails(".in 16") + assert_pio_kwargs( + ".pio_version 1\n.in 16 right", + pio_version=1, + sideset_enable=0, + in_count=16, + auto_push=False, + in_shift_right=True, + ) From 5659a2672a775689b60c5bc347a5381da5d4b8c4 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 5 Sep 2024 13:18:49 -0500 Subject: [PATCH 09/38] Improve error handling of origin & mov_status directives --- adafruit_pioasm.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 4b2308b..dc943e6 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -101,7 +101,7 @@ def int_in_range(arg, low, high, what, radix=0): pio_version = int_in_range(words[1], 0, 2, ".pio_version") elif line.startswith(".origin"): require_before_instruction() - offset = int(line.split()[1], 0) + offset = int_in_range(words[1], 0, 32, ".origin") elif line.startswith(".wrap_target"): wrap_target = len(instructions) elif line.startswith(".wrap"): @@ -126,7 +126,7 @@ def int_in_range(arg, low, high, what, radix=0): if words[1] in ("txfifo", "rxfifo"): if words[2] != "<": raise RuntimeError(f"Invalid {line}") - mov_status_count = int(words[3]) + mov_status_count = int_in_range(words[3], 0, 16, words[1]) elif words[1] == "irq": required_version = 1 idx = 2 @@ -139,9 +139,6 @@ def int_in_range(arg, low, high, what, radix=0): if words[idx] != "set": raise RuntimeError(f"Invalid {line})") mov_status_count = int(words[idx + 1]) - - if not 0 <= mov_status_count < 16: - raise RuntimeError(f"Invalid mov_status count {mov_status_count}") require_version(required_version, line) elif words[0] == ".in": require_before_instruction() From ed749474404774134efd71b231d026d389e58f0c Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 5 Sep 2024 13:46:47 -0500 Subject: [PATCH 10/38] Add .out directive --- adafruit_pioasm.py | 39 +++++++++++++++++++++++++++++++++++++++ tests/test_version.py | 19 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index dc943e6..6458c4c 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -68,6 +68,10 @@ def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: in_shift_right = None auto_push = None push_threshold = None + out_count = None + out_shift_right = None + auto_pull = None + pull_threshold = None def require_before_instruction(): if len(instructions) != 0: @@ -140,6 +144,29 @@ def int_in_range(arg, low, high, what, radix=0): raise RuntimeError(f"Invalid {line})") mov_status_count = int(words[idx + 1]) require_version(required_version, line) + elif words[0] == ".out": + require_before_instruction() + out_count = int_in_range( + words[1], 32 if pio_version == 0 else 1, 33, ".out count" + ) + auto_pull = False + + idx = 2 + if idx < len(words) and words[idx] == "left": + out_shift_right = False + idx += 1 + elif idx < len(words) and words[idx] == "right": + out_shift_right = True + idx += 1 + + if idx < len(words) and words[idx] == "auto": + auto_pull = True + idx += 1 + + if idx < len(words): + pull_threshold = int_in_range(words[idx], 1, 33, ".out threshold") + idx += 1 + elif words[0] == ".in": require_before_instruction() in_count = int_in_range( @@ -350,6 +377,18 @@ def int_in_range(arg, low, high, what, radix=0): self.pio_kwargs["mov_status_count"] = mov_status_count self.pio_kwargs["mov_status_param"] = mov_status_param + if out_count not in (None, 32): + self.pio_kwargs["out_count"] = out_count + + if out_shift_right is not None: + self.pio_kwargs["out_shift_right"] = out_shift_right + + if auto_pull is not None: + self.pio_kwargs["auto_pull"] = auto_pull + + if pull_threshold is not None: + self.pio_kwargs["pull_threshold"] = pull_threshold + if in_count not in (None, 32): self.pio_kwargs["in_count"] = in_count diff --git a/tests/test_version.py b/tests/test_version.py index 50de635..9d31388 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -80,3 +80,22 @@ def test_dot_in() -> None: auto_push=False, in_shift_right=True, ) + + +def test_dot_out() -> None: + assert_pio_kwargs( + ".out 32 left auto 11", + sideset_enable=0, + auto_pull=True, + pull_threshold=11, + out_shift_right=False, + ) + assert_assembly_fails(".out 16") + assert_pio_kwargs( + ".pio_version 1\n.out 16 right", + pio_version=1, + sideset_enable=0, + out_count=16, + auto_pull=False, + out_shift_right=True, + ) From e9858c36667a75da8c225cab0d1de542946a86dc Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 5 Sep 2024 13:50:19 -0500 Subject: [PATCH 11/38] Add .set --- adafruit_pioasm.py | 10 ++++++++++ tests/test_version.py | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 6458c4c..8ec06d7 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -72,6 +72,7 @@ def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: out_shift_right = None auto_pull = None pull_threshold = None + set_count = None def require_before_instruction(): if len(instructions) != 0: @@ -190,6 +191,12 @@ def int_in_range(arg, low, high, what, radix=0): push_threshold = int_in_range(words[idx], 1, 33, ".in threshold") idx += 1 + elif words[0] == ".set": + require_before_instruction() + set_count = int_in_range( + words[1], 32 if pio_version == 0 else 1, 33, ".set count" + ) + elif line.endswith(":"): label = line[:-1] if label in labels: @@ -377,6 +384,9 @@ def int_in_range(arg, low, high, what, radix=0): self.pio_kwargs["mov_status_count"] = mov_status_count self.pio_kwargs["mov_status_param"] = mov_status_param + if set_count not in (None, 32): + self.pio_kwargs["set_count"] = set_count + if out_count not in (None, 32): self.pio_kwargs["out_count"] = out_count diff --git a/tests/test_version.py b/tests/test_version.py index 9d31388..8f69f18 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -99,3 +99,11 @@ def test_dot_out() -> None: auto_pull=False, out_shift_right=True, ) + + +def test_dot_set() -> None: + assert_pio_kwargs(".set 32", sideset_enable=0) + assert_assembly_fails(".set 16") + assert_pio_kwargs( + ".pio_version 1\n.set 16 right", pio_version=1, sideset_enable=0, set_count=16 + ) From 4f98480aebf70c02b58ce7ab731845c4d9318569 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 5 Sep 2024 19:54:52 -0500 Subject: [PATCH 12/38] Add irq prev/next --- adafruit_pioasm.py | 32 +++++++++++++++++++++++--------- tests/test_version.py | 8 +++++++- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 8ec06d7..04fb93c 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -325,19 +325,33 @@ def int_in_range(arg, low, high, what, radix=0): if len(instruction) > 3: assembled[-1] |= MOV_OPS.index(instruction[-2]) << 3 elif instruction[0] == "irq": - # instr delay z c w index + # instr delay z c w tp/idx assembled.append(0b110_00000_0_0_0_00000) - if instruction[-1] == "rel": - assembled[-1] |= 0x10 # Set the high bit of the irq value + + irq_type = 0 + print(f"check prev/next/rel {instruction=}") + if instruction[-1] == "prev": + irq_type = 1 + require_version(1, "irq prev") + instruction.pop() + elif instruction[-1] == "next": + irq_type = 3 + require_version(1, "irq next") + instruction.pop() + elif instruction[-1] == "rel": + irq_type = 2 instruction.pop() - num = int(instruction[-1], 0) - if not 0 <= num <= 7: - raise RuntimeError("Interrupt index out of range") + + assembled[-1] |= irq_type << 3 + + num = int_in_range(instruction[-1], 0, 8, "irq index") assembled[-1] |= num - if len(instruction) == 3: # after rel has been removed - if instruction[1] == "wait": + instruction.pop() + + if len(instruction) > 1: # after rel has been removed + if instruction[-1] == "wait": assembled[-1] |= 0x20 - elif instruction[1] == "clear": + elif instruction[-1] == "clear": assembled[-1] |= 0x40 # All other values are the default of set without waiting elif instruction[0] == "set": diff --git a/tests/test_version.py b/tests/test_version.py index 8f69f18..0c8daaf 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -6,7 +6,7 @@ Tests version dependent instructions """ -from pytest_helpers import assert_pio_kwargs, assert_assembly_fails +from pytest_helpers import assert_pio_kwargs, assert_assembly_fails, assert_assembles_to def test_version() -> None: @@ -107,3 +107,9 @@ def test_dot_set() -> None: assert_pio_kwargs( ".pio_version 1\n.set 16 right", pio_version=1, sideset_enable=0, set_count=16 ) + + +def test_irq_v1() -> None: + assert_assembly_fails("irq 7 next") + assert_assembles_to(".pio_version 1\nirq 5 next", [0b110_00000_0_0_0_11_101]) + assert_assembles_to(".pio_version 1\nirq wait 1 prev", [0b110_00000_0_0_1_01_001]) From 895f8eb056dd25d5b8feadccd8453978c17f3d52 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sat, 7 Sep 2024 08:43:56 -0500 Subject: [PATCH 13/38] add mov rxfifo --- adafruit_pioasm.py | 68 ++++++++++++++++++++++++++++++------------- tests/test_version.py | 9 ++++++ 2 files changed, 56 insertions(+), 21 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 04fb93c..3c6c446 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -92,6 +92,18 @@ def int_in_range(arg, low, high, what, radix=0): f"{what} must be at least {low} and no greater than {high}, got {result}" ) + def parse_rxfifo_brackets(arg, fifo_dir): + require_version(1, line) + if ( # pylint: disable=consider-using-in + fifo_type != "putget" and fifo_type != fifo_dir + ): + raise RuntimeError( + f"FIFO must be configured for '{fifo_dir}' or 'putget' for {line}" + ) + if arg.endswith("[y]"): + return 0b1000 + return int_in_range(arg[7:-1], 0, 8, "rxfifo index") + for i, line in enumerate(text_program.split("\n")): line = line.split(";")[0].strip() if not line: @@ -212,7 +224,11 @@ def int_in_range(arg, low, high, what, radix=0): for line in instructions: instruction = splitter(line.strip()) delay = 0 - if len(instruction) > 1 and instruction[-1].endswith("]"): # Delay + if ( + len(instruction) > 1 + and instruction[-1].startswith("[") + and instruction[-1].endswith("]") + ): # Delay delay = int(instruction[-1].strip("[]"), 0) if delay < 0: raise RuntimeError("Delay negative:", delay) @@ -303,33 +319,43 @@ def int_in_range(arg, low, high, what, radix=0): assembled[-1] |= 0x40 elif instruction[0] == "mov": # instr delay dst op src - assembled.append(0b101_00000_000_00_000) - assembled[-1] |= MOV_DESTINATIONS.index(instruction[1]) << 5 - source = instruction[-1] - source_split = mov_splitter(source) - if len(source_split) == 1: - try: - assembled[-1] |= MOV_SOURCES.index(source) - except ValueError as exc: - raise ValueError(f"Invalid mov source '{source}'") from exc + if instruction[1].startswith("rxfifo["): # mov rxfifo[], isr + assembled.append(0b100_00000_0001_0_000) + if instruction[2] != "isr": + raise ValueError("mov rxfifo[] source must be isr") + assembled[-1] |= parse_rxfifo_brackets(instruction[1], "put") + elif instruction[2].startswith("rxfifo["): # mov osr, rxfifo[] + assembled.append(0b100_00000_1001_0_000) + if instruction[1] != "osr": + raise ValueError("mov ,rxfifo[] target must be osr") + assembled[-1] |= parse_rxfifo_brackets(instruction[2], "get") else: - assembled[-1] |= MOV_SOURCES.index(source_split[1]) - if source[:1] == "!": - assembled[-1] |= 0x08 - elif source[:1] == "~": - assembled[-1] |= 0x08 - elif source[:2] == "::": - assembled[-1] |= 0x10 + assembled.append(0b101_00000_000_00_000) + assembled[-1] |= MOV_DESTINATIONS.index(instruction[1]) << 5 + source = instruction[-1] + source_split = mov_splitter(source) + if len(source_split) == 1: + try: + assembled[-1] |= MOV_SOURCES.index(source) + except ValueError as exc: + raise ValueError(f"Invalid mov source '{source}'") from exc else: - raise RuntimeError("Invalid mov operator:", source[:1]) - if len(instruction) > 3: - assembled[-1] |= MOV_OPS.index(instruction[-2]) << 3 + assembled[-1] |= MOV_SOURCES.index(source_split[1]) + if source[:1] == "!": + assembled[-1] |= 0x08 + elif source[:1] == "~": + assembled[-1] |= 0x08 + elif source[:2] == "::": + assembled[-1] |= 0x10 + else: + raise RuntimeError("Invalid mov operator:", source[:1]) + if len(instruction) > 3: + assembled[-1] |= MOV_OPS.index(instruction[-2]) << 3 elif instruction[0] == "irq": # instr delay z c w tp/idx assembled.append(0b110_00000_0_0_0_00000) irq_type = 0 - print(f"check prev/next/rel {instruction=}") if instruction[-1] == "prev": irq_type = 1 require_version(1, "irq prev") diff --git a/tests/test_version.py b/tests/test_version.py index 0c8daaf..ff5840a 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -113,3 +113,12 @@ def test_irq_v1() -> None: assert_assembly_fails("irq 7 next") assert_assembles_to(".pio_version 1\nirq 5 next", [0b110_00000_0_0_0_11_101]) assert_assembles_to(".pio_version 1\nirq wait 1 prev", [0b110_00000_0_0_1_01_001]) + + +def test_mov_v1() -> None: + assert_assembly_fails("mov osr, rxfifo[y]") + assert_assembly_fails(".pio_version 1\nmov osr, rxfifo[y]") + prefix = ".pio_version 1\n.fifo putget\n" + assert_assembly_fails(prefix + "mov osr, rxfifo[8]") + assert_assembles_to(prefix + "mov rxfifo[y], isr", [0b100_00000_0001_1_000]) + assert_assembles_to(prefix + "mov osr, rxfifo[1]", [0b100_00000_1001_0_001]) From 7acebc63b0a641b8470795aa359f2f3dd0814836 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sat, 7 Sep 2024 08:50:25 -0500 Subject: [PATCH 14/38] Add mov pindirs --- adafruit_pioasm.py | 10 ++++++++-- tests/test_version.py | 3 +++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 3c6c446..8662e04 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -29,7 +29,8 @@ IN_SOURCES = ["pins", "x", "y", "null", None, None, "isr", "osr"] OUT_DESTINATIONS = ["pins", "x", "y", "null", "pindirs", "pc", "isr", "exec"] WAIT_SOURCES = ["gpio", "pin", "irq", None] -MOV_DESTINATIONS = ["pins", "x", "y", None, "exec", "pc", "isr", "osr"] +MOV_DESTINATIONS_V0 = ["pins", "x", "y", None, "exec", "pc", "isr", "osr"] +MOV_DESTINATIONS_V1 = ["pins", "x", "y", "pindirs", "exec", "pc", "isr", "osr"] MOV_SOURCES = ["pins", "x", "y", "null", None, "status", "isr", "osr"] MOV_OPS = [None, "~", "::", None] SET_DESTINATIONS = ["pins", "x", "y", None, "pindirs", None, None, None] @@ -219,6 +220,11 @@ def parse_rxfifo_brackets(arg, fifo_dir): instructions.append(line) linemap.append(i) + if pio_version >= 1: + mov_destinations = MOV_DESTINATIONS_V1 + else: + mov_destinations = MOV_DESTINATIONS_V0 + max_delay = 2 ** (5 - sideset_count - sideset_enable) - 1 assembled = [] for line in instructions: @@ -331,7 +337,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): assembled[-1] |= parse_rxfifo_brackets(instruction[2], "get") else: assembled.append(0b101_00000_000_00_000) - assembled[-1] |= MOV_DESTINATIONS.index(instruction[1]) << 5 + assembled[-1] |= mov_destinations.index(instruction[1]) << 5 source = instruction[-1] source_split = mov_splitter(source) if len(source_split) == 1: diff --git a/tests/test_version.py b/tests/test_version.py index ff5840a..2999caf 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -122,3 +122,6 @@ def test_mov_v1() -> None: assert_assembly_fails(prefix + "mov osr, rxfifo[8]") assert_assembles_to(prefix + "mov rxfifo[y], isr", [0b100_00000_0001_1_000]) assert_assembles_to(prefix + "mov osr, rxfifo[1]", [0b100_00000_1001_0_001]) + + assert_assembly_fails("mov pindirs, null", errtype=ValueError) + assert_assembles_to(prefix + "mov pindirs, null", [0b101_00000_01100011]) From 86f02e8b8389469b6ca3f6b375d1c1e0605250ee Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sun, 8 Sep 2024 08:01:09 -0500 Subject: [PATCH 15/38] Add wait jmppin & wait irq prev/next --- adafruit_pioasm.py | 34 +++++++++++++++++++++++++++------- tests/test_version.py | 13 +++++++++++++ 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 8662e04..1ddcb1b 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -278,16 +278,36 @@ def parse_rxfifo_brackets(arg, fifo_dir): # instr delay p sr index assembled.append(0b001_00000_0_00_00000) polarity = int(instruction[1], 0) + source = instruction[2] if not 0 <= polarity <= 1: raise RuntimeError("Invalid polarity") assembled[-1] |= polarity << 7 - assembled[-1] |= WAIT_SOURCES.index(instruction[2]) << 5 - num = int(instruction[3], 0) - if not 0 <= num <= 31: - raise RuntimeError("Wait num out of range") - assembled[-1] |= num - if instruction[-1] == "rel": - assembled[-1] |= 0x10 # Set the high bit of the irq value + if instruction[2] == "jmppin": + require_version(1, "wait jmppin") + num = 0 + print("wait jmppin", instruction) + if len(instruction) > 3: + if len(instruction) < 5 or instruction[3] != "+": + raise RuntimeError("invalid wait jmppin") + num = int_in_range(instruction[4], 0, 4, "wait jmppin offset") + assembled[-1] |= num + assembled[-1] |= 0b11 << 5 # JMPPIN wait source + else: + assembled[-1] |= WAIT_SOURCES.index(instruction[2]) << 5 + num = int(instruction[3], 0) + if not 0 <= num <= 31: + raise RuntimeError("Wait num out of range") + assembled[-1] |= num + # The flag index is decoded in the same way as the IRQ + # index field, decoding down from the two MSBs + if instruction[-1] == "next": + require_version(1, "wait irq next") + assembled[-1] |= 0b11000 + elif instruction[-1] == "prev": + require_version(1, "wait irq prev") + assembled[-1] |= 0b01000 + elif instruction[-1] == "rel": + assembled[-1] |= 0b10000 elif instruction[0] == "in": # instr delay src count assembled.append(0b010_00000_000_00000) diff --git a/tests/test_version.py b/tests/test_version.py index 2999caf..380d5aa 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -125,3 +125,16 @@ def test_mov_v1() -> None: assert_assembly_fails("mov pindirs, null", errtype=ValueError) assert_assembles_to(prefix + "mov pindirs, null", [0b101_00000_01100011]) + + +def test_wait_v1() -> None: + assert_assembly_fails("wait 0 jmppin") + assert_assembly_fails("wait 0 irq 5 next") + prefix = ".pio_version 1\n" + assert_assembly_fails(prefix + "wait 0 jmppin +") + assert_assembly_fails(prefix + "wait 0 jmppin + 7") + assert_assembles_to(prefix + "wait 0 jmppin + 3", [0b001_00000_0_11_00011]) + assert_assembles_to(prefix + "wait 1 jmppin", [0b001_00000_1_11_00000]) + + assert_assembles_to(prefix + "wait 0 irq 5 next", [0b001_00000_0_10_11_101]) + assert_assembles_to(prefix + "wait 1 irq 4 prev", [0b001_00000_1_10_01_100]) From a9f20eabe113c538c61789ba4b9fd8f6379b3478 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sun, 8 Sep 2024 08:18:38 -0500 Subject: [PATCH 16/38] pylint allow --- examples/pioasm_i2s_codec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pioasm_i2s_codec.py b/examples/pioasm_i2s_codec.py index d9cd74b..4fda44c 100644 --- a/examples/pioasm_i2s_codec.py +++ b/examples/pioasm_i2s_codec.py @@ -86,7 +86,7 @@ # read: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 -def i2s_codec( +def i2s_codec( # pylint: disable=too-many-arguments channels=2, sample_rate=48000, bits=16, From b379f05b29b310e1cece677847c131c5f08f022c Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 10 Sep 2024 11:20:48 -0500 Subject: [PATCH 17/38] Merge mov_status_count & _param into mov_status_n .. and correct .mov_status irq prev and range handling --- adafruit_pioasm.py | 20 ++++++++++---------- tests/test_version.py | 22 +++++++++++++--------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 1ddcb1b..3b83347 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -63,8 +63,7 @@ def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: pio_version = 0 fifo_type = None mov_status_type = None - mov_status_count = None - mov_status_param = None + mov_status_n = None in_count = None in_shift_right = None auto_push = None @@ -139,24 +138,26 @@ def parse_rxfifo_brackets(arg, fifo_dir): elif line.startswith(".mov_status"): require_before_instruction() required_version = 0 - mov_status_param = 0 + mov_status_n = 0 mov_status_type = words[1] if words[1] in ("txfifo", "rxfifo"): if words[2] != "<": raise RuntimeError(f"Invalid {line}") - mov_status_count = int_in_range(words[3], 0, 16, words[1]) + mov_status_n = int_in_range(words[3], 0, 32, words[1]) elif words[1] == "irq": required_version = 1 idx = 2 if words[idx] == "next": - mov_status_param = 2 + mov_status_n = 0x10 idx += 1 - if words[idx] == "next": - mov_status_param = 1 + elif words[idx] == "prev": + mov_status_n = 0x8 idx += 1 + else: + mov_status_n = 0 if words[idx] != "set": raise RuntimeError(f"Invalid {line})") - mov_status_count = int(words[idx + 1]) + mov_status_n |= int_in_range(words[idx + 1], 0, 8, "mov_status irq") require_version(required_version, line) elif words[0] == ".out": require_before_instruction() @@ -447,8 +448,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): if mov_status_type is not None: self.pio_kwargs["mov_status_type"] = mov_status_type - self.pio_kwargs["mov_status_count"] = mov_status_count - self.pio_kwargs["mov_status_param"] = mov_status_param + self.pio_kwargs["mov_status_n"] = mov_status_n if set_count not in (None, 32): self.pio_kwargs["set_count"] = set_count diff --git a/tests/test_version.py b/tests/test_version.py index 380d5aa..cf015a6 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -32,35 +32,39 @@ def test_mov_status() -> None: ".mov_status txfifo < 5", sideset_enable=0, mov_status_type="txfifo", - mov_status_count=5, - mov_status_param=0, + mov_status_n=5, ) assert_pio_kwargs( ".mov_status rxfifo < 8", sideset_enable=0, mov_status_type="rxfifo", - mov_status_count=8, - mov_status_param=0, + mov_status_n=8, ) assert_assembly_fails(".mov_status rxfifo < -1") - assert_assembly_fails(".mov_status rxfifo < 16") + assert_assembly_fails(".mov_status rxfifo < 33") assert_assembly_fails(".mov_status irq next set 3") + assert_pio_kwargs( + ".pio_version 1\n.mov_status irq prev set 3", + pio_version=1, + sideset_enable=0, + mov_status_type="irq", + mov_status_n=3 | 0x8, + ) assert_pio_kwargs( ".pio_version 1\n.mov_status irq next set 3", pio_version=1, sideset_enable=0, mov_status_type="irq", - mov_status_count=3, - mov_status_param=2, + mov_status_n=3 | 0x10, ) assert_pio_kwargs( ".pio_version 1\n.mov_status irq set 3", pio_version=1, sideset_enable=0, mov_status_type="irq", - mov_status_count=3, - mov_status_param=0, + mov_status_n=3, ) + assert_assembly_fails(".pio_version 1\n.mov_status irq prev set 9") def test_dot_in() -> None: From 8a97f71b0c646dae792ac27798d26c3303565c54 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 10 Sep 2024 14:09:34 -0500 Subject: [PATCH 18/38] More piov2 updates * As an extension, ".fifo auto" may be specified to request CircuitPython's auto fifo join behavior * bounds check on `.set` directive improved * redundant kwargs (e.g., out_count vs out_pin_count) fixed --- README.rst | 5 +++++ adafruit_pioasm.py | 24 ++++++++++++++++-------- tests/test_version.py | 13 ++++++++----- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/README.rst b/README.rst index b7ae53a..091c113 100644 --- a/README.rst +++ b/README.rst @@ -55,6 +55,11 @@ To install in a virtual environment in your current project: source .venv/bin/activate pip3 install adafruit-circuitpython-pioasm +CircuitPython Extensions +======================== + +* `.fifo auto`: By default, CircuitPython joins the TX and RX fifos if a PIO program only receives or transmits. The `.fifo auto` directive makes this explicit. + Usage Example ============= diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 3b83347..b559dd3 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -34,7 +34,15 @@ MOV_SOURCES = ["pins", "x", "y", "null", None, "status", "isr", "osr"] MOV_OPS = [None, "~", "::", None] SET_DESTINATIONS = ["pins", "x", "y", None, "pindirs", None, None, None] -FIFO_TYPES = {"txrx": 0, "tx": 0, "rx": 0, "txput": 1, "txget": 1, "putget": 1} +FIFO_TYPES = { + "auto": 0, + "txrx": 0, + "tx": 0, + "rx": 0, + "txput": 1, + "txget": 1, + "putget": 1, +} class Program: # pylint: disable=too-few-public-methods @@ -61,7 +69,7 @@ def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: wrap_target = None offset = -1 pio_version = 0 - fifo_type = None + fifo_type = "auto" mov_status_type = None mov_status_n = None in_count = None @@ -208,7 +216,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): elif words[0] == ".set": require_before_instruction() set_count = int_in_range( - words[1], 32 if pio_version == 0 else 1, 33, ".set count" + words[1], 5 if pio_version == 0 else 1, 6, ".set count" ) elif line.endswith(":"): @@ -443,18 +451,18 @@ def parse_rxfifo_brackets(arg, fifo_dir): if wrap_target is not None: self.pio_kwargs["wrap_target"] = wrap_target - if FIFO_TYPES.get(fifo_type): + if fifo_type != "auto": self.pio_kwargs["fifo_type"] = fifo_type if mov_status_type is not None: self.pio_kwargs["mov_status_type"] = mov_status_type self.pio_kwargs["mov_status_n"] = mov_status_n - if set_count not in (None, 32): - self.pio_kwargs["set_count"] = set_count + if set_count is not None: + self.pio_kwargs["set_pin_count"] = set_count if out_count not in (None, 32): - self.pio_kwargs["out_count"] = out_count + self.pio_kwargs["out_pin_count"] = out_count if out_shift_right is not None: self.pio_kwargs["out_shift_right"] = out_shift_right @@ -466,7 +474,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): self.pio_kwargs["pull_threshold"] = pull_threshold if in_count not in (None, 32): - self.pio_kwargs["in_count"] = in_count + self.pio_kwargs["in_pin_count"] = in_count if in_shift_right is not None: self.pio_kwargs["in_shift_right"] = in_shift_right diff --git a/tests/test_version.py b/tests/test_version.py index cf015a6..d7280b3 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -17,7 +17,8 @@ def test_version() -> None: def test_fifo() -> None: - assert_pio_kwargs(".fifo txrx", sideset_enable=0) + assert_pio_kwargs(".fifo txrx", sideset_enable=0, fifo_type="txrx") + assert_pio_kwargs(".fifo auto", sideset_enable=0) assert_assembly_fails(".fifo txput") assert_pio_kwargs( ".pio_version 1\n.fifo txput", @@ -80,7 +81,7 @@ def test_dot_in() -> None: ".pio_version 1\n.in 16 right", pio_version=1, sideset_enable=0, - in_count=16, + in_pin_count=16, auto_push=False, in_shift_right=True, ) @@ -99,17 +100,19 @@ def test_dot_out() -> None: ".pio_version 1\n.out 16 right", pio_version=1, sideset_enable=0, - out_count=16, + out_pin_count=16, auto_pull=False, out_shift_right=True, ) def test_dot_set() -> None: - assert_pio_kwargs(".set 32", sideset_enable=0) + assert_pio_kwargs(".set 5", sideset_enable=0, set_pin_count=5) assert_assembly_fails(".set 16") + assert_assembly_fails(".pio_version 1\n.set 16") + assert_assembly_fails(".set 3") assert_pio_kwargs( - ".pio_version 1\n.set 16 right", pio_version=1, sideset_enable=0, set_count=16 + ".pio_version 1\n.set 3 right", pio_version=1, sideset_enable=0, set_pin_count=3 ) From eaa8bb0dc744fb8dfe830db72ce9633344a82c00 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 10 Sep 2024 14:16:25 -0500 Subject: [PATCH 19/38] fix markup --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 091c113..67e7577 100644 --- a/README.rst +++ b/README.rst @@ -58,7 +58,7 @@ To install in a virtual environment in your current project: CircuitPython Extensions ======================== -* `.fifo auto`: By default, CircuitPython joins the TX and RX fifos if a PIO program only receives or transmits. The `.fifo auto` directive makes this explicit. +* ``.fifo auto``: By default, CircuitPython joins the TX and RX fifos if a PIO program only receives or transmits. The ``.fifo auto`` directive makes this explicit. Usage Example ============= From 919d075f342d0b8aefddf772fa38694bdf526338 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 13 Sep 2024 09:55:26 -0500 Subject: [PATCH 20/38] Fix checking fifo mode for mov rxfifo instructions --- adafruit_pioasm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index b559dd3..094504e 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -358,12 +358,12 @@ def parse_rxfifo_brackets(arg, fifo_dir): assembled.append(0b100_00000_0001_0_000) if instruction[2] != "isr": raise ValueError("mov rxfifo[] source must be isr") - assembled[-1] |= parse_rxfifo_brackets(instruction[1], "put") + assembled[-1] |= parse_rxfifo_brackets(instruction[1], "txput") elif instruction[2].startswith("rxfifo["): # mov osr, rxfifo[] assembled.append(0b100_00000_1001_0_000) if instruction[1] != "osr": raise ValueError("mov ,rxfifo[] target must be osr") - assembled[-1] |= parse_rxfifo_brackets(instruction[2], "get") + assembled[-1] |= parse_rxfifo_brackets(instruction[2], "txget") else: assembled.append(0b101_00000_000_00_000) assembled[-1] |= mov_destinations.index(instruction[1]) << 5 From 3159513f9b7de15b6e22727428d72a6d65f065ca Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 13 Sep 2024 09:56:57 -0500 Subject: [PATCH 21/38] Add an example using the RP2350 "mov ,txfifo[]" feature --- examples/pioasm_rp2350_fifo.py | 84 ++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 examples/pioasm_rp2350_fifo.py diff --git a/examples/pioasm_rp2350_fifo.py b/examples/pioasm_rp2350_fifo.py new file mode 100644 index 0000000..f6dda86 --- /dev/null +++ b/examples/pioasm_rp2350_fifo.py @@ -0,0 +1,84 @@ +# SPDX-FileCopyrightText: 2024 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +"""A PIO demo that uses the FIFO in random access mode + +Random access mode is a new feature of the PIO peripheral of the RP2350. +This demo is not compatible with the original RP2040 or Raspberry Pi +Pico. + +Wiring: + * LED with current limiting resistor on GP25 (Pico 2 standard location) + +The LED will blink in several patterns depending on the values loaded in the 'rxfifo' registers +""" + +import array +import time +import board +import rp2pio +import adafruit_pioasm + +program = adafruit_pioasm.Program( + """ + .pio_version 1 + .set 1 + .fifo txget + + ; LED on time taken from rxfifo[0] + mov osr, rxfifo[0] + mov x, osr + + set pins, 1 +xloop1: + jmp x--, xloop1 + + ; LED off time taken from rxfifo[1] + mov osr, rxfifo[1] + mov x, osr + + set pins, 0 +xloop2: + jmp x--, xloop2 + """ +) + + +def assign_uint32s(ar, off, *args): + """Assign multiple 32-bit registers within an AddressRange""" + vv = b"".join(v.to_bytes(4, "little") for v in args) + ar[off : off + 4 * len(args)] = vv + + +print(program.pio_kwargs) +sm = rp2pio.StateMachine( + program.assembled, + first_set_pin=board.GP25, + frequency=10_000_000, + **program.pio_kwargs, +) +fifo = sm.rxfifo + +# Set non-zero register entries & re-start the state machine at its offset. +# this is needed because the default register value could represent a very long delay +fifo[0:4] = b"\1\0\0\0" +fifo[4:8] = b"\1\0\0\0" +sm.run(array.array("H", [sm.offset])) + +while True: + # equal blinks + assign_uint32s(fifo, 0, 2000000, 2000000) + time.sleep(1) + + # small on time + assign_uint32s(fifo, 0, 1000000, 3000000) + time.sleep(1) + + # small off time + assign_uint32s(fifo, 0, 3000000, 1000000) + time.sleep(1) + + # slower blinks + assign_uint32s(fifo, 0, 3000000, 3000000) + time.sleep(1) From bdc9fe8277de78758e204c40bc4d6d50318a389a Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 13 Sep 2024 14:44:11 -0500 Subject: [PATCH 22/38] Update based on review comments --- adafruit_pioasm.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 094504e..671a559 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -97,7 +97,7 @@ def int_in_range(arg, low, high, what, radix=0): if low <= result < high: return result raise RuntimeError( - f"{what} must be at least {low} and no greater than {high}, got {result}" + f"{what} must be at least {low} and less than {high}, got {result}" ) def parse_rxfifo_brackets(arg, fifo_dir): @@ -169,9 +169,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): require_version(required_version, line) elif words[0] == ".out": require_before_instruction() - out_count = int_in_range( - words[1], 32 if pio_version == 0 else 1, 33, ".out count" - ) + out_count = int_in_range(words[1], 1, 33, ".out count") auto_pull = False idx = 2 From 403da520a6528e7b3552ec4e96808113df32828a Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 13 Sep 2024 14:49:58 -0500 Subject: [PATCH 23/38] Adjust condition in which ".out 16" is accepted syntax this feature is in all PIO versions --- tests/test_version.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_version.py b/tests/test_version.py index d7280b3..c12711a 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -95,10 +95,8 @@ def test_dot_out() -> None: pull_threshold=11, out_shift_right=False, ) - assert_assembly_fails(".out 16") assert_pio_kwargs( - ".pio_version 1\n.out 16 right", - pio_version=1, + ".out 16 right", sideset_enable=0, out_pin_count=16, auto_pull=False, From 87bf9a78305b2e453c594ab39222b47689c6603a Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 13 Sep 2024 14:51:06 -0500 Subject: [PATCH 24/38] remove a debug print --- adafruit_pioasm.py | 1 - 1 file changed, 1 deletion(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 671a559..4c5e8b7 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -292,7 +292,6 @@ def parse_rxfifo_brackets(arg, fifo_dir): if instruction[2] == "jmppin": require_version(1, "wait jmppin") num = 0 - print("wait jmppin", instruction) if len(instruction) > 3: if len(instruction) < 5 or instruction[3] != "+": raise RuntimeError("invalid wait jmppin") From 89fc1a125a4afd280ad62fe61cdb2f21b2f95231 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 13 Sep 2024 16:01:53 -0500 Subject: [PATCH 25/38] Add (failing) test comparing "all" instructions to sdk pioasm the failing tests will be addressed next. They fall into two simple classes, both pertaining to the new piov1 instructions. --- tests/all_pio_instructions.py | 1240 +++++++++++++++++++++++++++++++++ tests/make_all.py | 129 ++++ tests/test_all.py | 31 + 3 files changed, 1400 insertions(+) create mode 100644 tests/all_pio_instructions.py create mode 100644 tests/make_all.py create mode 100644 tests/test_all.py diff --git a/tests/all_pio_instructions.py b/tests/all_pio_instructions.py new file mode 100644 index 0000000..bd341ca --- /dev/null +++ b/tests/all_pio_instructions.py @@ -0,0 +1,1240 @@ +# SPDX-FileCopyrightText: 2024 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT +# pylint: disable=too-many-lines +# fmt: off + +all_instruction = { + 0: 'jmp 0', + 32: 'jmp !x 0', + 64: 'jmp x-- 0', + 96: 'jmp !y 0', + 128: 'jmp y-- 0', + 160: 'jmp x!=y 0', + 192: 'jmp pin 0', + 224: 'jmp !osre 0', + 1: 'jmp 1', + 33: 'jmp !x 1', + 65: 'jmp x-- 1', + 97: 'jmp !y 1', + 129: 'jmp y-- 1', + 161: 'jmp x!=y 1', + 193: 'jmp pin 1', + 225: 'jmp !osre 1', + 2: 'jmp 2', + 34: 'jmp !x 2', + 66: 'jmp x-- 2', + 98: 'jmp !y 2', + 130: 'jmp y-- 2', + 162: 'jmp x!=y 2', + 194: 'jmp pin 2', + 226: 'jmp !osre 2', + 3: 'jmp 3', + 35: 'jmp !x 3', + 67: 'jmp x-- 3', + 99: 'jmp !y 3', + 131: 'jmp y-- 3', + 163: 'jmp x!=y 3', + 195: 'jmp pin 3', + 227: 'jmp !osre 3', + 4: 'jmp 4', + 36: 'jmp !x 4', + 68: 'jmp x-- 4', + 100: 'jmp !y 4', + 132: 'jmp y-- 4', + 164: 'jmp x!=y 4', + 196: 'jmp pin 4', + 228: 'jmp !osre 4', + 5: 'jmp 5', + 37: 'jmp !x 5', + 69: 'jmp x-- 5', + 101: 'jmp !y 5', + 133: 'jmp y-- 5', + 165: 'jmp x!=y 5', + 197: 'jmp pin 5', + 229: 'jmp !osre 5', + 6: 'jmp 6', + 38: 'jmp !x 6', + 70: 'jmp x-- 6', + 102: 'jmp !y 6', + 134: 'jmp y-- 6', + 166: 'jmp x!=y 6', + 198: 'jmp pin 6', + 230: 'jmp !osre 6', + 7: 'jmp 7', + 39: 'jmp !x 7', + 71: 'jmp x-- 7', + 103: 'jmp !y 7', + 135: 'jmp y-- 7', + 167: 'jmp x!=y 7', + 199: 'jmp pin 7', + 231: 'jmp !osre 7', + 8: 'jmp 8', + 40: 'jmp !x 8', + 72: 'jmp x-- 8', + 104: 'jmp !y 8', + 136: 'jmp y-- 8', + 168: 'jmp x!=y 8', + 200: 'jmp pin 8', + 232: 'jmp !osre 8', + 9: 'jmp 9', + 41: 'jmp !x 9', + 73: 'jmp x-- 9', + 105: 'jmp !y 9', + 137: 'jmp y-- 9', + 169: 'jmp x!=y 9', + 201: 'jmp pin 9', + 233: 'jmp !osre 9', + 10: 'jmp 10', + 42: 'jmp !x 10', + 74: 'jmp x-- 10', + 106: 'jmp !y 10', + 138: 'jmp y-- 10', + 170: 'jmp x!=y 10', + 202: 'jmp pin 10', + 234: 'jmp !osre 10', + 11: 'jmp 11', + 43: 'jmp !x 11', + 75: 'jmp x-- 11', + 107: 'jmp !y 11', + 139: 'jmp y-- 11', + 171: 'jmp x!=y 11', + 203: 'jmp pin 11', + 235: 'jmp !osre 11', + 12: 'jmp 12', + 44: 'jmp !x 12', + 76: 'jmp x-- 12', + 108: 'jmp !y 12', + 140: 'jmp y-- 12', + 172: 'jmp x!=y 12', + 204: 'jmp pin 12', + 236: 'jmp !osre 12', + 13: 'jmp 13', + 45: 'jmp !x 13', + 77: 'jmp x-- 13', + 109: 'jmp !y 13', + 141: 'jmp y-- 13', + 173: 'jmp x!=y 13', + 205: 'jmp pin 13', + 237: 'jmp !osre 13', + 14: 'jmp 14', + 46: 'jmp !x 14', + 78: 'jmp x-- 14', + 110: 'jmp !y 14', + 142: 'jmp y-- 14', + 174: 'jmp x!=y 14', + 206: 'jmp pin 14', + 238: 'jmp !osre 14', + 15: 'jmp 15', + 47: 'jmp !x 15', + 79: 'jmp x-- 15', + 111: 'jmp !y 15', + 143: 'jmp y-- 15', + 175: 'jmp x!=y 15', + 207: 'jmp pin 15', + 239: 'jmp !osre 15', + 16: 'jmp 16', + 48: 'jmp !x 16', + 80: 'jmp x-- 16', + 112: 'jmp !y 16', + 144: 'jmp y-- 16', + 176: 'jmp x!=y 16', + 208: 'jmp pin 16', + 240: 'jmp !osre 16', + 17: 'jmp 17', + 49: 'jmp !x 17', + 81: 'jmp x-- 17', + 113: 'jmp !y 17', + 145: 'jmp y-- 17', + 177: 'jmp x!=y 17', + 209: 'jmp pin 17', + 241: 'jmp !osre 17', + 18: 'jmp 18', + 50: 'jmp !x 18', + 82: 'jmp x-- 18', + 114: 'jmp !y 18', + 146: 'jmp y-- 18', + 178: 'jmp x!=y 18', + 210: 'jmp pin 18', + 242: 'jmp !osre 18', + 19: 'jmp 19', + 51: 'jmp !x 19', + 83: 'jmp x-- 19', + 115: 'jmp !y 19', + 147: 'jmp y-- 19', + 179: 'jmp x!=y 19', + 211: 'jmp pin 19', + 243: 'jmp !osre 19', + 20: 'jmp 20', + 52: 'jmp !x 20', + 84: 'jmp x-- 20', + 116: 'jmp !y 20', + 148: 'jmp y-- 20', + 180: 'jmp x!=y 20', + 212: 'jmp pin 20', + 244: 'jmp !osre 20', + 21: 'jmp 21', + 53: 'jmp !x 21', + 85: 'jmp x-- 21', + 117: 'jmp !y 21', + 149: 'jmp y-- 21', + 181: 'jmp x!=y 21', + 213: 'jmp pin 21', + 245: 'jmp !osre 21', + 22: 'jmp 22', + 54: 'jmp !x 22', + 86: 'jmp x-- 22', + 118: 'jmp !y 22', + 150: 'jmp y-- 22', + 182: 'jmp x!=y 22', + 214: 'jmp pin 22', + 246: 'jmp !osre 22', + 23: 'jmp 23', + 55: 'jmp !x 23', + 87: 'jmp x-- 23', + 119: 'jmp !y 23', + 151: 'jmp y-- 23', + 183: 'jmp x!=y 23', + 215: 'jmp pin 23', + 247: 'jmp !osre 23', + 24: 'jmp 24', + 56: 'jmp !x 24', + 88: 'jmp x-- 24', + 120: 'jmp !y 24', + 152: 'jmp y-- 24', + 184: 'jmp x!=y 24', + 216: 'jmp pin 24', + 248: 'jmp !osre 24', + 25: 'jmp 25', + 57: 'jmp !x 25', + 89: 'jmp x-- 25', + 121: 'jmp !y 25', + 153: 'jmp y-- 25', + 185: 'jmp x!=y 25', + 217: 'jmp pin 25', + 249: 'jmp !osre 25', + 26: 'jmp 26', + 58: 'jmp !x 26', + 90: 'jmp x-- 26', + 122: 'jmp !y 26', + 154: 'jmp y-- 26', + 186: 'jmp x!=y 26', + 218: 'jmp pin 26', + 250: 'jmp !osre 26', + 27: 'jmp 27', + 59: 'jmp !x 27', + 91: 'jmp x-- 27', + 123: 'jmp !y 27', + 155: 'jmp y-- 27', + 187: 'jmp x!=y 27', + 219: 'jmp pin 27', + 251: 'jmp !osre 27', + 28: 'jmp 28', + 60: 'jmp !x 28', + 92: 'jmp x-- 28', + 124: 'jmp !y 28', + 156: 'jmp y-- 28', + 188: 'jmp x!=y 28', + 220: 'jmp pin 28', + 252: 'jmp !osre 28', + 29: 'jmp 29', + 61: 'jmp !x 29', + 93: 'jmp x-- 29', + 125: 'jmp !y 29', + 157: 'jmp y-- 29', + 189: 'jmp x!=y 29', + 221: 'jmp pin 29', + 253: 'jmp !osre 29', + 30: 'jmp 30', + 62: 'jmp !x 30', + 94: 'jmp x-- 30', + 126: 'jmp !y 30', + 158: 'jmp y-- 30', + 190: 'jmp x!=y 30', + 222: 'jmp pin 30', + 254: 'jmp !osre 30', + 31: 'jmp 31', + 63: 'jmp !x 31', + 95: 'jmp x-- 31', + 127: 'jmp !y 31', + 159: 'jmp y-- 31', + 191: 'jmp x!=y 31', + 223: 'jmp pin 31', + 255: 'jmp !osre 31', + 8288: 'wait 0 jmppin', + 8192: 'wait 0 gpio 0', + 8193: 'wait 0 gpio 1', + 8194: 'wait 0 gpio 2', + 8195: 'wait 0 gpio 3', + 8196: 'wait 0 gpio 4', + 8197: 'wait 0 gpio 5', + 8198: 'wait 0 gpio 6', + 8199: 'wait 0 gpio 7', + 8200: 'wait 0 gpio 8', + 8201: 'wait 0 gpio 9', + 8202: 'wait 0 gpio 10', + 8203: 'wait 0 gpio 11', + 8204: 'wait 0 gpio 12', + 8205: 'wait 0 gpio 13', + 8206: 'wait 0 gpio 14', + 8207: 'wait 0 gpio 15', + 8208: 'wait 0 gpio 16', + 8209: 'wait 0 gpio 17', + 8210: 'wait 0 gpio 18', + 8211: 'wait 0 gpio 19', + 8212: 'wait 0 gpio 20', + 8213: 'wait 0 gpio 21', + 8214: 'wait 0 gpio 22', + 8215: 'wait 0 gpio 23', + 8216: 'wait 0 gpio 24', + 8217: 'wait 0 gpio 25', + 8218: 'wait 0 gpio 26', + 8219: 'wait 0 gpio 27', + 8220: 'wait 0 gpio 28', + 8221: 'wait 0 gpio 29', + 8222: 'wait 0 gpio 30', + 8223: 'wait 0 gpio 31', + 8224: 'wait 0 pin 0', + 8225: 'wait 0 pin 1', + 8226: 'wait 0 pin 2', + 8227: 'wait 0 pin 3', + 8228: 'wait 0 pin 4', + 8229: 'wait 0 pin 5', + 8230: 'wait 0 pin 6', + 8231: 'wait 0 pin 7', + 8232: 'wait 0 pin 8', + 8233: 'wait 0 pin 9', + 8234: 'wait 0 pin 10', + 8235: 'wait 0 pin 11', + 8236: 'wait 0 pin 12', + 8237: 'wait 0 pin 13', + 8238: 'wait 0 pin 14', + 8239: 'wait 0 pin 15', + 8240: 'wait 0 pin 16', + 8241: 'wait 0 pin 17', + 8242: 'wait 0 pin 18', + 8243: 'wait 0 pin 19', + 8244: 'wait 0 pin 20', + 8245: 'wait 0 pin 21', + 8246: 'wait 0 pin 22', + 8247: 'wait 0 pin 23', + 8248: 'wait 0 pin 24', + 8249: 'wait 0 pin 25', + 8250: 'wait 0 pin 26', + 8251: 'wait 0 pin 27', + 8252: 'wait 0 pin 28', + 8253: 'wait 0 pin 29', + 8254: 'wait 0 pin 30', + 8255: 'wait 0 pin 31', + 8272: 'wait 0 irq 0 rel', + 8264: 'wait 0 irq prev 0', + 8280: 'wait 0 irq next 0', + 8273: 'wait 0 irq 1 rel', + 8265: 'wait 0 irq prev 1', + 8281: 'wait 0 irq next 1', + 8274: 'wait 0 irq 2 rel', + 8266: 'wait 0 irq prev 2', + 8282: 'wait 0 irq next 2', + 8275: 'wait 0 irq 3 rel', + 8267: 'wait 0 irq prev 3', + 8283: 'wait 0 irq next 3', + 8276: 'wait 0 irq 4 rel', + 8268: 'wait 0 irq prev 4', + 8284: 'wait 0 irq next 4', + 8277: 'wait 0 irq 5 rel', + 8269: 'wait 0 irq prev 5', + 8285: 'wait 0 irq next 5', + 8278: 'wait 0 irq 6 rel', + 8270: 'wait 0 irq prev 6', + 8286: 'wait 0 irq next 6', + 8279: 'wait 0 irq 7 rel', + 8271: 'wait 0 irq prev 7', + 8287: 'wait 0 irq next 7', + 8289: 'wait 0 jmppin + 1', + 8290: 'wait 0 jmppin + 2', + 8291: 'wait 0 jmppin + 3', + 8416: 'wait 1 jmppin', + 8320: 'wait 1 gpio 0', + 8321: 'wait 1 gpio 1', + 8322: 'wait 1 gpio 2', + 8323: 'wait 1 gpio 3', + 8324: 'wait 1 gpio 4', + 8325: 'wait 1 gpio 5', + 8326: 'wait 1 gpio 6', + 8327: 'wait 1 gpio 7', + 8328: 'wait 1 gpio 8', + 8329: 'wait 1 gpio 9', + 8330: 'wait 1 gpio 10', + 8331: 'wait 1 gpio 11', + 8332: 'wait 1 gpio 12', + 8333: 'wait 1 gpio 13', + 8334: 'wait 1 gpio 14', + 8335: 'wait 1 gpio 15', + 8336: 'wait 1 gpio 16', + 8337: 'wait 1 gpio 17', + 8338: 'wait 1 gpio 18', + 8339: 'wait 1 gpio 19', + 8340: 'wait 1 gpio 20', + 8341: 'wait 1 gpio 21', + 8342: 'wait 1 gpio 22', + 8343: 'wait 1 gpio 23', + 8344: 'wait 1 gpio 24', + 8345: 'wait 1 gpio 25', + 8346: 'wait 1 gpio 26', + 8347: 'wait 1 gpio 27', + 8348: 'wait 1 gpio 28', + 8349: 'wait 1 gpio 29', + 8350: 'wait 1 gpio 30', + 8351: 'wait 1 gpio 31', + 8352: 'wait 1 pin 0', + 8353: 'wait 1 pin 1', + 8354: 'wait 1 pin 2', + 8355: 'wait 1 pin 3', + 8356: 'wait 1 pin 4', + 8357: 'wait 1 pin 5', + 8358: 'wait 1 pin 6', + 8359: 'wait 1 pin 7', + 8360: 'wait 1 pin 8', + 8361: 'wait 1 pin 9', + 8362: 'wait 1 pin 10', + 8363: 'wait 1 pin 11', + 8364: 'wait 1 pin 12', + 8365: 'wait 1 pin 13', + 8366: 'wait 1 pin 14', + 8367: 'wait 1 pin 15', + 8368: 'wait 1 pin 16', + 8369: 'wait 1 pin 17', + 8370: 'wait 1 pin 18', + 8371: 'wait 1 pin 19', + 8372: 'wait 1 pin 20', + 8373: 'wait 1 pin 21', + 8374: 'wait 1 pin 22', + 8375: 'wait 1 pin 23', + 8376: 'wait 1 pin 24', + 8377: 'wait 1 pin 25', + 8378: 'wait 1 pin 26', + 8379: 'wait 1 pin 27', + 8380: 'wait 1 pin 28', + 8381: 'wait 1 pin 29', + 8382: 'wait 1 pin 30', + 8383: 'wait 1 pin 31', + 8400: 'wait 1 irq 0 rel', + 8392: 'wait 1 irq prev 0', + 8408: 'wait 1 irq next 0', + 8401: 'wait 1 irq 1 rel', + 8393: 'wait 1 irq prev 1', + 8409: 'wait 1 irq next 1', + 8402: 'wait 1 irq 2 rel', + 8394: 'wait 1 irq prev 2', + 8410: 'wait 1 irq next 2', + 8403: 'wait 1 irq 3 rel', + 8395: 'wait 1 irq prev 3', + 8411: 'wait 1 irq next 3', + 8404: 'wait 1 irq 4 rel', + 8396: 'wait 1 irq prev 4', + 8412: 'wait 1 irq next 4', + 8405: 'wait 1 irq 5 rel', + 8397: 'wait 1 irq prev 5', + 8413: 'wait 1 irq next 5', + 8406: 'wait 1 irq 6 rel', + 8398: 'wait 1 irq prev 6', + 8414: 'wait 1 irq next 6', + 8407: 'wait 1 irq 7 rel', + 8399: 'wait 1 irq prev 7', + 8415: 'wait 1 irq next 7', + 8417: 'wait 1 jmppin + 1', + 8418: 'wait 1 jmppin + 2', + 8419: 'wait 1 jmppin + 3', + 16385: 'in pins 1', + 16386: 'in pins 2', + 16387: 'in pins 3', + 16388: 'in pins 4', + 16389: 'in pins 5', + 16390: 'in pins 6', + 16391: 'in pins 7', + 16392: 'in pins 8', + 16393: 'in pins 9', + 16394: 'in pins 10', + 16395: 'in pins 11', + 16396: 'in pins 12', + 16397: 'in pins 13', + 16398: 'in pins 14', + 16399: 'in pins 15', + 16400: 'in pins 16', + 16401: 'in pins 17', + 16402: 'in pins 18', + 16403: 'in pins 19', + 16404: 'in pins 20', + 16405: 'in pins 21', + 16406: 'in pins 22', + 16407: 'in pins 23', + 16408: 'in pins 24', + 16409: 'in pins 25', + 16410: 'in pins 26', + 16411: 'in pins 27', + 16412: 'in pins 28', + 16413: 'in pins 29', + 16414: 'in pins 30', + 16415: 'in pins 31', + 16384: 'in pins 32', + 16417: 'in x 1', + 16418: 'in x 2', + 16419: 'in x 3', + 16420: 'in x 4', + 16421: 'in x 5', + 16422: 'in x 6', + 16423: 'in x 7', + 16424: 'in x 8', + 16425: 'in x 9', + 16426: 'in x 10', + 16427: 'in x 11', + 16428: 'in x 12', + 16429: 'in x 13', + 16430: 'in x 14', + 16431: 'in x 15', + 16432: 'in x 16', + 16433: 'in x 17', + 16434: 'in x 18', + 16435: 'in x 19', + 16436: 'in x 20', + 16437: 'in x 21', + 16438: 'in x 22', + 16439: 'in x 23', + 16440: 'in x 24', + 16441: 'in x 25', + 16442: 'in x 26', + 16443: 'in x 27', + 16444: 'in x 28', + 16445: 'in x 29', + 16446: 'in x 30', + 16447: 'in x 31', + 16416: 'in x 32', + 16449: 'in y 1', + 16450: 'in y 2', + 16451: 'in y 3', + 16452: 'in y 4', + 16453: 'in y 5', + 16454: 'in y 6', + 16455: 'in y 7', + 16456: 'in y 8', + 16457: 'in y 9', + 16458: 'in y 10', + 16459: 'in y 11', + 16460: 'in y 12', + 16461: 'in y 13', + 16462: 'in y 14', + 16463: 'in y 15', + 16464: 'in y 16', + 16465: 'in y 17', + 16466: 'in y 18', + 16467: 'in y 19', + 16468: 'in y 20', + 16469: 'in y 21', + 16470: 'in y 22', + 16471: 'in y 23', + 16472: 'in y 24', + 16473: 'in y 25', + 16474: 'in y 26', + 16475: 'in y 27', + 16476: 'in y 28', + 16477: 'in y 29', + 16478: 'in y 30', + 16479: 'in y 31', + 16448: 'in y 32', + 16481: 'in null 1', + 16482: 'in null 2', + 16483: 'in null 3', + 16484: 'in null 4', + 16485: 'in null 5', + 16486: 'in null 6', + 16487: 'in null 7', + 16488: 'in null 8', + 16489: 'in null 9', + 16490: 'in null 10', + 16491: 'in null 11', + 16492: 'in null 12', + 16493: 'in null 13', + 16494: 'in null 14', + 16495: 'in null 15', + 16496: 'in null 16', + 16497: 'in null 17', + 16498: 'in null 18', + 16499: 'in null 19', + 16500: 'in null 20', + 16501: 'in null 21', + 16502: 'in null 22', + 16503: 'in null 23', + 16504: 'in null 24', + 16505: 'in null 25', + 16506: 'in null 26', + 16507: 'in null 27', + 16508: 'in null 28', + 16509: 'in null 29', + 16510: 'in null 30', + 16511: 'in null 31', + 16480: 'in null 32', + 16577: 'in isr 1', + 16578: 'in isr 2', + 16579: 'in isr 3', + 16580: 'in isr 4', + 16581: 'in isr 5', + 16582: 'in isr 6', + 16583: 'in isr 7', + 16584: 'in isr 8', + 16585: 'in isr 9', + 16586: 'in isr 10', + 16587: 'in isr 11', + 16588: 'in isr 12', + 16589: 'in isr 13', + 16590: 'in isr 14', + 16591: 'in isr 15', + 16592: 'in isr 16', + 16593: 'in isr 17', + 16594: 'in isr 18', + 16595: 'in isr 19', + 16596: 'in isr 20', + 16597: 'in isr 21', + 16598: 'in isr 22', + 16599: 'in isr 23', + 16600: 'in isr 24', + 16601: 'in isr 25', + 16602: 'in isr 26', + 16603: 'in isr 27', + 16604: 'in isr 28', + 16605: 'in isr 29', + 16606: 'in isr 30', + 16607: 'in isr 31', + 16576: 'in isr 32', + 16609: 'in osr 1', + 16610: 'in osr 2', + 16611: 'in osr 3', + 16612: 'in osr 4', + 16613: 'in osr 5', + 16614: 'in osr 6', + 16615: 'in osr 7', + 16616: 'in osr 8', + 16617: 'in osr 9', + 16618: 'in osr 10', + 16619: 'in osr 11', + 16620: 'in osr 12', + 16621: 'in osr 13', + 16622: 'in osr 14', + 16623: 'in osr 15', + 16624: 'in osr 16', + 16625: 'in osr 17', + 16626: 'in osr 18', + 16627: 'in osr 19', + 16628: 'in osr 20', + 16629: 'in osr 21', + 16630: 'in osr 22', + 16631: 'in osr 23', + 16632: 'in osr 24', + 16633: 'in osr 25', + 16634: 'in osr 26', + 16635: 'in osr 27', + 16636: 'in osr 28', + 16637: 'in osr 29', + 16638: 'in osr 30', + 16639: 'in osr 31', + 16608: 'in osr 32', + 24577: 'out pins 1', + 24578: 'out pins 2', + 24579: 'out pins 3', + 24580: 'out pins 4', + 24581: 'out pins 5', + 24582: 'out pins 6', + 24583: 'out pins 7', + 24584: 'out pins 8', + 24585: 'out pins 9', + 24586: 'out pins 10', + 24587: 'out pins 11', + 24588: 'out pins 12', + 24589: 'out pins 13', + 24590: 'out pins 14', + 24591: 'out pins 15', + 24592: 'out pins 16', + 24593: 'out pins 17', + 24594: 'out pins 18', + 24595: 'out pins 19', + 24596: 'out pins 20', + 24597: 'out pins 21', + 24598: 'out pins 22', + 24599: 'out pins 23', + 24600: 'out pins 24', + 24601: 'out pins 25', + 24602: 'out pins 26', + 24603: 'out pins 27', + 24604: 'out pins 28', + 24605: 'out pins 29', + 24606: 'out pins 30', + 24607: 'out pins 31', + 24576: 'out pins 32', + 24609: 'out x 1', + 24610: 'out x 2', + 24611: 'out x 3', + 24612: 'out x 4', + 24613: 'out x 5', + 24614: 'out x 6', + 24615: 'out x 7', + 24616: 'out x 8', + 24617: 'out x 9', + 24618: 'out x 10', + 24619: 'out x 11', + 24620: 'out x 12', + 24621: 'out x 13', + 24622: 'out x 14', + 24623: 'out x 15', + 24624: 'out x 16', + 24625: 'out x 17', + 24626: 'out x 18', + 24627: 'out x 19', + 24628: 'out x 20', + 24629: 'out x 21', + 24630: 'out x 22', + 24631: 'out x 23', + 24632: 'out x 24', + 24633: 'out x 25', + 24634: 'out x 26', + 24635: 'out x 27', + 24636: 'out x 28', + 24637: 'out x 29', + 24638: 'out x 30', + 24639: 'out x 31', + 24608: 'out x 32', + 24641: 'out y 1', + 24642: 'out y 2', + 24643: 'out y 3', + 24644: 'out y 4', + 24645: 'out y 5', + 24646: 'out y 6', + 24647: 'out y 7', + 24648: 'out y 8', + 24649: 'out y 9', + 24650: 'out y 10', + 24651: 'out y 11', + 24652: 'out y 12', + 24653: 'out y 13', + 24654: 'out y 14', + 24655: 'out y 15', + 24656: 'out y 16', + 24657: 'out y 17', + 24658: 'out y 18', + 24659: 'out y 19', + 24660: 'out y 20', + 24661: 'out y 21', + 24662: 'out y 22', + 24663: 'out y 23', + 24664: 'out y 24', + 24665: 'out y 25', + 24666: 'out y 26', + 24667: 'out y 27', + 24668: 'out y 28', + 24669: 'out y 29', + 24670: 'out y 30', + 24671: 'out y 31', + 24640: 'out y 32', + 24673: 'out null 1', + 24674: 'out null 2', + 24675: 'out null 3', + 24676: 'out null 4', + 24677: 'out null 5', + 24678: 'out null 6', + 24679: 'out null 7', + 24680: 'out null 8', + 24681: 'out null 9', + 24682: 'out null 10', + 24683: 'out null 11', + 24684: 'out null 12', + 24685: 'out null 13', + 24686: 'out null 14', + 24687: 'out null 15', + 24688: 'out null 16', + 24689: 'out null 17', + 24690: 'out null 18', + 24691: 'out null 19', + 24692: 'out null 20', + 24693: 'out null 21', + 24694: 'out null 22', + 24695: 'out null 23', + 24696: 'out null 24', + 24697: 'out null 25', + 24698: 'out null 26', + 24699: 'out null 27', + 24700: 'out null 28', + 24701: 'out null 29', + 24702: 'out null 30', + 24703: 'out null 31', + 24672: 'out null 32', + 24705: 'out pindirs 1', + 24706: 'out pindirs 2', + 24707: 'out pindirs 3', + 24708: 'out pindirs 4', + 24709: 'out pindirs 5', + 24710: 'out pindirs 6', + 24711: 'out pindirs 7', + 24712: 'out pindirs 8', + 24713: 'out pindirs 9', + 24714: 'out pindirs 10', + 24715: 'out pindirs 11', + 24716: 'out pindirs 12', + 24717: 'out pindirs 13', + 24718: 'out pindirs 14', + 24719: 'out pindirs 15', + 24720: 'out pindirs 16', + 24721: 'out pindirs 17', + 24722: 'out pindirs 18', + 24723: 'out pindirs 19', + 24724: 'out pindirs 20', + 24725: 'out pindirs 21', + 24726: 'out pindirs 22', + 24727: 'out pindirs 23', + 24728: 'out pindirs 24', + 24729: 'out pindirs 25', + 24730: 'out pindirs 26', + 24731: 'out pindirs 27', + 24732: 'out pindirs 28', + 24733: 'out pindirs 29', + 24734: 'out pindirs 30', + 24735: 'out pindirs 31', + 24704: 'out pindirs 32', + 24737: 'out pc 1', + 24738: 'out pc 2', + 24739: 'out pc 3', + 24740: 'out pc 4', + 24741: 'out pc 5', + 24742: 'out pc 6', + 24743: 'out pc 7', + 24744: 'out pc 8', + 24745: 'out pc 9', + 24746: 'out pc 10', + 24747: 'out pc 11', + 24748: 'out pc 12', + 24749: 'out pc 13', + 24750: 'out pc 14', + 24751: 'out pc 15', + 24752: 'out pc 16', + 24753: 'out pc 17', + 24754: 'out pc 18', + 24755: 'out pc 19', + 24756: 'out pc 20', + 24757: 'out pc 21', + 24758: 'out pc 22', + 24759: 'out pc 23', + 24760: 'out pc 24', + 24761: 'out pc 25', + 24762: 'out pc 26', + 24763: 'out pc 27', + 24764: 'out pc 28', + 24765: 'out pc 29', + 24766: 'out pc 30', + 24767: 'out pc 31', + 24736: 'out pc 32', + 24769: 'out isr 1', + 24770: 'out isr 2', + 24771: 'out isr 3', + 24772: 'out isr 4', + 24773: 'out isr 5', + 24774: 'out isr 6', + 24775: 'out isr 7', + 24776: 'out isr 8', + 24777: 'out isr 9', + 24778: 'out isr 10', + 24779: 'out isr 11', + 24780: 'out isr 12', + 24781: 'out isr 13', + 24782: 'out isr 14', + 24783: 'out isr 15', + 24784: 'out isr 16', + 24785: 'out isr 17', + 24786: 'out isr 18', + 24787: 'out isr 19', + 24788: 'out isr 20', + 24789: 'out isr 21', + 24790: 'out isr 22', + 24791: 'out isr 23', + 24792: 'out isr 24', + 24793: 'out isr 25', + 24794: 'out isr 26', + 24795: 'out isr 27', + 24796: 'out isr 28', + 24797: 'out isr 29', + 24798: 'out isr 30', + 24799: 'out isr 31', + 24768: 'out isr 32', + 24801: 'out exec 1', + 24802: 'out exec 2', + 24803: 'out exec 3', + 24804: 'out exec 4', + 24805: 'out exec 5', + 24806: 'out exec 6', + 24807: 'out exec 7', + 24808: 'out exec 8', + 24809: 'out exec 9', + 24810: 'out exec 10', + 24811: 'out exec 11', + 24812: 'out exec 12', + 24813: 'out exec 13', + 24814: 'out exec 14', + 24815: 'out exec 15', + 24816: 'out exec 16', + 24817: 'out exec 17', + 24818: 'out exec 18', + 24819: 'out exec 19', + 24820: 'out exec 20', + 24821: 'out exec 21', + 24822: 'out exec 22', + 24823: 'out exec 23', + 24824: 'out exec 24', + 24825: 'out exec 25', + 24826: 'out exec 26', + 24827: 'out exec 27', + 24828: 'out exec 28', + 24829: 'out exec 29', + 24830: 'out exec 30', + 24831: 'out exec 31', + 24800: 'out exec 32', + 32800: ('push', {'fifo': 'txrx'}), + 32864: ('push iffull block', {'fifo': 'txrx'}), + 32832: ('push iffull noblock', {'fifo': 'txrx'}), + 32928: ('pull', {'fifo': 'txrx'}), + 32992: ('pull ifempty block', {'fifo': 'txrx'}), + 32960: ('pull ifempty noblock', {'fifo': 'txrx'}), + 40960: 'mov pins pins', + 40968: 'mov pins ~pins', + 40976: 'mov pins ::pins', + 40961: 'mov pins x', + 40969: 'mov pins ~x', + 40977: 'mov pins ::x', + 40962: 'mov pins y', + 40970: 'mov pins ~y', + 40978: 'mov pins ::y', + 40963: 'mov pins null', + 40971: 'mov pins ~null', + 40979: 'mov pins ::null', + 40965: 'mov pins status', + 40973: 'mov pins ~status', + 40981: 'mov pins ::status', + 40966: 'mov pins isr', + 40974: 'mov pins ~isr', + 40982: 'mov pins ::isr', + 40967: 'mov pins osr', + 40975: 'mov pins ~osr', + 40983: 'mov pins ::osr', + 40992: 'mov x pins', + 41000: 'mov x ~pins', + 41008: 'mov x ::pins', + 40993: 'mov x x', + 41001: 'mov x ~x', + 41009: 'mov x ::x', + 40994: 'mov x y', + 41002: 'mov x ~y', + 41010: 'mov x ::y', + 40995: 'mov x null', + 41003: 'mov x ~null', + 41011: 'mov x ::null', + 40997: 'mov x status', + 41005: 'mov x ~status', + 41013: 'mov x ::status', + 40998: 'mov x isr', + 41006: 'mov x ~isr', + 41014: 'mov x ::isr', + 40999: 'mov x osr', + 41007: 'mov x ~osr', + 41015: 'mov x ::osr', + 41024: 'mov y pins', + 41032: 'mov y ~pins', + 41040: 'mov y ::pins', + 41025: 'mov y x', + 41033: 'mov y ~x', + 41041: 'mov y ::x', + 41026: 'mov y y', + 41034: 'mov y ~y', + 41042: 'mov y ::y', + 41027: 'mov y null', + 41035: 'mov y ~null', + 41043: 'mov y ::null', + 41029: 'mov y status', + 41037: 'mov y ~status', + 41045: 'mov y ::status', + 41030: 'mov y isr', + 41038: 'mov y ~isr', + 41046: 'mov y ::isr', + 41031: 'mov y osr', + 41039: 'mov y ~osr', + 41047: 'mov y ::osr', + 41056: 'mov pindirs pins', + 41064: 'mov pindirs ~pins', + 41072: 'mov pindirs ::pins', + 41057: 'mov pindirs x', + 41065: 'mov pindirs ~x', + 41073: 'mov pindirs ::x', + 41058: 'mov pindirs y', + 41066: 'mov pindirs ~y', + 41074: 'mov pindirs ::y', + 41059: 'mov pindirs null', + 41067: 'mov pindirs ~null', + 41075: 'mov pindirs ::null', + 41061: 'mov pindirs status', + 41069: 'mov pindirs ~status', + 41077: 'mov pindirs ::status', + 41062: 'mov pindirs isr', + 41070: 'mov pindirs ~isr', + 41078: 'mov pindirs ::isr', + 41063: 'mov pindirs osr', + 41071: 'mov pindirs ~osr', + 41079: 'mov pindirs ::osr', + 41088: 'mov exec pins', + 41096: 'mov exec ~pins', + 41104: 'mov exec ::pins', + 41089: 'mov exec x', + 41097: 'mov exec ~x', + 41105: 'mov exec ::x', + 41090: 'mov exec y', + 41098: 'mov exec ~y', + 41106: 'mov exec ::y', + 41091: 'mov exec null', + 41099: 'mov exec ~null', + 41107: 'mov exec ::null', + 41093: 'mov exec status', + 41101: 'mov exec ~status', + 41109: 'mov exec ::status', + 41094: 'mov exec isr', + 41102: 'mov exec ~isr', + 41110: 'mov exec ::isr', + 41095: 'mov exec osr', + 41103: 'mov exec ~osr', + 41111: 'mov exec ::osr', + 41120: 'mov pc pins', + 41128: 'mov pc ~pins', + 41136: 'mov pc ::pins', + 41121: 'mov pc x', + 41129: 'mov pc ~x', + 41137: 'mov pc ::x', + 41122: 'mov pc y', + 41130: 'mov pc ~y', + 41138: 'mov pc ::y', + 41123: 'mov pc null', + 41131: 'mov pc ~null', + 41139: 'mov pc ::null', + 41125: 'mov pc status', + 41133: 'mov pc ~status', + 41141: 'mov pc ::status', + 41126: 'mov pc isr', + 41134: 'mov pc ~isr', + 41142: 'mov pc ::isr', + 41127: 'mov pc osr', + 41135: 'mov pc ~osr', + 41143: 'mov pc ::osr', + 41152: 'mov isr pins', + 41160: 'mov isr ~pins', + 41168: 'mov isr ::pins', + 41153: 'mov isr x', + 41161: 'mov isr ~x', + 41169: 'mov isr ::x', + 41154: 'mov isr y', + 41162: 'mov isr ~y', + 41170: 'mov isr ::y', + 41155: 'mov isr null', + 41163: 'mov isr ~null', + 41171: 'mov isr ::null', + 41157: 'mov isr status', + 41165: 'mov isr ~status', + 41173: 'mov isr ::status', + 41158: 'mov isr isr', + 41166: 'mov isr ~isr', + 41174: 'mov isr ::isr', + 41159: 'mov isr osr', + 41167: 'mov isr ~osr', + 41175: 'mov isr ::osr', + 41184: 'mov osr pins', + 41192: 'mov osr ~pins', + 41200: 'mov osr ::pins', + 41185: 'mov osr x', + 41193: 'mov osr ~x', + 41201: 'mov osr ::x', + 41186: 'mov osr y', + 41194: 'mov osr ~y', + 41202: 'mov osr ::y', + 41187: 'mov osr null', + 41195: 'mov osr ~null', + 41203: 'mov osr ::null', + 41189: 'mov osr status', + 41197: 'mov osr ~status', + 41205: 'mov osr ::status', + 41190: 'mov osr isr', + 41198: 'mov osr ~isr', + 41206: 'mov osr ::isr', + 41191: 'mov osr osr', + 41199: 'mov osr ~osr', + 41207: 'mov osr ::osr', + 32792: 'mov rxfifo[0], isr', + 32920: 'mov osr, rxfifo[0]', + 32793: 'mov rxfifo[1], isr', + 32921: 'mov osr, rxfifo[1]', + 32794: 'mov rxfifo[2], isr', + 32922: 'mov osr, rxfifo[2]', + 32795: 'mov rxfifo[3], isr', + 32923: 'mov osr, rxfifo[3]', + 32784: 'mov rxfifo[y], isr', + 32912: 'mov osr, rxfifo[y]', + 49152: 'irq 0', + 49168: 'irq 0 rel', + 49160: 'irq prev 0', + 49176: 'irq next 0', + 49153: 'irq 1', + 49169: 'irq 1 rel', + 49161: 'irq prev 1', + 49177: 'irq next 1', + 49154: 'irq 2', + 49170: 'irq 2 rel', + 49162: 'irq prev 2', + 49178: 'irq next 2', + 49155: 'irq 3', + 49171: 'irq 3 rel', + 49163: 'irq prev 3', + 49179: 'irq next 3', + 49156: 'irq 4', + 49172: 'irq 4 rel', + 49164: 'irq prev 4', + 49180: 'irq next 4', + 49157: 'irq 5', + 49173: 'irq 5 rel', + 49165: 'irq prev 5', + 49181: 'irq next 5', + 49158: 'irq 6', + 49174: 'irq 6 rel', + 49166: 'irq prev 6', + 49182: 'irq next 6', + 49159: 'irq 7', + 49175: 'irq 7 rel', + 49167: 'irq prev 7', + 49183: 'irq next 7', + 57344: 'set pins 0', + 57345: 'set pins 1', + 57346: 'set pins 2', + 57347: 'set pins 3', + 57348: 'set pins 4', + 57349: 'set pins 5', + 57350: 'set pins 6', + 57351: 'set pins 7', + 57352: 'set pins 8', + 57353: 'set pins 9', + 57354: 'set pins 10', + 57355: 'set pins 11', + 57356: 'set pins 12', + 57357: 'set pins 13', + 57358: 'set pins 14', + 57359: 'set pins 15', + 57360: 'set pins 16', + 57361: 'set pins 17', + 57362: 'set pins 18', + 57363: 'set pins 19', + 57364: 'set pins 20', + 57365: 'set pins 21', + 57366: 'set pins 22', + 57367: 'set pins 23', + 57368: 'set pins 24', + 57369: 'set pins 25', + 57370: 'set pins 26', + 57371: 'set pins 27', + 57372: 'set pins 28', + 57373: 'set pins 29', + 57374: 'set pins 30', + 57375: 'set pins 31', + 57376: 'set x 0', + 57377: 'set x 1', + 57378: 'set x 2', + 57379: 'set x 3', + 57380: 'set x 4', + 57381: 'set x 5', + 57382: 'set x 6', + 57383: 'set x 7', + 57384: 'set x 8', + 57385: 'set x 9', + 57386: 'set x 10', + 57387: 'set x 11', + 57388: 'set x 12', + 57389: 'set x 13', + 57390: 'set x 14', + 57391: 'set x 15', + 57392: 'set x 16', + 57393: 'set x 17', + 57394: 'set x 18', + 57395: 'set x 19', + 57396: 'set x 20', + 57397: 'set x 21', + 57398: 'set x 22', + 57399: 'set x 23', + 57400: 'set x 24', + 57401: 'set x 25', + 57402: 'set x 26', + 57403: 'set x 27', + 57404: 'set x 28', + 57405: 'set x 29', + 57406: 'set x 30', + 57407: 'set x 31', + 57408: 'set y 0', + 57409: 'set y 1', + 57410: 'set y 2', + 57411: 'set y 3', + 57412: 'set y 4', + 57413: 'set y 5', + 57414: 'set y 6', + 57415: 'set y 7', + 57416: 'set y 8', + 57417: 'set y 9', + 57418: 'set y 10', + 57419: 'set y 11', + 57420: 'set y 12', + 57421: 'set y 13', + 57422: 'set y 14', + 57423: 'set y 15', + 57424: 'set y 16', + 57425: 'set y 17', + 57426: 'set y 18', + 57427: 'set y 19', + 57428: 'set y 20', + 57429: 'set y 21', + 57430: 'set y 22', + 57431: 'set y 23', + 57432: 'set y 24', + 57433: 'set y 25', + 57434: 'set y 26', + 57435: 'set y 27', + 57436: 'set y 28', + 57437: 'set y 29', + 57438: 'set y 30', + 57439: 'set y 31', + 57472: 'set pindirs 0', + 57473: 'set pindirs 1', + 57474: 'set pindirs 2', + 57475: 'set pindirs 3', + 57476: 'set pindirs 4', + 57477: 'set pindirs 5', + 57478: 'set pindirs 6', + 57479: 'set pindirs 7', + 57480: 'set pindirs 8', + 57481: 'set pindirs 9', + 57482: 'set pindirs 10', + 57483: 'set pindirs 11', + 57484: 'set pindirs 12', + 57485: 'set pindirs 13', + 57486: 'set pindirs 14', + 57487: 'set pindirs 15', + 57488: 'set pindirs 16', + 57489: 'set pindirs 17', + 57490: 'set pindirs 18', + 57491: 'set pindirs 19', + 57492: 'set pindirs 20', + 57493: 'set pindirs 21', + 57494: 'set pindirs 22', + 57495: 'set pindirs 23', + 57496: 'set pindirs 24', + 57497: 'set pindirs 25', + 57498: 'set pindirs 26', + 57499: 'set pindirs 27', + 57500: 'set pindirs 28', + 57501: 'set pindirs 29', + 57502: 'set pindirs 30', + 57503: 'set pindirs 31', +} diff --git a/tests/make_all.py b/tests/make_all.py new file mode 100644 index 0000000..d3ca0da --- /dev/null +++ b/tests/make_all.py @@ -0,0 +1,129 @@ +# SPDX-FileCopyrightText: 2024 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +import re +from subprocess import check_output + +PIOASM = ( + "/home/jepler/src/circuitpython/ports/raspberrypi/sdk/tools/pioasm/build/pioasm" +) + + +def assemble_one_instruction(instruction_in): + if isinstance(instruction_in, str): + return _assemble_one_instruction(instruction_in) + return _assemble_one_instruction(instruction_in[0], **instruction_in[1]) + + +def _assemble_one_instruction(instruction_in, fifo="putget"): + nops = "\n".join("nop" for _ in range(31)) + program = f""" + .program all_pio + .pio_version 1 + .fifo {fifo} + {instruction_in} + {nops} + """ + output = check_output( + [PIOASM, "/dev/stdin", "/dev/stdout"], input=program, encoding="utf-8" + ) + return int(re.search("0x[0-9a-f]{4}", output).group(0), 16) + + +def all_jmp(): + for i in range(32): + yield f"jmp {i}" + for cond in "!x", "x--", "!y", "y--", "x!=y", "pin", "!osre": + yield f"jmp {cond} {i}" + + +def all_wait(): + for polarity in range(2): + yield f"wait {polarity} jmppin" + for source in "gpio", "pin": + for i in range(32): + yield f"wait {polarity} {source} {i}" + for i in range(8): + yield f"wait {polarity} irq {i} rel" + for what in "prev", "next": + yield f"wait {polarity} irq {what} {i}" + for i in range(1, 4): + yield f"wait {polarity} jmppin + {i}" + + +def all_in(): + for source in "pins", "x", "y", "null", "isr", "osr": + for bit_count in range(1, 33): + yield f"in {source} {bit_count}" + + +def all_out(): + for dest in "pins", "x", "y", "null", "pindirs", "pc", "isr", "exec": + for bit_count in range(1, 33): + yield f"out {dest} {bit_count}" + + +def all_push(): + yield "push", {"fifo": "txrx"} + yield "push iffull block", {"fifo": "txrx"} + yield "push iffull noblock", {"fifo": "txrx"} + + +def all_pull(): + yield "pull", {"fifo": "txrx"} + yield "pull ifempty block", {"fifo": "txrx"} + yield "pull ifempty noblock", {"fifo": "txrx"} + + +def all_mov(): + for dest in ("pins", "x", "y", "pindirs", "exec", "pc", "isr", "osr"): + for source in ("pins", "x", "y", "null", "status", "isr", "osr"): + for operator in "", "~", "::": + yield f"mov {dest} {operator}{source}" + for where in 0, 1, 2, 3, "y": + yield f"mov rxfifo[{where}], isr" + yield f"mov osr, rxfifo[{where}]" + + +def all_irq(): + for i in range(8): + yield f"irq {i}" + yield f"irq {i} rel" + for what in "prev", "next": + yield f"irq {what} {i}" + + +def all_set(): + for dest in ("pins", "x", "y", "pindirs"): + for i in range(32): + yield f"set {dest} {i}" + + +def all_instructions(): + yield from all_jmp() + yield from all_wait() + yield from all_in() + yield from all_out() + yield from all_push() + yield from all_pull() + yield from all_mov() + yield from all_irq() + yield from all_set() + + +if __name__ == "__main__": + print( + """\ +# SPDX-FileCopyrightText: 2024 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT +# pylint: disable=too-many-lines +# fmt: off +""" + ) + print("all_instruction = {") + for instr in all_instructions(): + assembled = assemble_one_instruction(instr) + print(f" {assembled}: {instr!r},") + print("}") diff --git a/tests/test_all.py b/tests/test_all.py new file mode 100644 index 0000000..5d0d094 --- /dev/null +++ b/tests/test_all.py @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2024 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +import pytest +from pytest_helpers import assert_assembles_to + +import all_pio_instructions + + +def _assert_one(expected, instruction_in, fifo="putget"): + program = f""" + .program all_pio + .pio_version 1 + .fifo {fifo} + {instruction_in} + """ + assert_assembles_to(program, [expected]) + + +def assert_one(expected, instruction_in): + if isinstance(instruction_in, str): + return _assert_one(expected, instruction_in) + return _assert_one(expected, instruction_in[0], **instruction_in[1]) + + +@pytest.mark.parametrize("arg", all_pio_instructions.all_instruction.items()) +def test_all(arg): + expected = arg[0] + instruction = arg[1] + assert_one(expected, instruction) From 679feb8e21488a02139e358f135bd9df71b11244 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 13 Sep 2024 16:32:12 -0500 Subject: [PATCH 26/38] Fix assembly of irq & mov rxfifo[] instructions to match sdk pioasm --- adafruit_pioasm.py | 76 ++++++++++++++++++++++++++----------------- tests/test_version.py | 17 +++++----- 2 files changed, 55 insertions(+), 38 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 4c5e8b7..8ed564b 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -234,7 +234,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): max_delay = 2 ** (5 - sideset_count - sideset_enable) - 1 assembled = [] - for line in instructions: + for line in instructions: # pylint: disable=too-many-nested-blocks instruction = splitter(line.strip()) delay = 0 if ( @@ -299,21 +299,32 @@ def parse_rxfifo_brackets(arg, fifo_dir): assembled[-1] |= num assembled[-1] |= 0b11 << 5 # JMPPIN wait source else: + idx = 3 assembled[-1] |= WAIT_SOURCES.index(instruction[2]) << 5 - num = int(instruction[3], 0) - if not 0 <= num <= 31: - raise RuntimeError("Wait num out of range") + if source == "irq": + if instruction[idx] == "next": + require_version(1, "wait irq next") + assembled[-1] |= 0b11000 + idx += 1 + elif instruction[idx] == "prev": + require_version(1, "wait irq prev") + assembled[-1] |= 0b01000 + idx += 1 + + limit = 8 + # The flag index is decoded in the same way as the IRQ + # index field, decoding down from the two MSBs + if instruction[-1] == "rel": + if assembled[-1] & 0b11000: + raise RuntimeError("cannot use next/prev with rel") + assembled[-1] |= 0b10000 + else: + limit = 32 + num = int_in_range( + instruction[idx], 0, limit, "wait {instruction[2]}" + ) assembled[-1] |= num - # The flag index is decoded in the same way as the IRQ - # index field, decoding down from the two MSBs - if instruction[-1] == "next": - require_version(1, "wait irq next") - assembled[-1] |= 0b11000 - elif instruction[-1] == "prev": - require_version(1, "wait irq prev") - assembled[-1] |= 0b01000 - elif instruction[-1] == "rel": - assembled[-1] |= 0b10000 + elif instruction[0] == "in": # instr delay src count assembled.append(0b010_00000_000_00000) @@ -352,15 +363,15 @@ def parse_rxfifo_brackets(arg, fifo_dir): elif instruction[0] == "mov": # instr delay dst op src if instruction[1].startswith("rxfifo["): # mov rxfifo[], isr - assembled.append(0b100_00000_0001_0_000) + assembled.append(0b100_00000_0001_1_000) if instruction[2] != "isr": raise ValueError("mov rxfifo[] source must be isr") - assembled[-1] |= parse_rxfifo_brackets(instruction[1], "txput") + assembled[-1] ^= parse_rxfifo_brackets(instruction[1], "txput") elif instruction[2].startswith("rxfifo["): # mov osr, rxfifo[] - assembled.append(0b100_00000_1001_0_000) + assembled.append(0b100_00000_1001_1_000) if instruction[1] != "osr": raise ValueError("mov ,rxfifo[] target must be osr") - assembled[-1] |= parse_rxfifo_brackets(instruction[2], "txget") + assembled[-1] ^= parse_rxfifo_brackets(instruction[2], "txget") else: assembled.append(0b101_00000_000_00_000) assembled[-1] |= mov_destinations.index(instruction[1]) << 5 @@ -388,30 +399,35 @@ def parse_rxfifo_brackets(arg, fifo_dir): assembled.append(0b110_00000_0_0_0_00000) irq_type = 0 - if instruction[-1] == "prev": + idx = 1 + if instruction[idx] == "wait": + assembled[-1] |= 0x20 + idx += 1 + elif instruction[idx] == "clear": + assembled[-1] |= 0x40 + idx += 1 + + if instruction[idx] == "prev": irq_type = 1 require_version(1, "irq prev") - instruction.pop() - elif instruction[-1] == "next": + idx += 1 + elif instruction[idx] == "next": irq_type = 3 require_version(1, "irq next") - instruction.pop() - elif instruction[-1] == "rel": + idx += 1 + + if instruction[-1] == "rel": + if irq_type != 0: + raise RuntimeError("cannot use next/prev with rel") irq_type = 2 instruction.pop() assembled[-1] |= irq_type << 3 - num = int_in_range(instruction[-1], 0, 8, "irq index") + num = int_in_range(instruction[idx], 0, 8, "irq index") assembled[-1] |= num instruction.pop() - if len(instruction) > 1: # after rel has been removed - if instruction[-1] == "wait": - assembled[-1] |= 0x20 - elif instruction[-1] == "clear": - assembled[-1] |= 0x40 - # All other values are the default of set without waiting elif instruction[0] == "set": # instr delay dst data assembled.append(0b111_00000_000_00000) diff --git a/tests/test_version.py b/tests/test_version.py index c12711a..d2aa57f 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -115,9 +115,10 @@ def test_dot_set() -> None: def test_irq_v1() -> None: - assert_assembly_fails("irq 7 next") - assert_assembles_to(".pio_version 1\nirq 5 next", [0b110_00000_0_0_0_11_101]) - assert_assembles_to(".pio_version 1\nirq wait 1 prev", [0b110_00000_0_0_1_01_001]) + assert_assembly_fails("irq next 7") + assert_assembly_fails(".pio_version 1\nirq next 7 rel") + assert_assembles_to(".pio_version 1\nirq next 5", [0b110_00000_0_0_0_11_101]) + assert_assembles_to(".pio_version 1\nirq wait prev 1", [0b110_00000_0_0_1_01_001]) def test_mov_v1() -> None: @@ -125,8 +126,8 @@ def test_mov_v1() -> None: assert_assembly_fails(".pio_version 1\nmov osr, rxfifo[y]") prefix = ".pio_version 1\n.fifo putget\n" assert_assembly_fails(prefix + "mov osr, rxfifo[8]") - assert_assembles_to(prefix + "mov rxfifo[y], isr", [0b100_00000_0001_1_000]) - assert_assembles_to(prefix + "mov osr, rxfifo[1]", [0b100_00000_1001_0_001]) + assert_assembles_to(prefix + "mov rxfifo[y], isr", [0b100_00000_0001_0_000]) + assert_assembles_to(prefix + "mov osr, rxfifo[1]", [0b100_00000_1001_1_001]) assert_assembly_fails("mov pindirs, null", errtype=ValueError) assert_assembles_to(prefix + "mov pindirs, null", [0b101_00000_01100011]) @@ -134,12 +135,12 @@ def test_mov_v1() -> None: def test_wait_v1() -> None: assert_assembly_fails("wait 0 jmppin") - assert_assembly_fails("wait 0 irq 5 next") + assert_assembly_fails("wait 0 irq next 5") prefix = ".pio_version 1\n" assert_assembly_fails(prefix + "wait 0 jmppin +") assert_assembly_fails(prefix + "wait 0 jmppin + 7") assert_assembles_to(prefix + "wait 0 jmppin + 3", [0b001_00000_0_11_00011]) assert_assembles_to(prefix + "wait 1 jmppin", [0b001_00000_1_11_00000]) - assert_assembles_to(prefix + "wait 0 irq 5 next", [0b001_00000_0_10_11_101]) - assert_assembles_to(prefix + "wait 1 irq 4 prev", [0b001_00000_1_10_01_100]) + assert_assembles_to(prefix + "wait 0 irq next 5", [0b001_00000_0_10_11_101]) + assert_assembles_to(prefix + "wait 1 irq prev 4", [0b001_00000_1_10_01_100]) From ce01b8c10f450e2cec8d2f0916fccbfcbcf46468 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sun, 15 Sep 2024 14:30:54 -0500 Subject: [PATCH 27/38] add Program.from_file --- adafruit_pioasm.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 8ed564b..ad29fcb 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -502,6 +502,13 @@ def parse_rxfifo_brackets(arg, fifo_dir): self.debuginfo = (linemap, text_program) if build_debuginfo else None + @classmethod + def from_file(cls, filename: str, **kwargs) -> "Program": + """Assemble a PIO program in a file""" + with open(filename, "r", encoding="utf-8") as i: + program = i.read() + return cls(program, **kwargs) + def print_c_program(self, name: str, qualifier: str = "const") -> None: """Print the program into a C program snippet""" if self.debuginfo is None: From 19f5967fbd1deac1d259423737d2df511bc7ff26 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 18 Sep 2024 15:02:02 -0500 Subject: [PATCH 28/38] Remove call to never-defined function --- examples/pioasm_rotaryencoder.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/pioasm_rotaryencoder.py b/examples/pioasm_rotaryencoder.py index 842c526..c111ba6 100644 --- a/examples/pioasm_rotaryencoder.py +++ b/examples/pioasm_rotaryencoder.py @@ -110,8 +110,6 @@ def value(self): old_value = None while True: - gen() # pylint: disable=undefined-variable - value = encoder.value if old_value != value: print("Encoder:", value) From 8f52bea49da071e08cafc52e15856ecae7890434 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 18 Sep 2024 15:12:50 -0500 Subject: [PATCH 29/38] Rename this file to emphasize it's for generating test cases .. not part of the testsuite proper. --- {tests => tools}/make_all.py | 6 ++++++ 1 file changed, 6 insertions(+) rename {tests => tools}/make_all.py (96%) diff --git a/tests/make_all.py b/tools/make_all.py similarity index 96% rename from tests/make_all.py rename to tools/make_all.py index d3ca0da..e71cb97 100644 --- a/tests/make_all.py +++ b/tools/make_all.py @@ -2,6 +2,12 @@ # # SPDX-License-Identifier: MIT +""" +Generate test cases for adafruit_pioasm, with expected results from sdk pioasm +""" + +# pylint: disable=missing-function-docstring + import re from subprocess import check_output From 4d17d310df4d32c8b8a62bacc8b15738b04176a7 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 19 Sep 2024 11:53:46 -0500 Subject: [PATCH 30/38] Start documenting the PIO language subset supported by adafuit_pioasm this documentation could be more complete, but it's better than nothing. --- README.rst | 5 --- docs/index.rst | 6 +++ docs/syntax.rst | 82 +++++++++++++++++++++++++++++++++++++++++ docs/syntax.rst.license | 3 ++ 4 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 docs/syntax.rst create mode 100644 docs/syntax.rst.license diff --git a/README.rst b/README.rst index 67e7577..b7ae53a 100644 --- a/README.rst +++ b/README.rst @@ -55,11 +55,6 @@ To install in a virtual environment in your current project: source .venv/bin/activate pip3 install adafruit-circuitpython-pioasm -CircuitPython Extensions -======================== - -* ``.fifo auto``: By default, CircuitPython joins the TX and RX fifos if a PIO program only receives or transmits. The ``.fifo auto`` directive makes this explicit. - Usage Example ============= diff --git a/docs/index.rst b/docs/index.rst index da492bc..4e8cc91 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -14,6 +14,12 @@ Table of Contents examples +.. toctree:: + :caption: Syntax + :maxdepth: 3 + + syntax + .. toctree:: :caption: API Reference :maxdepth: 3 diff --git a/docs/syntax.rst b/docs/syntax.rst new file mode 100644 index 0000000..129162c --- /dev/null +++ b/docs/syntax.rst @@ -0,0 +1,82 @@ +This chapter documents the language features accepted by the `adafruit_pioasm` +assembler. The dialect is intended to be a compatible subset of the one in the +pico-sdk's ``pioasm`` program (which does not produce CircuitPython-compatible +output). + +For full details, refer to the relevant chapter in the RP2040 or RP2350 datasheet. + +In this informal grammar, ```` represent some text, usually a single +word or number. ``{curly brackets}`` represent an element that is optional. +``|`` represents alternatives. ``...`` indicates further arguments that are +explained in the official datasheet. + +Lines +~~~~~ + +First, any portion of the line starting with the comment character ``;`` is removed. +Then, extra whitespace is removed and the line is parsed. + +Each line may be: + * blank + * a directive + * a label + * an instruction, possibly with side-set and delay information + +Directives +---------- + + * ``.program ``: Accepts a program name, which should be a valid Python identifier + * ``.pio_version ``: The numeric version of the PIO peripheral to target. Version 0, the default, is in the RP2040. Version 1 is in RP2350 + * ``.origin ``: The required load address of the program. If specified and not ``-1``, this will be stored in ``pio_kwargs["offset"]`` + * ``.wrap``, ``.wrap_target``: This pair of directives set the range of instructions for implicit looping, by placing values in ``pio_kwargs`` + * ``.side_set {opt}``: Controls the side-set behavior and sets ``pio_kwargs["sideset_enable"]`` and ``pio_kwargs["sideset_pin_count"]`` + * ``.fifo ``: Sets the FIFO mode. As a CircuitPython extension, ``auto`` (the default) automatically chooses among ``txrx``, ``tx``, and ``rx`` modes + * ``.mov_status ...``: Controls what information the ``mov status`` instruction accesses, by placing values in ``pio_kwargs`` + * ``.out {{left|right}} {{auto}}``: Settings that control how the ``out`` instruction works, including shift direction and whether auto pull is enabled, by placing values in ``pio_kwargs`` + * ``.in {{left|right}} {{auto}}``: Settings that control how the ``in`` instruction works, including shift direction and whether auto push is enabled, by placing values in ``pio_kwargs`` + * ``.set ``: Settings that control how the ``set`` instruction works, including shift direction and whether auto push is enabled, by placing values in ``pio_kwargs`` + +Labels +------ + + * ``:`` creates a label which may be referred to by a ``jmp`` instruction. + +Instructions +------------ + + * ``nop`` + * ``jmp `` + * ``wait ...`` + * ``in ...`` + * ``out ...`` + * ``push ...`` + * ``pull ...`` + * ``mov ...`` + * ``irq ...`` + * ``set ...`` + +Side-set and delay +------------------ +The next part of each line can contain "side-set" and "delay" information, in order. + + * ``side ``: Set the side-set pins to ``number`` + * ``[]``: Add ``number`` extra delay cycles to this instruction + +The range of these numbers depends on the count of side-set pins and whether side-set is +optional. If side-set is not optional, a missing ``side `` is treated the same as +``side 0``. + +Unsupported Features +-------------------- + +In places where a numeric value is needed, only a valid Python numeric literal +is accepted. Arithmetic is not supported. + +Whitespace is not accepted in certain places, for instance within an instruction delay. +It must be written ``[7]`` not ``[ 7 ]``. + +Extra +CircuitPython extensions +------------------------ + +* ``.fifo auto``: By default, CircuitPython joins the TX and RX fifos if a PIO program only receives or transmits. The ``.fifo auto`` directive makes this explicit. diff --git a/docs/syntax.rst.license b/docs/syntax.rst.license new file mode 100644 index 0000000..6fd4abf --- /dev/null +++ b/docs/syntax.rst.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2024 Jeff Epler, written for Adafruit Industries + +SPDX-License-Identifier: MIT From 4f1bfde8589c3d70a616539c6dc4a3b050ee88ee Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 19 Sep 2024 12:49:01 -0500 Subject: [PATCH 31/38] Expand documentation of syntax a bit --- docs/syntax.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/syntax.rst b/docs/syntax.rst index 129162c..fd8462c 100644 --- a/docs/syntax.rst +++ b/docs/syntax.rst @@ -52,7 +52,10 @@ Instructions * ``push ...`` * ``pull ...`` * ``mov ...`` - * ``irq ...`` + * ``mov rxfifo[y|number], isr`` (requires PIO version 1 and compatible ``.fifo`` setting) + * ``mov osr, rxfifo[y|number]`` (requires PIO version 1 and compatible ``.fifo`` setting) + * ``irq {rel}`` + * ``irq next|prev ``. (requires PIO version 1) adafruit_pioasm follows sdk pioasm in placing ``next`` and ``prev`` before the IRQ number. The datasheet (version 05c4754) implies a different syntax. * ``set ...`` Side-set and delay @@ -75,7 +78,10 @@ is accepted. Arithmetic is not supported. Whitespace is not accepted in certain places, for instance within an instruction delay. It must be written ``[7]`` not ``[ 7 ]``. -Extra +Extra commas that would not be acceptable to sdk pioasm are not always diagnosed. + +Extra words in some locations that would not be acceptable to sdk pioasm are not always diagnosed. + CircuitPython extensions ------------------------ From b746aec7299f05955eb70f00318a4827f37a5676 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 19 Sep 2024 22:09:36 -0500 Subject: [PATCH 32/38] Add public_labels, test of labels Now, a label declared with `public foo:` will be exported in the `public_labels` property of `Program` objects. Additionally, a test of this feature as well as the existing duplicate label detection feature is added. Change the return type of `assemble` so that it better reflects reality Add docstrings for the public properties of Program objects --- adafruit_pioasm.py | 18 +++++++++++-- tests/test_label.py | 63 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 tests/test_label.py diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index ad29fcb..b92a8be 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -12,7 +12,7 @@ """ try: - from typing import List, MutableSequence + from typing import List, Sequence, Any except ImportError: pass @@ -55,12 +55,20 @@ class Program: # pylint: disable=too-few-public-methods """ + assembled: array.array + """The assembled PIO program instructions""" + public_labels: dict[str, int] + """The offset of any labels delcared public""" + pio_kwargs: dict[str, Any] + """Settings from assembler directives to pass to the StateMachine constructor""" + def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: """Converts pioasm text to encoded instruction bytes""" # pylint: disable=too-many-branches,too-many-statements,too-many-locals assembled: List[int] = [] program_name = None labels = {} + public_labels = {} linemap = [] instructions: List[str] = [] sideset_count = 0 @@ -219,6 +227,9 @@ def parse_rxfifo_brackets(arg, fifo_dir): elif line.endswith(":"): label = line[:-1] + if line.startswith("public "): + label = label[7:] + public_labels[label] = len(instructions) if label in labels: raise SyntaxError(f"Duplicate label {repr(label)}") labels[label] = len(instructions) @@ -227,6 +238,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): instructions.append(line) linemap.append(i) + mov_destinations: Sequence[str | None] if pio_version >= 1: mov_destinations = MOV_DESTINATIONS_V1 else: @@ -502,6 +514,8 @@ def parse_rxfifo_brackets(arg, fifo_dir): self.debuginfo = (linemap, text_program) if build_debuginfo else None + self.public_labels = public_labels + @classmethod def from_file(cls, filename: str, **kwargs) -> "Program": """Assemble a PIO program in a file""" @@ -557,7 +571,7 @@ def print_c_program(self, name: str, qualifier: str = "const") -> None: print() -def assemble(program_text: str) -> MutableSequence[int]: +def assemble(program_text: str) -> array.array: """Converts pioasm text to encoded instruction bytes In new code, prefer to use the `Program` class so that the extra arguments diff --git a/tests/test_label.py b/tests/test_label.py new file mode 100644 index 0000000..d9e9183 --- /dev/null +++ b/tests/test_label.py @@ -0,0 +1,63 @@ +# SPDX-FileCopyrightText: 2024 Jeff Epler for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +Tests out +""" + +from pytest_helpers import assert_assembly_fails +import adafruit_pioasm + + +def test_label() -> None: + source = [ + " jmp label1", + "label1:", + " jmp label2", + "public label2:", + " nop", + ] + program = adafruit_pioasm.Program("\n".join(source)) + assert program.public_labels == {"label2": 2} + + # Test each combination of public/privagte label duplication + source = [ + "label1:\n", + "nop\n", + "public label1:\n", + "nop\n", + ] + assert_assembly_fails( + "\n".join(source), match="Duplicate label", errtype=SyntaxError + ) + + source = [ + "label1:\n", + " nop\n", + "label1:\n", + " nop\n", + ] + assert_assembly_fails( + "\n".join(source), match="Duplicate label", errtype=SyntaxError + ) + + source = [ + "public label1:\n", + " nop\n", + "label1:\n", + " nop\n", + ] + assert_assembly_fails( + "\n".join(source), match="Duplicate label", errtype=SyntaxError + ) + + source = [ + "public label1:\n", + " nop\n", + "public label1:\n", + " nop\n", + ] + assert_assembly_fails( + "\n".join(source), match="Duplicate label", errtype=SyntaxError + ) From d6dcfab4781bebb897b63f7c1ccc5e62e5cb52c8 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 24 Sep 2024 16:58:29 -0500 Subject: [PATCH 33/38] Update adafruit_pioasm.py Co-authored-by: Scott Shawcroft --- adafruit_pioasm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index b92a8be..2a4df32 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -58,7 +58,7 @@ class Program: # pylint: disable=too-few-public-methods assembled: array.array """The assembled PIO program instructions""" public_labels: dict[str, int] - """The offset of any labels delcared public""" + """The offset of any labels declared public""" pio_kwargs: dict[str, Any] """Settings from assembler directives to pass to the StateMachine constructor""" From de37174dbcf92a1ef5afa2bb5b56b06ff88eac51 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Mon, 7 Oct 2024 09:24:05 -0500 Subject: [PATCH 34/38] remove deprecated get_html_theme_path() call Signed-off-by: foamyguy --- docs/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index dbc6149..f730eb7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -113,7 +113,6 @@ import sphinx_rtd_theme html_theme = "sphinx_rtd_theme" -html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, From 23bd1b13e75f0b0f15c0e63a798414a6a5b215fd Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sun, 1 Dec 2024 11:11:09 -0600 Subject: [PATCH 35/38] Add support for `.side_set ... pindirs` This also requires a core change to set the respective bit in the PIO hardware. --- adafruit_pioasm.py | 4 ++++ tests/test_pseudo.py | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 2a4df32..f1484a3 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -73,6 +73,7 @@ def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: instructions: List[str] = [] sideset_count = 0 sideset_enable = 0 + sideset_pindirs = False wrap = None wrap_target = None offset = -1 @@ -144,6 +145,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): elif line.startswith(".side_set"): sideset_count = int(line.split()[1], 0) sideset_enable = "opt" in line + sideset_pindirs = "pindirs" in line elif line.startswith(".fifo"): require_before_instruction() fifo_type = line.split()[1] @@ -470,6 +472,8 @@ def parse_rxfifo_brackets(arg, fifo_dir): if sideset_count != 0: self.pio_kwargs["sideset_pin_count"] = sideset_count + if sideset_pindirs: + self.pio_kwargs["sideset_pindirs"] = sideset_pindirs if wrap is not None: self.pio_kwargs["wrap"] = wrap diff --git a/tests/test_pseudo.py b/tests/test_pseudo.py index 1bf89f8..a5049ec 100644 --- a/tests/test_pseudo.py +++ b/tests/test_pseudo.py @@ -12,3 +12,20 @@ def test_offset() -> None: assert_pio_kwargs(".origin 7", offset=7, sideset_enable=False) assert_assembly_fails("nop\n.origin 7") + + +def test_sideset_pindirs() -> None: + assert_pio_kwargs( + ".side_set 2 opt pindirs", + sideset_pin_count=2, + sideset_enable=True, + sideset_pindirs=True, + ) + assert_pio_kwargs( + ".side_set 2 pindirs", + sideset_pin_count=2, + sideset_enable=False, + sideset_pindirs=True, + ) + # Setting not emitted (as =False) for backwards compat + assert_pio_kwargs(".side_set 2", sideset_pin_count=2, sideset_enable=False) From 00cb534d033d9f4571a1649450858bb9edac37a9 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 14 Jan 2025 11:32:34 -0600 Subject: [PATCH 36/38] add sphinx configuration to rtd.yaml Signed-off-by: foamyguy --- .readthedocs.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 33c2a61..88bca9f 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -8,6 +8,9 @@ # Required version: 2 +sphinx: + configuration: docs/conf.py + build: os: ubuntu-20.04 tools: From 649526118b904c54ae0f1a6626faae479cef7e83 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 23 Jan 2025 16:13:12 -0600 Subject: [PATCH 37/38] Avoid printing `True` in C program & test it! Closes: #74 --- adafruit_pioasm.py | 2 +- tests/test_print_c_program.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 tests/test_print_c_program.py diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index f1484a3..dba68ba 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -545,7 +545,7 @@ def print_c_program(self, name: str, qualifier: str = "const") -> None: sideset_pin_count = self.pio_kwargs.get("sideset_pin_count", 0) print(f"{qualifier} int {name}_sideset_pin_count = {sideset_pin_count};") print( - f"{qualifier} bool {name}_sideset_enable = {self.pio_kwargs['sideset_enable']};" + f"{qualifier} bool {name}_sideset_enable = {+self.pio_kwargs['sideset_enable']};" ) print(f"{qualifier} uint16_t {name}[] = " + "{") last_line = 0 diff --git a/tests/test_print_c_program.py b/tests/test_print_c_program.py new file mode 100644 index 0000000..f42dd66 --- /dev/null +++ b/tests/test_print_c_program.py @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2025 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +import contextlib +import io + +import adafruit_pioasm + + +def test_print_c_program(): + output = io.StringIO() + with contextlib.redirect_stdout(output): + adafruit_pioasm.Program(".side_set 1 opt").print_c_program("mood") + c_program = output.getvalue() + assert "True" not in c_program + assert "sideset_enable = 1" in c_program From 1539f0a96ef8df1fa5580ec3976d5174f9ec4fc4 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Fri, 16 May 2025 18:31:52 +0000 Subject: [PATCH 38/38] change to ruff --- .gitattributes | 11 + .pre-commit-config.yaml | 43 +-- .pylintrc | 399 ---------------------------- README.rst | 6 +- adafruit_pioasm.py | 65 ++--- docs/api.rst | 3 + docs/conf.py | 8 +- examples/pioasm_7seg.py | 2 + examples/pioasm_7seg_fader.py | 18 +- examples/pioasm_background_morse.py | 4 +- examples/pioasm_blink.py | 2 + examples/pioasm_hello.py | 2 + examples/pioasm_i2s_codec.py | 3 +- examples/pioasm_i2sout.py | 2 + examples/pioasm_led_brightness.py | 14 +- examples/pioasm_neopixel.py | 4 +- examples/pioasm_neopixel_bg.py | 10 +- examples/pioasm_pdm.py | 2 + examples/pioasm_pulsegroup.py | 13 +- examples/pioasm_rotaryencoder.py | 4 +- examples/pioasm_rp2350_fifo.py | 2 + examples/pioasm_rxuart.py | 9 +- examples/pioasm_simpletest.py | 4 +- examples/pioasm_txuart.py | 1 + ruff.toml | 112 ++++++++ tests/all_pio_instructions.py | 1 - tests/test_all.py | 3 +- tests/test_in.py | 8 +- tests/test_label.py | 17 +- tests/test_misc.py | 8 +- tests/test_mov.py | 4 +- tests/test_nop.py | 4 +- tests/test_out.py | 8 +- tests/test_pseudo.py | 2 +- tests/test_version.py | 2 +- tools/make_all.py | 11 +- 36 files changed, 234 insertions(+), 577 deletions(-) create mode 100644 .gitattributes delete mode 100644 .pylintrc create mode 100644 ruff.toml diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..21c125c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +.py text eol=lf +.rst text eol=lf +.txt text eol=lf +.yaml text eol=lf +.toml text eol=lf +.license text eol=lf +.md text eol=lf diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 70ade69..ff19dde 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,42 +1,21 @@ -# SPDX-FileCopyrightText: 2020 Diego Elio Pettenò +# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries # # SPDX-License-Identifier: Unlicense repos: - - repo: https://github.com/python/black - rev: 23.3.0 - hooks: - - id: black - - repo: https://github.com/fsfe/reuse-tool - rev: v1.1.2 - hooks: - - id: reuse - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace - - repo: https://github.com/pycqa/pylint - rev: v2.17.4 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.3.4 hooks: - - id: pylint - name: pylint (library code) - types: [python] - args: - - --disable=consider-using-f-string - exclude: "^(docs/|examples/|tests/|setup.py$)" - - id: pylint - name: pylint (example code) - description: Run pylint rules on "examples/*.py" files - types: [python] - files: "^examples/" - args: - - --disable=missing-docstring,invalid-name,consider-using-f-string,duplicate-code - - id: pylint - name: pylint (test code) - description: Run pylint rules on "tests/*.py" files - types: [python] - files: "^tests/" - args: - - --disable=missing-docstring,consider-using-f-string,duplicate-code + - id: ruff-format + - id: ruff + args: ["--fix"] + - repo: https://github.com/fsfe/reuse-tool + rev: v3.0.1 + hooks: + - id: reuse diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index f945e92..0000000 --- a/.pylintrc +++ /dev/null @@ -1,399 +0,0 @@ -# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries -# -# SPDX-License-Identifier: Unlicense - -[MASTER] - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code -extension-pkg-whitelist= - -# Add files or directories to the ignore-list. They should be base names, not -# paths. -ignore=CVS - -# Add files or directories matching the regex patterns to the ignore-list. The -# regex matches against base names, not paths. -ignore-patterns= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Use multiple processes to speed up Pylint. -jobs=1 - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins=pylint.extensions.no_self_use - -# Pickle collected data for later comparisons. -persistent=yes - -# Specify a configuration file. -#rcfile= - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED -confidence= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" -# disable=import-error,raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,deprecated-str-translate-call -disable=raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,import-error,pointless-string-statement,unspecified-encoding - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -enable= - - -[REPORTS] - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details -#msg-template= - -# Set the output format. Available formats are text, parseable, colorized, json -# and msvs (visual studio).You can also give a reporter class, eg -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Tells whether to display a full report or only the messages -reports=no - -# Activate the evaluation score. -score=yes - - -[REFACTORING] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=5 - - -[LOGGING] - -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging - - -[SPELLING] - -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -# notes=FIXME,XXX,TODO -notes=FIXME,XXX - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# This flag controls whether pylint should warn about no-member and similar -# checks whenever an opaque object is returned when inferring. The inference -# can return multiple potential results while evaluating a Python object, but -# some branches might not be evaluated, which results in partial inference. In -# that case, it might be useful to still emit no-member and other checks for -# the rest of the inferred objects. -ignore-on-opaque-inference=yes - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules=board - -# Show a hint with possible names when a member name was not found. The aspect -# of finding the hint is based on edit distance. -missing-member-hint=yes - -# The minimum edit distance a name should have in order to be considered a -# similar match for a missing member name. -missing-member-hint-distance=1 - -# The total number of similar names that should be taken in consideration when -# showing a hint for a missing member. -missing-member-max-choices=1 - - -[VARIABLES] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - -# Tells whether unused global variables should be treated as a violation. -allow-global-unused-variables=yes - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_,_cb - -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.*|^ignored_|^unused_ - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six.moves,future.builtins - - -[FORMAT] - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -# expected-line-ending-format= -expected-line-ending-format=LF - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Maximum number of characters on a single line. -max-line-length=100 - -# Maximum number of lines in a module -max-module-lines=1000 - -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - - -[SIMILARITIES] - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=yes - -# Minimum lines number of a similarity. -min-similarity-lines=12 - - -[BASIC] - -# Regular expression matching correct argument names -argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct attribute names -attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# Regular expression matching correct class attribute names -class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Regular expression matching correct class names -# class-rgx=[A-Z_][a-zA-Z0-9]+$ -class-rgx=[A-Z_][a-zA-Z0-9_]+$ - -# Regular expression matching correct constant names -const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - -# Regular expression matching correct function names -function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Good variable names which should always be accepted, separated by a comma -# good-names=i,j,k,ex,Run,_ -good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_ - -# Include a hint for the correct naming format with invalid-name -include-naming-hint=no - -# Regular expression matching correct inline iteration names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Regular expression matching correct method names -method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_ - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -property-classes=abc.abstractproperty - -# Regular expression matching correct variable names -variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - - -[IMPORTS] - -# Allow wildcard imports from modules that define __all__. -allow-wildcard-with-all=no - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=optparse,tkinter.tix - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict,_fields,_replace,_source,_make - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs - - -[DESIGN] - -# Maximum number of arguments for function / method -max-args=5 - -# Maximum number of attributes for a class (see R0902). -# max-attributes=7 -max-attributes=11 - -# Maximum number of boolean expressions in a if statement -max-bool-expr=5 - -# Maximum number of branch for function / method body -max-branches=12 - -# Maximum number of locals for function / method body -max-locals=15 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - -# Maximum number of return / yield for function / method body -max-returns=6 - -# Maximum number of statements in function / method body -max-statements=50 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=1 - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=builtins.Exception diff --git a/README.rst b/README.rst index b7ae53a..06123d2 100644 --- a/README.rst +++ b/README.rst @@ -13,9 +13,9 @@ Introduction :target: https://github.com/adafruit/Adafruit_CircuitPython_PIOASM/actions :alt: Build Status -.. image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://github.com/psf/black - :alt: Code Style: Black +.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json + :target: https://github.com/astral-sh/ruff + :alt: Code Style: Ruff Simple assembler to convert pioasm to bytes diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index dba68ba..ea969b2 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -12,7 +12,7 @@ """ try: - from typing import List, Sequence, Any + from typing import Any, List, Sequence except ImportError: pass @@ -45,7 +45,7 @@ } -class Program: # pylint: disable=too-few-public-methods +class Program: """Encapsulates a program's instruction stream and configuration flags Example:: @@ -64,7 +64,6 @@ class Program: # pylint: disable=too-few-public-methods def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: """Converts pioasm text to encoded instruction bytes""" - # pylint: disable=too-many-branches,too-many-statements,too-many-locals assembled: List[int] = [] program_name = None labels = {} @@ -97,23 +96,17 @@ def require_before_instruction(): def require_version(required_version, instruction): if pio_version < required_version: - raise RuntimeError( - f"{instruction} requires .pio_version {required_version}" - ) + raise RuntimeError(f"{instruction} requires .pio_version {required_version}") def int_in_range(arg, low, high, what, radix=0): result = int(arg, radix) if low <= result < high: return result - raise RuntimeError( - f"{what} must be at least {low} and less than {high}, got {result}" - ) + raise RuntimeError(f"{what} must be at least {low} and less than {high}, got {result}") def parse_rxfifo_brackets(arg, fifo_dir): require_version(1, line) - if ( # pylint: disable=consider-using-in - fifo_type != "putget" and fifo_type != fifo_dir - ): + if fifo_type != "putget" and fifo_type != fifo_dir: raise RuntimeError( f"FIFO must be configured for '{fifo_dir}' or 'putget' for {line}" ) @@ -158,7 +151,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): required_version = 0 mov_status_n = 0 mov_status_type = words[1] - if words[1] in ("txfifo", "rxfifo"): + if words[1] in {"txfifo", "rxfifo"}: if words[2] != "<": raise RuntimeError(f"Invalid {line}") mov_status_n = int_in_range(words[3], 0, 32, words[1]) @@ -200,9 +193,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): elif words[0] == ".in": require_before_instruction() - in_count = int_in_range( - words[1], 32 if pio_version == 0 else 1, 33, ".in count" - ) + in_count = int_in_range(words[1], 32 if pio_version == 0 else 1, 33, ".in count") auto_push = False idx = 2 @@ -223,9 +214,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): elif words[0] == ".set": require_before_instruction() - set_count = int_in_range( - words[1], 5 if pio_version == 0 else 1, 6, ".set count" - ) + set_count = int_in_range(words[1], 5 if pio_version == 0 else 1, 6, ".set count") elif line.endswith(":"): label = line[:-1] @@ -248,7 +237,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): max_delay = 2 ** (5 - sideset_count - sideset_enable) - 1 assembled = [] - for line in instructions: # pylint: disable=too-many-nested-blocks + for line in instructions: instruction = splitter(line.strip()) delay = 0 if ( @@ -291,9 +280,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): try: assembled[-1] |= CONDITIONS.index(instruction[1]) << 5 except ValueError as exc: - raise ValueError( - f"Invalid jmp condition '{instruction[1]}'" - ) from exc + raise ValueError(f"Invalid jmp condition '{instruction[1]}'") from exc elif instruction[0] == "wait": # instr delay p sr index @@ -334,9 +321,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): assembled[-1] |= 0b10000 else: limit = 32 - num = int_in_range( - instruction[idx], 0, limit, "wait {instruction[2]}" - ) + num = int_in_range(instruction[idx], 0, limit, "wait {instruction[2]}") assembled[-1] |= num elif instruction[0] == "in": @@ -358,9 +343,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): try: assembled[-1] |= OUT_DESTINATIONS.index(destination) << 5 except ValueError as exc: - raise ValueError( - f"Invalid out destination '{destination}'" - ) from exc + raise ValueError(f"Invalid out destination '{destination}'") from exc count = int(instruction[-1], 0) if not 1 <= count <= 32: raise RuntimeError("Count out of range") @@ -372,7 +355,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): assembled[-1] |= 0x80 if instruction[-1] == "block" or not instruction[-1].endswith("block"): assembled[-1] |= 0x20 - if len(instruction) > 1 and instruction[1] in ("ifempty", "iffull"): + if len(instruction) > 1 and instruction[1] in {"ifempty", "iffull"}: assembled[-1] |= 0x40 elif instruction[0] == "mov": # instr delay dst op src @@ -448,9 +431,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): try: assembled[-1] |= SET_DESTINATIONS.index(instruction[1]) << 5 except ValueError as exc: - raise ValueError( - f"Invalid set destination '{instruction[1]}'" - ) from exc + raise ValueError(f"Invalid set destination '{instruction[1]}'") from exc value = int(instruction[-1], 0) if not 0 <= value <= 31: raise RuntimeError("Set value out of range") @@ -490,7 +471,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): if set_count is not None: self.pio_kwargs["set_pin_count"] = set_count - if out_count not in (None, 32): + if out_count not in {None, 32}: self.pio_kwargs["out_pin_count"] = out_count if out_shift_right is not None: @@ -502,7 +483,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): if pull_threshold is not None: self.pio_kwargs["pull_threshold"] = pull_threshold - if in_count not in (None, 32): + if in_count not in {None, 32}: self.pio_kwargs["in_pin_count"] = in_count if in_shift_right is not None: @@ -523,7 +504,7 @@ def parse_rxfifo_brackets(arg, fifo_dir): @classmethod def from_file(cls, filename: str, **kwargs) -> "Program": """Assemble a PIO program in a file""" - with open(filename, "r", encoding="utf-8") as i: + with open(filename, encoding="utf-8") as i: program = i.read() return cls(program, **kwargs) @@ -539,14 +520,10 @@ def print_c_program(self, name: str, qualifier: str = "const") -> None: print( f"{qualifier} int {name}_wrap = {self.pio_kwargs.get('wrap', len(self.assembled)-1)};" ) - print( - f"{qualifier} int {name}_wrap_target = {self.pio_kwargs.get('wrap_target', 0)};" - ) + print(f"{qualifier} int {name}_wrap_target = {self.pio_kwargs.get('wrap_target', 0)};") sideset_pin_count = self.pio_kwargs.get("sideset_pin_count", 0) print(f"{qualifier} int {name}_sideset_pin_count = {sideset_pin_count};") - print( - f"{qualifier} bool {name}_sideset_enable = {+self.pio_kwargs['sideset_enable']};" - ) + print(f"{qualifier} bool {name}_sideset_enable = {+self.pio_kwargs['sideset_enable']};") print(f"{qualifier} uint16_t {name}[] = " + "{") last_line = 0 if linemap: @@ -568,9 +545,7 @@ def print_c_program(self, name: str, qualifier: str = "const") -> None: last_line += 1 else: for i in range(0, len(self.assembled), 8): - print( - " " + ", ".join("0x%04x" % i for i in self.assembled[i : i + 8]) - ) + print(" " + ", ".join("0x%04x" % i for i in self.assembled[i : i + 8])) print("};") print() diff --git a/docs/api.rst b/docs/api.rst index bf97b0e..daa885a 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -4,5 +4,8 @@ .. If your library file(s) are nested in a directory (e.g. /adafruit_foo/foo.py) .. use this format as the module name: "adafruit_foo.foo" +API Reference +############# + .. automodule:: adafruit_pioasm :members: diff --git a/docs/conf.py b/docs/conf.py index f730eb7..6cc92af 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,12 +1,10 @@ -# -*- coding: utf-8 -*- - # SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries # # SPDX-License-Identifier: MIT +import datetime import os import sys -import datetime sys.path.insert(0, os.path.abspath("..")) @@ -51,9 +49,7 @@ creation_year = "2021" current_year = str(datetime.datetime.now().year) year_duration = ( - current_year - if current_year == creation_year - else creation_year + " - " + current_year + current_year if current_year == creation_year else creation_year + " - " + current_year ) copyright = year_duration + " Scott Shawcroft" author = "Scott Shawcroft" diff --git a/examples/pioasm_7seg.py b/examples/pioasm_7seg.py index 1961381..f8a7def 100644 --- a/examples/pioasm_7seg.py +++ b/examples/pioasm_7seg.py @@ -39,8 +39,10 @@ import array import time + import board import rp2pio + import adafruit_pioasm _program = adafruit_pioasm.Program( diff --git a/examples/pioasm_7seg_fader.py b/examples/pioasm_7seg_fader.py index f6d5117..2acd4d9 100644 --- a/examples/pioasm_7seg_fader.py +++ b/examples/pioasm_7seg_fader.py @@ -36,12 +36,14 @@ * Pico GP16 to LED matrix 14 (COM1) """ +import array import asyncio import random -import array + import board import rp2pio from ulab import numpy as np + import adafruit_pioasm _pio_source = """ @@ -130,20 +132,16 @@ def make_digit_wt(v): class LedFader: - def __init__( - self, first_pin, pin_count, cathode_weights, anode_weights, levels=64 - ): # pylint: disable=too-many-arguments + def __init__(self, first_pin, pin_count, cathode_weights, anode_weights, levels=64): self._cathode_weights = cathode_weights self._anode_weights = anode_weights - self._stream = array.array("L", [0, 0, 1]) * ( - 1 + len(cathode_weights) * len(anode_weights) - ) + self._stream = array.array("L", [0, 0, 1]) * (1 + len(cathode_weights) * len(anode_weights)) self._levels = levels self._max_count = levels * len(self) self._total = len(self) program = adafruit_pioasm.Program(_pio_source.format(n=pin_count)) - self._sm = rp2pio.StateMachine( # pylint: disable=too-many-arguments + self._sm = rp2pio.StateMachine( program.assembled, frequency=125_000_000, first_out_pin=first_pin, @@ -152,9 +150,7 @@ def __init__( pull_threshold=14, **program.pio_kwargs, ) - print( - f"Note: approximate refresh rate {self._sm.frequency / self._max_count:.0f}Hz" - ) + print(f"Note: approximate refresh rate {self._sm.frequency / self._max_count:.0f}Hz") self._sm.background_write(loop=self._stream) def __enter__(self): diff --git a/examples/pioasm_background_morse.py b/examples/pioasm_background_morse.py index d3dcdc9..fe8129f 100644 --- a/examples/pioasm_background_morse.py +++ b/examples/pioasm_background_morse.py @@ -19,8 +19,10 @@ import array import time + from board import LED from rp2pio import StateMachine + from adafruit_pioasm import Program # This program turns the LED on or off depending on the first bit of the value, @@ -73,7 +75,7 @@ ) # To simply repeat 'TEST' forever, change to 'if True': -if False: # pylint: disable=using-constant-test +if False: print("Sending out TEST forever", end="") sm.background_write(loop=TEST) while True: diff --git a/examples/pioasm_blink.py b/examples/pioasm_blink.py index 3d2fc97..7b2302b 100644 --- a/examples/pioasm_blink.py +++ b/examples/pioasm_blink.py @@ -6,8 +6,10 @@ import array import time + import board import rp2pio + import adafruit_pioasm blink = adafruit_pioasm.assemble( diff --git a/examples/pioasm_hello.py b/examples/pioasm_hello.py index ade87ad..7796253 100644 --- a/examples/pioasm_hello.py +++ b/examples/pioasm_hello.py @@ -5,8 +5,10 @@ # Adapted from the example https://github.com/raspberrypi/pico-examples/tree/master/pio/hello_pio import time + import board import rp2pio + import adafruit_pioasm hello = """ diff --git a/examples/pioasm_i2s_codec.py b/examples/pioasm_i2s_codec.py index 4fda44c..f102e13 100644 --- a/examples/pioasm_i2s_codec.py +++ b/examples/pioasm_i2s_codec.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: MIT import array + import board import rp2pio @@ -86,7 +87,7 @@ # read: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 -def i2s_codec( # pylint: disable=too-many-arguments +def i2s_codec( channels=2, sample_rate=48000, bits=16, diff --git a/examples/pioasm_i2sout.py b/examples/pioasm_i2sout.py index e57f69f..5929fc4 100644 --- a/examples/pioasm_i2sout.py +++ b/examples/pioasm_i2sout.py @@ -5,9 +5,11 @@ import array import math import time + import board import digitalio import rp2pio + import adafruit_pioasm trigger = digitalio.DigitalInOut(board.D4) diff --git a/examples/pioasm_led_brightness.py b/examples/pioasm_led_brightness.py index 71ba925..005ba3b 100644 --- a/examples/pioasm_led_brightness.py +++ b/examples/pioasm_led_brightness.py @@ -5,8 +5,10 @@ # Adapted from the an example in Appendix C of RPi_PiPico_Digital_v10.pdf import time + import board import rp2pio + import adafruit_pioasm led_quarter_brightness = adafruit_pioasm.assemble( @@ -30,20 +32,14 @@ ) while True: - sm = rp2pio.StateMachine( - led_quarter_brightness, frequency=10000, first_set_pin=board.LED - ) + sm = rp2pio.StateMachine(led_quarter_brightness, frequency=10000, first_set_pin=board.LED) time.sleep(1) sm.deinit() - sm = rp2pio.StateMachine( - led_half_brightness, frequency=10000, first_set_pin=board.LED - ) + sm = rp2pio.StateMachine(led_half_brightness, frequency=10000, first_set_pin=board.LED) time.sleep(1) sm.deinit() - sm = rp2pio.StateMachine( - led_full_brightness, frequency=10000, first_set_pin=board.LED - ) + sm = rp2pio.StateMachine(led_full_brightness, frequency=10000, first_set_pin=board.LED) time.sleep(1) sm.deinit() diff --git a/examples/pioasm_neopixel.py b/examples/pioasm_neopixel.py index 0c043d5..701df86 100644 --- a/examples/pioasm_neopixel.py +++ b/examples/pioasm_neopixel.py @@ -3,9 +3,11 @@ # SPDX-License-Identifier: MIT import time -import rp2pio + import board import microcontroller +import rp2pio + import adafruit_pioasm # NeoPixels are 800khz bit streams. We are choosing zeros as <312ns hi, 936 lo> diff --git a/examples/pioasm_neopixel_bg.py b/examples/pioasm_neopixel_bg.py index 19c3948..c9e7522 100644 --- a/examples/pioasm_neopixel_bg.py +++ b/examples/pioasm_neopixel_bg.py @@ -23,8 +23,10 @@ """ import struct + import adafruit_pixelbuf from rp2pio import StateMachine + from adafruit_pioasm import Program # Pixel color order constants @@ -66,12 +68,8 @@ ) -class NeoPixelBackground( # pylint: disable=too-few-public-methods - adafruit_pixelbuf.PixelBuf -): - def __init__( - self, pin, n, *, bpp=3, brightness=1.0, auto_write=True, pixel_order=None - ): +class NeoPixelBackground(adafruit_pixelbuf.PixelBuf): + def __init__(self, pin, n, *, bpp=3, brightness=1.0, auto_write=True, pixel_order=None): if not pixel_order: pixel_order = GRB if bpp == 3 else GRBW elif isinstance(pixel_order, tuple): diff --git a/examples/pioasm_pdm.py b/examples/pioasm_pdm.py index 852d33b..61acfc8 100644 --- a/examples/pioasm_pdm.py +++ b/examples/pioasm_pdm.py @@ -4,9 +4,11 @@ import array import time + import board import digitalio import rp2pio + import adafruit_pioasm trigger = digitalio.DigitalInOut(board.D4) diff --git a/examples/pioasm_pulsegroup.py b/examples/pioasm_pulsegroup.py index 753ac13..30a58a4 100644 --- a/examples/pioasm_pulsegroup.py +++ b/examples/pioasm_pulsegroup.py @@ -8,13 +8,12 @@ import array +import adafruit_ticks import board import rp2pio -import adafruit_ticks import ulab.numpy as np from adafruit_motor import servo - import adafruit_pioasm _cycle_count = 3 @@ -73,7 +72,7 @@ def phase(self, phase): def _recalculate(self): self._turn_on = self._get_turn_on() self._turn_off = self._get_turn_off() - self._group._maybe_update() # pylint: disable=protected-access + self._group._maybe_update() def _get_turn_on(self): maxval = self._maxval @@ -104,7 +103,7 @@ def __init__( maxval=65535, stagger=False, auto_update=True, - ): # pylint: disable=too-many-arguments + ): """Create a pulse group with the given characteristics""" self._frequency = round(1 / period) pio_frequency = round((1 + maxval) * _cycle_count / period) @@ -149,9 +148,9 @@ def update(self): changes = {0: [0, 0]} for i in self._items: - turn_on = i._turn_on # pylint: disable=protected-access - turn_off = i._turn_off # pylint: disable=protected-access - mask = i._mask # pylint: disable=protected-access + turn_on = i._turn_on + turn_off = i._turn_off + mask = i._mask if turn_on is not None: this_change = changes.get(turn_on) diff --git a/examples/pioasm_rotaryencoder.py b/examples/pioasm_rotaryencoder.py index c111ba6..d8ce591 100644 --- a/examples/pioasm_rotaryencoder.py +++ b/examples/pioasm_rotaryencoder.py @@ -6,8 +6,10 @@ # https://github.com/micropython/micropython/pull/6894/files import array -import rp2pio + import board +import rp2pio + import adafruit_pioasm diff --git a/examples/pioasm_rp2350_fifo.py b/examples/pioasm_rp2350_fifo.py index f6dda86..d4d2fd6 100644 --- a/examples/pioasm_rp2350_fifo.py +++ b/examples/pioasm_rp2350_fifo.py @@ -16,8 +16,10 @@ import array import time + import board import rp2pio + import adafruit_pioasm program = adafruit_pioasm.Program( diff --git a/examples/pioasm_rxuart.py b/examples/pioasm_rxuart.py index 280c0bc..5675522 100644 --- a/examples/pioasm_rxuart.py +++ b/examples/pioasm_rxuart.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: MIT import rp2pio + import adafruit_pioasm code = adafruit_pioasm.assemble( @@ -43,8 +44,8 @@ def baudrate(self): return self.pio.frequency // 8 @baudrate.setter - def baudrate(self, frequency): # pylint: disable=unused-argument - self.pio.frequency = freqency * 8 # pylint: disable=undefined-variable + def baudrate(self, frequency): + self.pio.frequency = freqency * 8 @property def in_waiting(self): @@ -55,5 +56,5 @@ def read(self, n): n = self.pio.readinto(b) return b[:n] - def readinto(self, buf): # pylint: disable=unused-argument - return self.pio.readinto(n) # pylint: disable=undefined-variable + def readinto(self, buf): + return self.pio.readinto(n) diff --git a/examples/pioasm_simpletest.py b/examples/pioasm_simpletest.py index ac3694c..afc03bd 100644 --- a/examples/pioasm_simpletest.py +++ b/examples/pioasm_simpletest.py @@ -3,8 +3,10 @@ # SPDX-License-Identifier: MIT import time -import rp2pio + import board +import rp2pio + import adafruit_pioasm squarewave = """ diff --git a/examples/pioasm_txuart.py b/examples/pioasm_txuart.py index e3382ee..6ab8203 100644 --- a/examples/pioasm_txuart.py +++ b/examples/pioasm_txuart.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: MIT import rp2pio + import adafruit_pioasm code = adafruit_pioasm.Program( diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..d7b1f3e --- /dev/null +++ b/ruff.toml @@ -0,0 +1,112 @@ +# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +target-version = "py38" +line-length = 100 + +[lint] +preview = true +select = ["I", "PL", "UP"] + +extend-select = [ + "D419", # empty-docstring + "E501", # line-too-long + "W291", # trailing-whitespace + "PLC0414", # useless-import-alias + "PLC2401", # non-ascii-name + "PLC2801", # unnecessary-dunder-call + "PLC3002", # unnecessary-direct-lambda-call + "E999", # syntax-error + "PLE0101", # return-in-init + "F706", # return-outside-function + "F704", # yield-outside-function + "PLE0116", # continue-in-finally + "PLE0117", # nonlocal-without-binding + "PLE0241", # duplicate-bases + "PLE0302", # unexpected-special-method-signature + "PLE0604", # invalid-all-object + "PLE0605", # invalid-all-format + "PLE0643", # potential-index-error + "PLE0704", # misplaced-bare-raise + "PLE1141", # dict-iter-missing-items + "PLE1142", # await-outside-async + "PLE1205", # logging-too-many-args + "PLE1206", # logging-too-few-args + "PLE1307", # bad-string-format-type + "PLE1310", # bad-str-strip-call + "PLE1507", # invalid-envvar-value + "PLE2502", # bidirectional-unicode + "PLE2510", # invalid-character-backspace + "PLE2512", # invalid-character-sub + "PLE2513", # invalid-character-esc + "PLE2514", # invalid-character-nul + "PLE2515", # invalid-character-zero-width-space + "PLR0124", # comparison-with-itself + "PLR0202", # no-classmethod-decorator + "PLR0203", # no-staticmethod-decorator + "UP004", # useless-object-inheritance + "PLR0206", # property-with-parameters + "PLR0904", # too-many-public-methods + "PLR0911", # too-many-return-statements + "PLR0912", # too-many-branches + "PLR0913", # too-many-arguments + "PLR0914", # too-many-locals + "PLR0915", # too-many-statements + "PLR0916", # too-many-boolean-expressions + "PLR1702", # too-many-nested-blocks + "PLR1704", # redefined-argument-from-local + "PLR1711", # useless-return + "C416", # unnecessary-comprehension + "PLR1733", # unnecessary-dict-index-lookup + "PLR1736", # unnecessary-list-index-lookup + + # ruff reports this rule is unstable + #"PLR6301", # no-self-use + + "PLW0108", # unnecessary-lambda + "PLW0120", # useless-else-on-loop + "PLW0127", # self-assigning-variable + "PLW0129", # assert-on-string-literal + "B033", # duplicate-value + "PLW0131", # named-expr-without-context + "PLW0245", # super-without-brackets + "PLW0406", # import-self + "PLW0602", # global-variable-not-assigned + "PLW0603", # global-statement + "PLW0604", # global-at-module-level + + # fails on the try: import typing used by libraries + #"F401", # unused-import + + "F841", # unused-variable + "E722", # bare-except + "PLW0711", # binary-op-exception + "PLW1501", # bad-open-mode + "PLW1508", # invalid-envvar-default + "PLW1509", # subprocess-popen-preexec-fn + "PLW2101", # useless-with-lock + "PLW3301", # nested-min-max +] + +ignore = [ + "PLR2004", # magic-value-comparison + "UP030", # format literals + "PLW1514", # unspecified-encoding + "PLR0913", # too-many-arguments + "PLR0915", # too-many-statements + "PLR0917", # too-many-positional-arguments + "PLR0904", # too-many-public-methods + "PLR0912", # too-many-branches + "PLR0916", # too-many-boolean-expressions + "PLR6301", # could-be-static no-self-use + "PLC0415", # import outside toplevel + "PLC2701", # private import + "PLW2901", # loop var overwrite + "PLR1702", # too many nested blocks + "PLR1714", # multiple comparisons + "PLR0914", # too many local vars +] + +[format] +line-ending = "lf" diff --git a/tests/all_pio_instructions.py b/tests/all_pio_instructions.py index bd341ca..df512e6 100644 --- a/tests/all_pio_instructions.py +++ b/tests/all_pio_instructions.py @@ -1,7 +1,6 @@ # SPDX-FileCopyrightText: 2024 Jeff Epler, written for Adafruit Industries # # SPDX-License-Identifier: MIT -# pylint: disable=too-many-lines # fmt: off all_instruction = { diff --git a/tests/test_all.py b/tests/test_all.py index 5d0d094..576737c 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -2,11 +2,10 @@ # # SPDX-License-Identifier: MIT +import all_pio_instructions import pytest from pytest_helpers import assert_assembles_to -import all_pio_instructions - def _assert_one(expected, instruction_in, fifo="putget"): program = f""" diff --git a/tests/test_in.py b/tests/test_in.py index 9c7eeb7..ff397d1 100644 --- a/tests/test_in.py +++ b/tests/test_in.py @@ -58,12 +58,8 @@ def test_in_delay_with_sideset() -> None: def test_in_bad_source() -> None: - assert_assembly_fails( - "in bad, 17", match="Invalid in source 'bad'", errtype=ValueError - ) + assert_assembly_fails("in bad, 17", match="Invalid in source 'bad'", errtype=ValueError) def test_in_bad_bitcount() -> None: - assert_assembly_fails( - "in pins, 0", match="Count out of range", errtype=RuntimeError - ) + assert_assembly_fails("in pins, 0", match="Count out of range", errtype=RuntimeError) diff --git a/tests/test_label.py b/tests/test_label.py index d9e9183..b1c76a1 100644 --- a/tests/test_label.py +++ b/tests/test_label.py @@ -7,6 +7,7 @@ """ from pytest_helpers import assert_assembly_fails + import adafruit_pioasm @@ -28,9 +29,7 @@ def test_label() -> None: "public label1:\n", "nop\n", ] - assert_assembly_fails( - "\n".join(source), match="Duplicate label", errtype=SyntaxError - ) + assert_assembly_fails("\n".join(source), match="Duplicate label", errtype=SyntaxError) source = [ "label1:\n", @@ -38,9 +37,7 @@ def test_label() -> None: "label1:\n", " nop\n", ] - assert_assembly_fails( - "\n".join(source), match="Duplicate label", errtype=SyntaxError - ) + assert_assembly_fails("\n".join(source), match="Duplicate label", errtype=SyntaxError) source = [ "public label1:\n", @@ -48,9 +45,7 @@ def test_label() -> None: "label1:\n", " nop\n", ] - assert_assembly_fails( - "\n".join(source), match="Duplicate label", errtype=SyntaxError - ) + assert_assembly_fails("\n".join(source), match="Duplicate label", errtype=SyntaxError) source = [ "public label1:\n", @@ -58,6 +53,4 @@ def test_label() -> None: "public label1:\n", " nop\n", ] - assert_assembly_fails( - "\n".join(source), match="Duplicate label", errtype=SyntaxError - ) + assert_assembly_fails("\n".join(source), match="Duplicate label", errtype=SyntaxError) diff --git a/tests/test_misc.py b/tests/test_misc.py index ea07e51..fdf5226 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -28,12 +28,8 @@ def test_invalid_sideset() -> None: def test_invalid_delay() -> None: - assert_assembly_fails( - "[5]", match=r"Unknown instruction: \[5\]", errtype=RuntimeError - ) + assert_assembly_fails("[5]", match=r"Unknown instruction: \[5\]", errtype=RuntimeError) def test_invalid_instruction() -> None: - assert_assembly_fails( - "bad", match=r"Unknown instruction: bad", errtype=RuntimeError - ) + assert_assembly_fails("bad", match=r"Unknown instruction: bad", errtype=RuntimeError) diff --git a/tests/test_mov.py b/tests/test_mov.py index 52492d7..7cfba6f 100644 --- a/tests/test_mov.py +++ b/tests/test_mov.py @@ -11,9 +11,7 @@ def test_mov_non_happy() -> None: # non happy path - assert_assembly_fails( - "mov x, blah", match="Invalid mov source 'blah'", errtype=ValueError - ) + assert_assembly_fails("mov x, blah", match="Invalid mov source 'blah'", errtype=ValueError) def test_mov_invert() -> None: diff --git a/tests/test_nop.py b/tests/test_nop.py index 68bd3c5..30cda93 100644 --- a/tests/test_nop.py +++ b/tests/test_nop.py @@ -34,9 +34,7 @@ def test_sideset_opt() -> None: def test_set() -> None: # non happy path - assert_assembly_fails( - "set isr, 1", match="Invalid set destination 'isr'", errtype=ValueError - ) + assert_assembly_fails("set isr, 1", match="Invalid set destination 'isr'", errtype=ValueError) def test_jmp() -> None: diff --git a/tests/test_out.py b/tests/test_out.py index 67bc978..cd6eca0 100644 --- a/tests/test_out.py +++ b/tests/test_out.py @@ -60,12 +60,8 @@ def test_out_delay_with_sideset() -> None: def test_out_bad_destination() -> None: - assert_assembly_fails( - "out bad, 17", match="Invalid out destination 'bad'", errtype=ValueError - ) + assert_assembly_fails("out bad, 17", match="Invalid out destination 'bad'", errtype=ValueError) def test_out_bad_bitcount() -> None: - assert_assembly_fails( - "out pins, 0", match="Count out of range", errtype=RuntimeError - ) + assert_assembly_fails("out pins, 0", match="Count out of range", errtype=RuntimeError) diff --git a/tests/test_pseudo.py b/tests/test_pseudo.py index a5049ec..5438640 100644 --- a/tests/test_pseudo.py +++ b/tests/test_pseudo.py @@ -6,7 +6,7 @@ Tests pseudo-ops """ -from pytest_helpers import assert_pio_kwargs, assert_assembly_fails +from pytest_helpers import assert_assembly_fails, assert_pio_kwargs def test_offset() -> None: diff --git a/tests/test_version.py b/tests/test_version.py index d2aa57f..4f03bd2 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -6,7 +6,7 @@ Tests version dependent instructions """ -from pytest_helpers import assert_pio_kwargs, assert_assembly_fails, assert_assembles_to +from pytest_helpers import assert_assembles_to, assert_assembly_fails, assert_pio_kwargs def test_version() -> None: diff --git a/tools/make_all.py b/tools/make_all.py index e71cb97..e89d744 100644 --- a/tools/make_all.py +++ b/tools/make_all.py @@ -6,14 +6,10 @@ Generate test cases for adafruit_pioasm, with expected results from sdk pioasm """ -# pylint: disable=missing-function-docstring - import re from subprocess import check_output -PIOASM = ( - "/home/jepler/src/circuitpython/ports/raspberrypi/sdk/tools/pioasm/build/pioasm" -) +PIOASM = "/home/jepler/src/circuitpython/ports/raspberrypi/sdk/tools/pioasm/build/pioasm" def assemble_one_instruction(instruction_in): @@ -31,9 +27,7 @@ def _assemble_one_instruction(instruction_in, fifo="putget"): {instruction_in} {nops} """ - output = check_output( - [PIOASM, "/dev/stdin", "/dev/stdout"], input=program, encoding="utf-8" - ) + output = check_output([PIOASM, "/dev/stdin", "/dev/stdout"], input=program, encoding="utf-8") return int(re.search("0x[0-9a-f]{4}", output).group(0), 16) @@ -124,7 +118,6 @@ def all_instructions(): # SPDX-FileCopyrightText: 2024 Jeff Epler, written for Adafruit Industries # # SPDX-License-Identifier: MIT -# pylint: disable=too-many-lines # fmt: off """ )