From 6853d0638aceadc336d4ec63aba8e6ade0def685 Mon Sep 17 00:00:00 2001 From: dherrada Date: Tue, 9 Nov 2021 15:31:09 -0500 Subject: [PATCH 001/124] Updated readthedocs file --- .readthedocs.yaml | 15 +++++++++++++++ .readthedocs.yml | 7 ------- 2 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 .readthedocs.yaml delete mode 100644 .readthedocs.yml diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..95ec218 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +python: + version: "3.6" + install: + - requirements: docs/requirements.txt + - requirements: requirements.txt diff --git a/.readthedocs.yml b/.readthedocs.yml deleted file mode 100644 index 338427a..0000000 --- a/.readthedocs.yml +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries -# -# SPDX-License-Identifier: Unlicense - -python: - version: 3 -requirements_file: docs/requirements.txt From 0f3df847d65682f83d7fc00420eb4daf14954614 Mon Sep 17 00:00:00 2001 From: Dylan Herrada <33632497+dherrada@users.noreply.github.com> Date: Fri, 12 Nov 2021 13:57:28 -0500 Subject: [PATCH 002/124] Disabled unspecified-encoding --- .pylintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pylintrc b/.pylintrc index 96e676b..08e12bf 100644 --- a/.pylintrc +++ b/.pylintrc @@ -55,7 +55,7 @@ confidence= # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" # disable=import-error,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call -disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error,bad-continuation,pointless-string-statement +disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error,bad-continuation,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 From cea24409c33697bf252697426bc69ed4cc04846b Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 23 Nov 2021 13:15:34 -0600 Subject: [PATCH 003/124] update rtd py version --- .readthedocs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 95ec218..1335112 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -9,7 +9,7 @@ version: 2 python: - version: "3.6" + version: "3.7" install: - requirements: docs/requirements.txt - requirements: requirements.txt From 8a5deaf8cbef2fbdaa19e067b1c1b9c5000cec21 Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Tue, 21 Dec 2021 12:04:57 +0000 Subject: [PATCH 004/124] Add a link to the related rp2pio docs --- docs/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.rst b/docs/index.rst index f6ad1e4..ed5921d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -38,6 +38,7 @@ Table of Contents Download CircuitPython Reference Documentation + CircuitPython rp2pio Reference Documentation CircuitPython Support Forum Discord Chat Adafruit Learning System From 9a1d5c61419f4454bc1b97d0b8002db2eb3e85aa Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 22 Dec 2021 12:10:14 -0800 Subject: [PATCH 005/124] Add sideset assembly support. You must also set `sideset_enable` when creating the rp2pio.StateMachine object. Fixes #16 and fixes #21. --- adafruit_pioasm.py | 10 ++++++--- examples/txuart.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 examples/txuart.py diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 824140f..d507537 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -55,6 +55,7 @@ def assemble(text_program): pass elif line.startswith(".side_set"): sideset_count = int(line.split()[1]) + sideset_enable = 1 if "opt" in line else 0 elif line.endswith(":"): label = line[:-1] if label in labels: @@ -64,7 +65,7 @@ def assemble(text_program): # Only add as an instruction if the line isn't empty instructions.append(line) - max_delay = 2 ** (5 - sideset_count) - 1 + max_delay = 2 ** (5 - sideset_count - sideset_enable) - 1 assembled = [] for instruction in instructions: # print(instruction) @@ -76,10 +77,13 @@ def assemble(text_program): raise RuntimeError("Delay too long:", delay) instruction.pop() if len(instruction) > 1 and instruction[-2] == "side": + if sideset_count == 0: + raise RuntimeError("No side_set count set") sideset_value = int(instruction[-1]) if sideset_value > 2 ** sideset_count: raise RuntimeError("Sideset value too large") - delay |= sideset_value << (5 - sideset_count) + delay |= sideset_value << (5 - sideset_count - sideset_enable) + delay |= sideset_enable << 4 instruction.pop() instruction.pop() @@ -186,6 +190,6 @@ def assemble(text_program): else: raise RuntimeError("Unknown instruction:" + instruction[0]) assembled[-1] |= delay << 8 - # print(hex(assembled[-1])) + # print(bin(assembled[-1])) return array.array("H", assembled) diff --git a/examples/txuart.py b/examples/txuart.py new file mode 100644 index 0000000..0264019 --- /dev/null +++ b/examples/txuart.py @@ -0,0 +1,52 @@ +# SPDX-FileCopyrightText: 2021 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +import rp2pio +import adafruit_pioasm + +code = adafruit_pioasm.assemble( + """ +.program uart_tx +.side_set 1 opt + +; An 8n1 UART transmit program. +; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin. + + pull side 1 [7] ; Assert stop bit, or stall with line in idle state + set x, 7 side 0 [7] ; Preload bit counter, assert start bit for 8 clocks +bitloop: ; This loop will run 8 times (8n1 UART) + out pins, 1 ; Shift 1 bit from OSR to the first OUT pin + jmp x-- bitloop [6] ; Each loop iteration is 8 cycles. + +""" +) + +class TXUART: + def __init__(self, *, tx, baudrate=9600): + self.pio = rp2pio.StateMachine( + code, + first_out_pin=tx, + first_sideset_pin=tx, + frequency=8 * baudrate, + initial_sideset_pin_state=1, + initial_sideset_pin_direction=1, + initial_out_pin_state=1, + initial_out_pin_direction=1, + sideset_enable=True + ) + + @property + def timeout(self): + return 0 + + @property + def baudrate(self): + return self.pio.frequency // 8 + + @baudrate.setter + def baudrate(self, frequency): + self.pio.frequency = frequency * 8 + + def write(self, buf): + return self.pio.write(buf) From 210873997eecee2ce8c08f499b90d277e1c33c1f Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 22 Dec 2021 12:20:08 -0800 Subject: [PATCH 006/124] pre-commit formatting --- examples/txuart.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/txuart.py b/examples/txuart.py index 0264019..e12b5ab 100644 --- a/examples/txuart.py +++ b/examples/txuart.py @@ -22,6 +22,7 @@ """ ) + class TXUART: def __init__(self, *, tx, baudrate=9600): self.pio = rp2pio.StateMachine( @@ -33,7 +34,7 @@ def __init__(self, *, tx, baudrate=9600): initial_sideset_pin_direction=1, initial_out_pin_state=1, initial_out_pin_direction=1, - sideset_enable=True + sideset_enable=True, ) @property From 6fd0518867bb7ab06755c8fadbd58662a9752cfe Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 22 Dec 2021 12:35:25 -0800 Subject: [PATCH 007/124] Fix tests and add one --- adafruit_pioasm.py | 1 + tests/testpioasm.py | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index d507537..96fb8dd 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -38,6 +38,7 @@ def assemble(text_program): labels = {} instructions = [] sideset_count = 0 + sideset_enable = 0 for line in text_program.split("\n"): line = line.strip() if not line: diff --git a/tests/testpioasm.py b/tests/testpioasm.py index 1e90923..2ad3bf2 100644 --- a/tests/testpioasm.py +++ b/tests/testpioasm.py @@ -44,6 +44,13 @@ def testNop(self): self.assertAssemblesTo(".side_set 1\nnop side 1", [0b101_10000_010_00_010]) self.assertAssemblesTo(".side_set 1\nnop side 1 [1]", [0b101_10001_010_00_010]) + def testSidesetOpt(self): + self.assertAssemblesTo(".side_set 1 opt\nnop side 1", [0b101_11000_010_00_010]) + self.assertAssemblesTo( + ".side_set 1 opt\nnop side 0 [1]", [0b101_10001_010_00_010] + ) + self.assertAssemblesTo(".side_set 1 opt\nnop [1]", [0b101_00001_010_00_010]) + def testJmp(self): self.assertAssemblesTo("l:\njmp l", [0b000_00000_000_00000]) self.assertAssemblesTo("l:\njmp 7", [0b000_00000_000_00111]) From dd2f65e4d5fe06131c3b7d699aeaae4a10bf9260 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 23 Dec 2021 11:21:30 -0800 Subject: [PATCH 008/124] Add one more test case --- tests/testpioasm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/testpioasm.py b/tests/testpioasm.py index 2ad3bf2..a15d01c 100644 --- a/tests/testpioasm.py +++ b/tests/testpioasm.py @@ -46,6 +46,7 @@ def testNop(self): def testSidesetOpt(self): self.assertAssemblesTo(".side_set 1 opt\nnop side 1", [0b101_11000_010_00_010]) + self.assertAssemblesTo(".side_set 1 opt\nnop side 0", [0b101_10000_010_00_010]) self.assertAssemblesTo( ".side_set 1 opt\nnop side 0 [1]", [0b101_10001_010_00_010] ) From 9707333567db24c6c631cca0610d53d7f0035390 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sun, 26 Dec 2021 10:35:41 -0600 Subject: [PATCH 009/124] Add `Program` class, type information An instance of the `Program` class has the `.assembled` property as well as the `pio_kwargs` property. This allows passing information from the assembler to the StateMachine constructor, in a way that can be extended in future versions (e.g., for .wrap / .wrap_target) This re-indents a lot of code so it may be better viewed with whitespace changes hidden. Tests are added for the limits of the side-set and delay values. An off-by-one bug with the sideset_value limit was fixed. --- adafruit_pioasm.py | 348 ++++++++++++++++++++++++-------------------- tests/testpioasm.py | 26 +++- 2 files changed, 212 insertions(+), 162 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 96fb8dd..266d962 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -30,167 +30,193 @@ SET_DESTINATIONS = ["pins", "x", "y", None, "pindirs", None, None, None] -def assemble(text_program): - """Converts pioasm text to encoded instruction bytes""" - # pylint: disable=too-many-branches,too-many-statements,too-many-locals - assembled = [] - program_name = None - labels = {} - instructions = [] - sideset_count = 0 - sideset_enable = 0 - for line in text_program.split("\n"): - line = line.strip() - if not line: - continue - if ";" in line: - line = line.split(";")[0].strip() - if line.startswith(".program"): - if program_name: - raise RuntimeError("Multiple programs not supported") - program_name = line.split()[1] - elif line.startswith(".wrap_target"): - if len(instructions) > 0: - raise RuntimeError("wrap_target not supported") - elif line.startswith(".wrap"): - pass - elif line.startswith(".side_set"): - sideset_count = int(line.split()[1]) - sideset_enable = 1 if "opt" in line else 0 - elif line.endswith(":"): - label = line[:-1] - if label in labels: - raise SyntaxError(f"Duplicate label {repr(label)}") - labels[label] = len(instructions) - elif line: - # Only add as an instruction if the line isn't empty - instructions.append(line) - - max_delay = 2 ** (5 - sideset_count - sideset_enable) - 1 - assembled = [] - for instruction in instructions: - # print(instruction) - instruction = splitter(instruction.strip()) - delay = 0 - if instruction[-1].endswith("]"): # Delay - delay = int(instruction[-1].strip("[]")) - if delay > max_delay: - raise RuntimeError("Delay too long:", delay) - instruction.pop() - if len(instruction) > 1 and instruction[-2] == "side": - if sideset_count == 0: - raise RuntimeError("No side_set count set") - sideset_value = int(instruction[-1]) - if sideset_value > 2 ** sideset_count: - raise RuntimeError("Sideset value too large") - delay |= sideset_value << (5 - sideset_count - sideset_enable) - delay |= sideset_enable << 4 - instruction.pop() - instruction.pop() - - if instruction[0] == "nop": - # mov delay y op y - assembled.append(0b101_00000_010_00_010) - elif instruction[0] == "jmp": - # instr delay cnd addr - assembled.append(0b000_00000_000_00000) - target = instruction[-1] - if target[:1] in "0123456789": - assembled[-1] |= int(target) - elif instruction[-1] in labels: - assembled[-1] |= labels[target] - else: - raise SyntaxError(f"Invalid jmp target {repr(target)}") - - if len(instruction) > 2: - assembled[-1] |= CONDITIONS.index(instruction[1]) << 5 - - elif instruction[0] == "wait": - # instr delay p sr index - assembled.append(0b001_00000_0_00_00000) - polarity = int(instruction[1]) - 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]) - 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 - elif instruction[0] == "in": - # instr delay src count - assembled.append(0b010_00000_000_00000) - assembled[-1] |= IN_SOURCES.index(instruction[1]) << 5 - count = int(instruction[-1]) - if not 1 <= count <= 32: - raise RuntimeError("Count out of range") - assembled[-1] |= count & 0x1F # 32 is 00000 so we mask the top - elif instruction[0] == "out": - # instr delay dst count - assembled.append(0b011_00000_000_00000) - assembled[-1] |= OUT_DESTINATIONS.index(instruction[1]) << 5 - count = int(instruction[-1]) - if not 1 <= count <= 32: - raise RuntimeError("Count out of range") - assembled[-1] |= count & 0x1F # 32 is 00000 so we mask the top - elif instruction[0] == "push" or instruction[0] == "pull": - # instr delay d i b zero - assembled.append(0b100_00000_0_0_0_00000) - if instruction[0] == "pull": - 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"): - 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: - assembled[-1] |= MOV_SOURCES.index(source) - 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 - 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 index - assembled.append(0b110_00000_0_0_0_00000) - if instruction[-1] == "rel": - assembled[-1] |= 0x10 # Set the high bit of the irq value +class Program: # pylint: disable=too-few-public-methods + """Encapsulates a program's instruction stream and configuration flags + + Example:: + + program = adafruit_pioasm.Program(...) + state_machine = rp2pio.StateMachine(program.assembled, ..., **program.pio_kwargs) + + """ + + def __init__(self, text_program: str) -> None: + """Converts pioasm text to encoded instruction bytes""" + # pylint: disable=too-many-branches,too-many-statements,too-many-locals + assembled = [] + program_name = None + labels = {} + instructions = [] + sideset_count = 0 + sideset_enable = 0 + for line in text_program.split("\n"): + line = line.strip() + if not line: + continue + if ";" in line: + line = line.split(";")[0].strip() + if line.startswith(".program"): + if program_name: + raise RuntimeError("Multiple programs not supported") + program_name = line.split()[1] + elif line.startswith(".wrap_target"): + if len(instructions) > 0: + raise RuntimeError("wrap_target not supported") + elif line.startswith(".wrap"): + pass + elif line.startswith(".side_set"): + sideset_count = int(line.split()[1]) + sideset_enable = 1 if "opt" in line else 0 + elif line.endswith(":"): + label = line[:-1] + if label in labels: + raise SyntaxError(f"Duplicate label {repr(label)}") + labels[label] = len(instructions) + elif line: + # Only add as an instruction if the line isn't empty + instructions.append(line) + + max_delay = 2 ** (5 - sideset_count - sideset_enable) - 1 + assembled = [] + for instruction in instructions: + # print(instruction) + instruction = splitter(instruction.strip()) + delay = 0 + if instruction[-1].endswith("]"): # Delay + delay = int(instruction[-1].strip("[]")) + if delay < 0: + raise RuntimeError("Delay negative:", delay) + if delay > max_delay: + raise RuntimeError("Delay too long:", delay) + instruction.pop() + if len(instruction) > 1 and instruction[-2] == "side": + if sideset_count == 0: + raise RuntimeError("No side_set count set") + sideset_value = int(instruction[-1]) + if sideset_value >= 2 ** sideset_count: + raise RuntimeError("Sideset value too large") + delay |= sideset_value << (5 - sideset_count - sideset_enable) + delay |= sideset_enable << 4 instruction.pop() - num = int(instruction[-1]) - if not 0 <= num <= 7: - raise RuntimeError("Interrupt index out of range") - assembled[-1] |= num - if len(instruction) == 3: # after rel has been removed - if instruction[1] == "wait": + instruction.pop() + + if instruction[0] == "nop": + # mov delay y op y + assembled.append(0b101_00000_010_00_010) + elif instruction[0] == "jmp": + # instr delay cnd addr + assembled.append(0b000_00000_000_00000) + target = instruction[-1] + if target[:1] in "0123456789": + assembled[-1] |= int(target) + elif instruction[-1] in labels: + assembled[-1] |= labels[target] + else: + raise SyntaxError(f"Invalid jmp target {repr(target)}") + + if len(instruction) > 2: + assembled[-1] |= CONDITIONS.index(instruction[1]) << 5 + + elif instruction[0] == "wait": + # instr delay p sr index + assembled.append(0b001_00000_0_00_00000) + polarity = int(instruction[1]) + 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]) + 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 + elif instruction[0] == "in": + # instr delay src count + assembled.append(0b010_00000_000_00000) + assembled[-1] |= IN_SOURCES.index(instruction[1]) << 5 + count = int(instruction[-1]) + if not 1 <= count <= 32: + raise RuntimeError("Count out of range") + assembled[-1] |= count & 0x1F # 32 is 00000 so we mask the top + elif instruction[0] == "out": + # instr delay dst count + assembled.append(0b011_00000_000_00000) + assembled[-1] |= OUT_DESTINATIONS.index(instruction[1]) << 5 + count = int(instruction[-1]) + if not 1 <= count <= 32: + raise RuntimeError("Count out of range") + assembled[-1] |= count & 0x1F # 32 is 00000 so we mask the top + elif instruction[0] == "push" or instruction[0] == "pull": + # instr delay d i b zero + assembled.append(0b100_00000_0_0_0_00000) + if instruction[0] == "pull": + assembled[-1] |= 0x80 + if instruction[-1] == "block" or not instruction[-1].endswith("block"): assembled[-1] |= 0x20 - elif instruction[1] == "clear": + if len(instruction) > 1 and instruction[1] in ("ifempty", "iffull"): 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) - assembled[-1] |= SET_DESTINATIONS.index(instruction[1]) << 5 - value = int(instruction[-1]) - if not 0 <= value <= 31: - raise RuntimeError("Set value out of range") - assembled[-1] |= value - else: - raise RuntimeError("Unknown instruction:" + instruction[0]) - assembled[-1] |= delay << 8 - # print(bin(assembled[-1])) - - return array.array("H", assembled) + 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: + assembled[-1] |= MOV_SOURCES.index(source) + 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 + 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 index + assembled.append(0b110_00000_0_0_0_00000) + if instruction[-1] == "rel": + assembled[-1] |= 0x10 # Set the high bit of the irq value + instruction.pop() + num = int(instruction[-1]) + if not 0 <= num <= 7: + raise RuntimeError("Interrupt index out of range") + assembled[-1] |= num + if len(instruction) == 3: # 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) + assembled[-1] |= SET_DESTINATIONS.index(instruction[1]) << 5 + value = int(instruction[-1]) + if not 0 <= value <= 31: + raise RuntimeError("Set value out of range") + assembled[-1] |= value + else: + raise RuntimeError("Unknown instruction:" + instruction[0]) + assembled[-1] |= delay << 8 + # print(bin(assembled[-1])) + + self.pio_kwargs = { + "sideset_count": sideset_count, + "sideset_enable": sideset_enable, + } + + self.assembled = array.array("H", assembled) + + +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 + such as the details about side-set pins can be easily passsed to the + ``StateMachine`` constructor.""" + return Program(program_text).assembled diff --git a/tests/testpioasm.py b/tests/testpioasm.py index a15d01c..684702a 100644 --- a/tests/testpioasm.py +++ b/tests/testpioasm.py @@ -32,6 +32,10 @@ def assertAssemblesTo(self, source, expected): def assertAssemblyFails(self, source): self.assertRaises(RuntimeError, adafruit_pioasm.assemble, source) + def assertPioKwargs(self, source, **kw): + program = adafruit_pioasm.Program(source) + self.assertEqual(kw, program.pio_kwargs) + def testNonsense(self): self.assertAssemblyFails("nope") @@ -41,8 +45,9 @@ def testNop(self): "nop\nnop", [0b101_00000_010_00_010, 0b101_00000_010_00_010] ) self.assertAssemblesTo("nop [1]", [0b101_00001_010_00_010]) + self.assertAssemblesTo("nop [31]", [0b101_11111_010_00_010]) self.assertAssemblesTo(".side_set 1\nnop side 1", [0b101_10000_010_00_010]) - self.assertAssemblesTo(".side_set 1\nnop side 1 [1]", [0b101_10001_010_00_010]) + self.assertAssemblesTo(".side_set 1\nnop side 1 [15]", [0b101_11111_010_00_010]) def testSidesetOpt(self): self.assertAssemblesTo(".side_set 1 opt\nnop side 1", [0b101_11000_010_00_010]) @@ -51,6 +56,13 @@ def testSidesetOpt(self): ".side_set 1 opt\nnop side 0 [1]", [0b101_10001_010_00_010] ) self.assertAssemblesTo(".side_set 1 opt\nnop [1]", [0b101_00001_010_00_010]) + self.assertAssemblesTo(".side_set 1 opt\nnop [7]", [0b101_00111_010_00_010]) + self.assertAssemblesTo( + ".side_set 1 opt\nnop side 1 [1]", [0b101_11001_010_00_010] + ) + self.assertAssemblesTo( + ".side_set 1 opt\nnop side 0 [7]", [0b101_10111_010_00_010] + ) def testJmp(self): self.assertAssemblesTo("l:\njmp l", [0b000_00000_000_00000]) @@ -75,3 +87,15 @@ def testWait(self): self.assertAssemblesTo("wait 0 irq 0 rel", [0b001_00000_0_10_10000]) self.assertAssemblesTo("wait 1 irq 0", [0b001_00000_1_10_00000]) self.assertAssemblesTo("wait 0 irq 1 rel", [0b001_00000_0_10_10001]) + + def testLimits(self): + self.assertAssemblyFails(".side_set 1\nnop side 2") + self.assertAssemblyFails(".side_set 1\nnop side 2 [1]") + self.assertAssemblyFails("nop [32]") + self.assertAssemblyFails(".side_set 1\nnop side 0 [16]") + self.assertAssemblyFails(".side_set 1 opt\nnop side 0 [8]") + + def testCls(self): + self.assertPioKwargs("", sideset_count=0, sideset_enable=False) + self.assertPioKwargs(".side_set 1", sideset_count=1, sideset_enable=False) + self.assertPioKwargs(".side_set 3 opt", sideset_count=3, sideset_enable=True) From a2dd248c0726594fa547e8c7b471a4f031897914 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 1 Jan 2022 14:26:55 -0800 Subject: [PATCH 010/124] Remove the "not available on PyPI yet" note. It appears to be obsolete given the validity of the link just below. --- README.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.rst b/README.rst index 9b83f08..96dcbc8 100644 --- a/README.rst +++ b/README.rst @@ -32,8 +32,6 @@ This is easily achieved by downloading Installing from PyPI ===================== -.. note:: This library is not available on PyPI yet. Install documentation is included - as a standard element. Stay tuned for PyPI availability! On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from PyPI `_. To install for current user: From e1e5e2a3ee23fa0b36f352a97ef0cb6358ab1512 Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sun, 2 Jan 2022 12:07:24 +0000 Subject: [PATCH 011/124] Add some helpful exception wrappers These make errors easier to fix. --- adafruit_pioasm.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 96fb8dd..e58d774 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -103,7 +103,9 @@ def assemble(text_program): raise SyntaxError(f"Invalid jmp target {repr(target)}") if len(instruction) > 2: - assembled[-1] |= CONDITIONS.index(instruction[1]) << 5 + try: + assembled[-1] |= CONDITIONS.index(instruction[1]) << 5 + raise SyntaxError(f"Invalid jmp condition {instruction[1]}") elif instruction[0] == "wait": # instr delay p sr index @@ -183,7 +185,10 @@ def assemble(text_program): elif instruction[0] == "set": # instr delay dst data assembled.append(0b111_00000_000_00000) - assembled[-1] |= SET_DESTINATIONS.index(instruction[1]) << 5 + try: + assembled[-1] |= SET_DESTINATIONS.index(instruction[1]) << 5 + except ValueError: + raise RuntimeError(f"Uknnown set destination {instruction[1]}") value = int(instruction[-1]) if not 0 <= value <= 31: raise RuntimeError("Set value out of range") From ac1cb9937d48917669a837b6e233784a45cabced Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sun, 2 Jan 2022 12:09:21 +0000 Subject: [PATCH 012/124] Copied incorrectly --- adafruit_pioasm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index e58d774..750248f 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -105,7 +105,8 @@ def assemble(text_program): if len(instruction) > 2: try: assembled[-1] |= CONDITIONS.index(instruction[1]) << 5 - raise SyntaxError(f"Invalid jmp condition {instruction[1]}") + except ValueError: + raise SyntaxError(f"Invalid jmp condition {instruction[1]}") elif instruction[0] == "wait": # instr delay p sr index From 0381b301195ac284039897bba4ca6ec7d132e092 Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sun, 2 Jan 2022 12:56:55 +0000 Subject: [PATCH 013/124] Fix the pylint, add in one other I've been using --- adafruit_pioasm.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 750248f..c3e9e08 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -105,8 +105,8 @@ def assemble(text_program): if len(instruction) > 2: try: assembled[-1] |= CONDITIONS.index(instruction[1]) << 5 - except ValueError: - raise SyntaxError(f"Invalid jmp condition {instruction[1]}") + except ValueError as exc: + raise SyntaxError(f"Invalid jmp condition {instruction[1]}") from exc elif instruction[0] == "wait": # instr delay p sr index @@ -154,7 +154,10 @@ def assemble(text_program): source = instruction[-1] source_split = mov_splitter(source) if len(source_split) == 1: - assembled[-1] |= MOV_SOURCES.index(source) + try: + assembled[-1] |= MOV_SOURCES.index(source) + except ValueError as exc: + raise RuntimeError("Invalid mov source:", source) from exc else: assembled[-1] |= MOV_SOURCES.index(source_split[1]) if source[:1] == "!": @@ -188,8 +191,8 @@ def assemble(text_program): assembled.append(0b111_00000_000_00000) try: assembled[-1] |= SET_DESTINATIONS.index(instruction[1]) << 5 - except ValueError: - raise RuntimeError(f"Uknnown set destination {instruction[1]}") + except ValueError as exc: + raise RuntimeError(f"Uknnown set destination {instruction[1]}") from exc value = int(instruction[-1]) if not 0 <= value <= 31: raise RuntimeError("Set value out of range") From fa2b8689ce498a89b6c621ed66031aa9df5ff5f3 Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sun, 2 Jan 2022 13:02:33 +0000 Subject: [PATCH 014/124] Typo spotted after tweeting... Doh --- adafruit_pioasm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index c3e9e08..5b5c21b 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -192,7 +192,7 @@ def assemble(text_program): try: assembled[-1] |= SET_DESTINATIONS.index(instruction[1]) << 5 except ValueError as exc: - raise RuntimeError(f"Uknnown set destination {instruction[1]}") from exc + raise RuntimeError(f"Unknown set destination {instruction[1]}") from exc value = int(instruction[-1]) if not 0 <= value <= 31: raise RuntimeError("Set value out of range") From b0654228131f9d273d6bb62e2ab9486a479cb6b0 Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sun, 2 Jan 2022 13:22:11 +0000 Subject: [PATCH 015/124] Black reformat --- adafruit_pioasm.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 5b5c21b..8d530cc 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -106,7 +106,9 @@ def assemble(text_program): try: assembled[-1] |= CONDITIONS.index(instruction[1]) << 5 except ValueError as exc: - raise SyntaxError(f"Invalid jmp condition {instruction[1]}") from exc + raise SyntaxError( + f"Invalid jmp condition {instruction[1]}" + ) from exc elif instruction[0] == "wait": # instr delay p sr index From df0662297d8ae666b3b1c50f233590ebfde02577 Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sun, 2 Jan 2022 16:07:07 +0000 Subject: [PATCH 016/124] make my exceptions more consistent, and add tests. --- .gitignore | 1 + adafruit_pioasm.py | 8 ++++---- tests/testpioasm.py | 23 +++++++++++++++++++++-- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 2c6ddfd..6559e83 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ bundles dist **/*.egg-info .vscode +.venv diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 8d530cc..07137fe 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -106,8 +106,8 @@ def assemble(text_program): try: assembled[-1] |= CONDITIONS.index(instruction[1]) << 5 except ValueError as exc: - raise SyntaxError( - f"Invalid jmp condition {instruction[1]}" + raise ValueError( + f"Invalid jmp condition '{instruction[1]}'" ) from exc elif instruction[0] == "wait": @@ -159,7 +159,7 @@ def assemble(text_program): try: assembled[-1] |= MOV_SOURCES.index(source) except ValueError as exc: - raise RuntimeError("Invalid mov source:", source) from exc + raise ValueError(f"Invalid mov source '{source}'") from exc else: assembled[-1] |= MOV_SOURCES.index(source_split[1]) if source[:1] == "!": @@ -194,7 +194,7 @@ def assemble(text_program): try: assembled[-1] |= SET_DESTINATIONS.index(instruction[1]) << 5 except ValueError as exc: - raise RuntimeError(f"Unknown set destination {instruction[1]}") from exc + raise ValueError(f"Invalid set destination '{instruction[1]}'") from exc value = int(instruction[-1]) if not 0 <= value <= 31: raise RuntimeError("Set value out of range") diff --git a/tests/testpioasm.py b/tests/testpioasm.py index a15d01c..3ef3be5 100644 --- a/tests/testpioasm.py +++ b/tests/testpioasm.py @@ -29,8 +29,11 @@ def assertAssemblesTo(self, source, expected): f"Assembling {source!r}: Expected {expected_bin}, got {actual_bin}", ) - def assertAssemblyFails(self, source): - self.assertRaises(RuntimeError, adafruit_pioasm.assemble, source) + def assertAssemblyFails(self, source, match=None, errtype=RuntimeError): + if match: + self.assertRaisesRegex(errtype, match, adafruit_pioasm.assemble, source) + else: + self.assertRaises(errtype, adafruit_pioasm.assemble, source) def testNonsense(self): self.assertAssemblyFails("nope") @@ -52,6 +55,18 @@ def testSidesetOpt(self): ) self.assertAssemblesTo(".side_set 1 opt\nnop [1]", [0b101_00001_010_00_010]) + def testMov(self): + # non happy path + self.assertAssemblyFails( + "mov x, blah", match="Invalid mov source 'blah'", errtype=ValueError + ) + + def testSet(self): + # non happy path + self.assertAssemblyFails( + "set isr, 1", match="Invalid set destination 'isr'", errtype=ValueError + ) + def testJmp(self): self.assertAssemblesTo("l:\njmp l", [0b000_00000_000_00000]) self.assertAssemblesTo("l:\njmp 7", [0b000_00000_000_00111]) @@ -63,6 +78,10 @@ def testJmp(self): self.assertAssemblesTo("jmp x!=y, l\nl:", [0b000_00000_101_00001]) self.assertAssemblesTo("jmp pin, l\nl:", [0b000_00000_110_00001]) self.assertAssemblesTo("jmp !osre, l\nl:", [0b000_00000_111_00001]) + # non happy path + self.assertAssemblyFails( + "jmp x--., l\nl:", match="Invalid jmp condition 'x--.'", errtype=ValueError + ) def testWait(self): self.assertAssemblesTo("wait 0 gpio 0", [0b001_00000_0_00_00000]) From fcfaf5f628de2d691eddf502989e3251ec871fd2 Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sat, 8 Jan 2022 14:37:02 +0000 Subject: [PATCH 017/124] Add a link to the PIO tutorial --- docs/index.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index ed5921d..e08b9e0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -24,7 +24,8 @@ Table of Contents :caption: Tutorials Getting Started with Raspberry Pi Pico and CircuitPython - + An Introduction to RP2040 PIO with CircuitPython + .. toctree:: :caption: Related Products From c3fa9b65bd7c7454c10243629e555066e29670dc Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sat, 8 Jan 2022 15:50:28 +0000 Subject: [PATCH 018/124] Turns out, this dialect supports taking out spaces and the bang mark. --- tests/testpioasm.py | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/tests/testpioasm.py b/tests/testpioasm.py index 3ef3be5..8a3743f 100644 --- a/tests/testpioasm.py +++ b/tests/testpioasm.py @@ -18,7 +18,7 @@ def nice_opcode(o): return o[:3] + "_" + o[3:8] + "_" + o[8:] -class TestNop(unittest.TestCase): +class AssembleChecks(unittest.TestCase): def assertAssemblesTo(self, source, expected): actual = adafruit_pioasm.assemble(source) expected_bin = [nice_opcode(x) for x in expected] @@ -35,6 +35,8 @@ def assertAssemblyFails(self, source, match=None, errtype=RuntimeError): else: self.assertRaises(errtype, adafruit_pioasm.assemble, source) + +class TestNop(AssembleChecks): def testNonsense(self): self.assertAssemblyFails("nope") @@ -55,12 +57,6 @@ def testSidesetOpt(self): ) self.assertAssemblesTo(".side_set 1 opt\nnop [1]", [0b101_00001_010_00_010]) - def testMov(self): - # non happy path - self.assertAssemblyFails( - "mov x, blah", match="Invalid mov source 'blah'", errtype=ValueError - ) - def testSet(self): # non happy path self.assertAssemblyFails( @@ -94,3 +90,23 @@ def testWait(self): self.assertAssemblesTo("wait 0 irq 0 rel", [0b001_00000_0_10_10000]) self.assertAssemblesTo("wait 1 irq 0", [0b001_00000_1_10_00000]) self.assertAssemblesTo("wait 0 irq 1 rel", [0b001_00000_0_10_10001]) + + +class TestMov(AssembleChecks): + def testMovNonHappy(self): + # non happy path + self.assertAssemblyFails( + "mov x, blah", match="Invalid mov source 'blah'", errtype=ValueError + ) + + def testMovInvert(self): + # test moving and inverting + self.assertAssemblesTo("mov x, ~ x", [0b101_00000_001_01_001]) + self.assertAssemblesTo("mov x, ~ x", [0b101_00000_001_01_001]) + self.assertAssemblesTo("mov x, ~x", [0b101_00000_001_01_001]) + self.assertAssemblesTo("mov x, !x", [0b101_00000_001_01_001]) + + def testMovReverse(self): + # test moving and reversing bits + self.assertAssemblesTo("mov x, :: x", [0b101_00000_001_10_001]) + self.assertAssemblesTo("mov x, ::x", [0b101_00000_001_10_001]) From 1b7d7377eeb153cd3963ba9245bbefe6651219b1 Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sat, 8 Jan 2022 15:56:48 +0000 Subject: [PATCH 019/124] Fix the CI failure --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index e08b9e0..96d9e30 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -25,7 +25,7 @@ Table of Contents Getting Started with Raspberry Pi Pico and CircuitPython An Introduction to RP2040 PIO with CircuitPython - + .. toctree:: :caption: Related Products From 2a22758cf9b6ceb1bd12a06ea59cc147a0fb1f7b Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 12 Jan 2022 08:43:07 -0600 Subject: [PATCH 020/124] Change txuart example to use Program --- examples/txuart.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/txuart.py b/examples/txuart.py index e12b5ab..1ac3bf9 100644 --- a/examples/txuart.py +++ b/examples/txuart.py @@ -5,7 +5,7 @@ import rp2pio import adafruit_pioasm -code = adafruit_pioasm.assemble( +code = adafruit_pioasm.Program( """ .program uart_tx .side_set 1 opt @@ -25,8 +25,7 @@ class TXUART: def __init__(self, *, tx, baudrate=9600): - self.pio = rp2pio.StateMachine( - code, + self.pio = rp2pio.StateMachine(code.assembled, first_out_pin=tx, first_sideset_pin=tx, frequency=8 * baudrate, @@ -34,7 +33,7 @@ def __init__(self, *, tx, baudrate=9600): initial_sideset_pin_direction=1, initial_out_pin_state=1, initial_out_pin_direction=1, - sideset_enable=True, + **code.pio_kwargs ) @property From 68a58810444f89f74ca444361803fead935d7ba3 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 12 Jan 2022 08:43:33 -0600 Subject: [PATCH 021/124] black --- adafruit_pioasm.py | 4 +++- examples/txuart.py | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 263e986..85a5deb 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -206,7 +206,9 @@ def __init__(self, text_program: str) -> None: 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]) if not 0 <= value <= 31: raise RuntimeError("Set value out of range") diff --git a/examples/txuart.py b/examples/txuart.py index 1ac3bf9..e3382ee 100644 --- a/examples/txuart.py +++ b/examples/txuart.py @@ -25,7 +25,8 @@ class TXUART: def __init__(self, *, tx, baudrate=9600): - self.pio = rp2pio.StateMachine(code.assembled, + self.pio = rp2pio.StateMachine( + code.assembled, first_out_pin=tx, first_sideset_pin=tx, frequency=8 * baudrate, @@ -33,7 +34,7 @@ def __init__(self, *, tx, baudrate=9600): initial_sideset_pin_direction=1, initial_out_pin_state=1, initial_out_pin_direction=1, - **code.pio_kwargs + **code.pio_kwargs, ) @property From e2d819e88c53827a13fac7f279af9caf1c5ef65f Mon Sep 17 00:00:00 2001 From: dherrada Date: Thu, 13 Jan 2022 16:27:30 -0500 Subject: [PATCH 022/124] First part of patch Signed-off-by: dherrada --- .../PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md | 2 +- .github/workflows/build.yml | 6 +++--- .github/workflows/release.yml | 8 ++++---- .readthedocs.yaml | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md b/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md index 71ef8f8..8de294e 100644 --- a/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md +++ b/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md @@ -4,7 +4,7 @@ Thank you for contributing! Before you submit a pull request, please read the following. -Make sure any changes you're submitting are in line with the CircuitPython Design Guide, available here: https://circuitpython.readthedocs.io/en/latest/docs/design_guide.html +Make sure any changes you're submitting are in line with the CircuitPython Design Guide, available here: https://docs.circuitpython.org/en/latest/docs/design_guide.html If your changes are to documentation, please verify that the documentation builds locally by following the steps found here: https://adafru.it/build-docs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb7161a..f11c8c0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,10 +22,10 @@ jobs: awk -F '\/' '{ print tolower($2) }' | tr '_' '-' ) - - name: Set up Python 3.7 - uses: actions/setup-python@v1 + - name: Set up Python 3.x + uses: actions/setup-python@v2 with: - python-version: 3.7 + python-version: "3.x" - name: Versions run: | python3 --version diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6d0015a..a65e5de 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,10 +24,10 @@ jobs: awk -F '\/' '{ print tolower($2) }' | tr '_' '-' ) - - name: Set up Python 3.6 - uses: actions/setup-python@v1 + - name: Set up Python 3.x + uses: actions/setup-python@v2 with: - python-version: 3.6 + python-version: "3.x" - name: Versions run: | python3 --version @@ -67,7 +67,7 @@ jobs: echo ::set-output name=setup-py::$( find . -wholename './setup.py' ) - name: Set up Python if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: '3.x' - name: Install dependencies diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 1335112..f8b2891 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -9,7 +9,7 @@ version: 2 python: - version: "3.7" + version: "3.x" install: - requirements: docs/requirements.txt - requirements: requirements.txt From d32edfd8e7b238f721487fc634d550f8acb58c6e Mon Sep 17 00:00:00 2001 From: dherrada Date: Mon, 24 Jan 2022 16:46:17 -0500 Subject: [PATCH 023/124] Updated docs link, updated python docs link, updated setup.py --- README.rst | 4 ++-- docs/conf.py | 4 ++-- docs/index.rst | 4 ++-- setup.py | 2 -- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index 96dcbc8..5209aef 100644 --- a/README.rst +++ b/README.rst @@ -2,7 +2,7 @@ Introduction ============ .. image:: https://readthedocs.org/projects/adafruit-circuitpython-pioasm/badge/?version=latest - :target: https://circuitpython.readthedocs.io/projects/pioasm/en/latest/ + :target: https://docs.circuitpython.org/projects/pioasm/en/latest/ :alt: Documentation Status .. image:: https://img.shields.io/discord/327254708534116352.svg @@ -86,7 +86,7 @@ Usage Example Documentation ============= -API documentation for this library can be found on `Read the Docs `_. +API documentation for this library can be found on `Read the Docs `_. Contributing ============ diff --git a/docs/conf.py b/docs/conf.py index 3f6774a..131c8d1 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,8 +29,8 @@ intersphinx_mapping = { - "python": ("https://docs.python.org/3.4", None), - "CircuitPython": ("https://circuitpython.readthedocs.io/en/latest/", None), + "python": ("https://docs.python.org/3", None), + "CircuitPython": ("https://docs.circuitpython.org/en/latest/", None), } # Show the docstring from both the class and its __init__() method. diff --git a/docs/index.rst b/docs/index.rst index 96d9e30..3c680d9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -38,8 +38,8 @@ Table of Contents :caption: Other Links Download - CircuitPython Reference Documentation - CircuitPython rp2pio Reference Documentation + CircuitPython Reference Documentation + CircuitPython rp2pio Reference Documentation CircuitPython Support Forum Discord Chat Adafruit Learning System diff --git a/setup.py b/setup.py index 5903e73..266b40e 100644 --- a/setup.py +++ b/setup.py @@ -47,8 +47,6 @@ "Topic :: System :: Hardware", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", ], # What does your project relate to? keywords="adafruit blinka circuitpython micropython pioasm rp2040", From 2722649975ab36bbb7e42a48d516929f94dcb11c Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 1 Feb 2022 10:34:51 -0600 Subject: [PATCH 024/124] Fix name of parameter in pio_kwargs --- adafruit_pioasm.py | 2 +- tests/testpioasm.py | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 85a5deb..5c3e7cc 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -219,7 +219,7 @@ def __init__(self, text_program: str) -> None: # print(bin(assembled[-1])) self.pio_kwargs = { - "sideset_count": sideset_count, + "sideset_pin_count": sideset_count, "sideset_enable": sideset_enable, } diff --git a/tests/testpioasm.py b/tests/testpioasm.py index 0069b0e..722c10e 100644 --- a/tests/testpioasm.py +++ b/tests/testpioasm.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021 Jeff Epler, written for Adafruit Industries +# SPDX-FileCopyrightText: 2021 Jeff Epler, written for Adafruit Industriessideset_pin_count # # SPDX-License-Identifier: MIT @@ -111,9 +111,11 @@ def testLimits(self): self.assertAssemblyFails(".side_set 1 opt\nnop side 0 [8]") def testCls(self): - self.assertPioKwargs("", sideset_count=0, sideset_enable=False) - self.assertPioKwargs(".side_set 1", sideset_count=1, sideset_enable=False) - self.assertPioKwargs(".side_set 3 opt", sideset_count=3, sideset_enable=True) + self.assertPioKwargs("", sideset_pin_count=0, sideset_enable=False) + self.assertPioKwargs(".side_set 1", sideset_pin_count=1, sideset_enable=False) + self.assertPioKwargs( + ".side_set 3 opt", sideset_pin_count=3, sideset_enable=True + ) class TestMov(AssembleChecks): From ac5290f6c7fd452c6bd7fb5c4745f12146ad6a04 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 1 Feb 2022 10:49:49 -0600 Subject: [PATCH 025/124] Add pretty-printing of PIO programs (into C for now) --- adafruit_pioasm.py | 53 ++++++++++++++++++++++++++++-- examples/pioasm_print_c_program.py | 28 ++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 examples/pioasm_print_c_program.py diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 5c3e7cc..d63f5c0 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -40,16 +40,17 @@ class Program: # pylint: disable=too-few-public-methods """ - def __init__(self, text_program: str) -> None: + def __init__(self, text_program: str, *, build_debuginfo=False) -> None: """Converts pioasm text to encoded instruction bytes""" # pylint: disable=too-many-branches,too-many-statements,too-many-locals assembled = [] program_name = None labels = {} + linemap = [] instructions = [] sideset_count = 0 sideset_enable = 0 - for line in text_program.split("\n"): + for i, line in enumerate(text_program.split("\n")): line = line.strip() if not line: continue @@ -75,6 +76,7 @@ def __init__(self, text_program: str) -> None: elif line: # Only add as an instruction if the line isn't empty instructions.append(line) + linemap.append(i) max_delay = 2 ** (5 - sideset_count - sideset_enable) - 1 assembled = [] @@ -225,6 +227,53 @@ def __init__(self, text_program: str) -> None: self.assembled = array.array("H", assembled) + if build_debuginfo: + self.debuginfo = (linemap, text_program) + else: + self.debuginfo = None + + def print_c_program(self, name, qualifier="const"): + """Print the program into a C program snippet""" + if self.debuginfo is None: + linemap = None + program_lines = None + else: + linemap = self.debuginfo[0][:] # Use a copy since we destroy it + program_lines = self.debuginfo[1].split("\n") + + print( + f"{qualifier} int {name}_sideset_pin_count = {self.pio_kwargs['sideset_pin_count']};" + ) + print( + f"{qualifier} bool {name}_sideset_enable = {self.pio_kwargs['sideset_enable']};" + ) + print(f"{qualifier} uint16_t {name}[] = " + "{") + last_line = 0 + if linemap: + for inst in self.assembled: + next_line = linemap[0] + del linemap[0] + while last_line < next_line: + line = program_lines[last_line] + if line: + print(f" // {line}") + last_line += 1 + line = program_lines[last_line] + print(f" 0x{inst:04x}, // {line}") + last_line += 1 + while last_line < len(program_lines): + line = program_lines[last_line] + if line: + print(f" // {line}") + 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("};") + print() + def assemble(program_text: str) -> array.array: """Converts pioasm text to encoded instruction bytes diff --git a/examples/pioasm_print_c_program.py b/examples/pioasm_print_c_program.py new file mode 100644 index 0000000..7cada38 --- /dev/null +++ b/examples/pioasm_print_c_program.py @@ -0,0 +1,28 @@ +# SPDX-FileCopyrightText: 2021 Scott Shawcroft, written for Adafruit Industries +# SPDX-FileCopyrightText: 2022 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +import adafruit_pioasm + +# NeoPixels are 800khz bit streams. Zeroes are 1/3 duty cycle (~416ns) and ones +# are 2/3 duty cycle (~833ns). +text_program = """ +.program ws2812 +.side_set 1 +.wrap_target +bitloop: + out x 1 side 0 [1]; Side-set still takes place when instruction stalls + jmp !x do_zero side 1 [1]; Branch on the bit we shifted out. Positive pulse +do_one: + jmp bitloop side 1 [1]; Continue driving high, for a long pulse +do_zero: + nop side 0 [1]; Or drive low, for a short pulse +.wrap +""" + +program = adafruit_pioasm.Program(text_program, build_debuginfo=True) +program.print_c_program("pio_ws2812", qualifier="static const") + +program = adafruit_pioasm.Program(text_program, build_debuginfo=False) +program.print_c_program("pio_ws2812_short") From 5736f5034bb9ee3db9b18091e5ab00190ad4bcdd Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 1 Feb 2022 16:50:15 -0600 Subject: [PATCH 026/124] Update testpioasm.py --- tests/testpioasm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testpioasm.py b/tests/testpioasm.py index 722c10e..629a780 100644 --- a/tests/testpioasm.py +++ b/tests/testpioasm.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021 Jeff Epler, written for Adafruit Industriessideset_pin_count +# SPDX-FileCopyrightText: 2021 Jeff Epler, written for Adafruit Industries # # SPDX-License-Identifier: MIT From 9bda24e83f7f310f17a51e925957146e418809f5 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Thu, 10 Feb 2022 10:06:57 -0500 Subject: [PATCH 027/124] Consolidate Documentation sections of README --- README.rst | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 5209aef..e6549ef 100644 --- a/README.rst +++ b/README.rst @@ -88,14 +88,11 @@ Documentation API documentation for this library can be found on `Read the Docs `_. +For information on building library documentation, please check out `this guide `_. + Contributing ============ Contributions are welcome! Please read our `Code of Conduct `_ before contributing to help this project stay welcoming. - -Documentation -============= - -For information on building library documentation, please check out `this guide `_. From fde6fccaa053676ce7e3d305a6e42e593f677eb4 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 13 Jan 2022 09:17:51 -0600 Subject: [PATCH 028/124] adding this file lets 'python3 -munittest' in the top directory work --- tests/__init__.py | 0 tests/testpioasm.py | 6 ++++++ 2 files changed, 6 insertions(+) create mode 100644 tests/__init__.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/testpioasm.py b/tests/testpioasm.py index 629a780..64e9de5 100644 --- a/tests/testpioasm.py +++ b/tests/testpioasm.py @@ -136,3 +136,9 @@ def testMovReverse(self): # test moving and reversing bits self.assertAssemblesTo("mov x, :: x", [0b101_00000_001_10_001]) self.assertAssemblesTo("mov x, ::x", [0b101_00000_001_10_001]) + +class TestWrap(AssembleChecks): + def testWrap(self): + self.assertAssemblyFails(".wrap") + self.assertPioKwargs("nop\n.wrap_target\nnop\nnop\n.wrap", + sideset_count=0, sideset_enable=False, wrap=2, wrap_target=1) From d16bd7e6a4adf7c4ce301b98c2e9805b1e772446 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sun, 13 Feb 2022 13:20:28 -0600 Subject: [PATCH 029/124] Add support for wrap/wrap_target .. This also needs support in the core for specifying them in the StateMachine constructor. --- adafruit_pioasm.py | 22 ++++++++++++++++++---- tests/testpioasm.py | 10 ++++++++-- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index d63f5c0..e616c79 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -50,6 +50,8 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: instructions = [] sideset_count = 0 sideset_enable = 0 + wrap = None + wrap_target = None for i, line in enumerate(text_program.split("\n")): line = line.strip() if not line: @@ -61,13 +63,14 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: raise RuntimeError("Multiple programs not supported") program_name = line.split()[1] elif line.startswith(".wrap_target"): - if len(instructions) > 0: - raise RuntimeError("wrap_target not supported") + wrap_target = len(instructions) elif line.startswith(".wrap"): - pass + if len(instructions) == 0: + raise RuntimeError("Cannot have .wrap as first instruction") + wrap = len(instructions) - 1 elif line.startswith(".side_set"): sideset_count = int(line.split()[1]) - sideset_enable = 1 if "opt" in line else 0 + sideset_enable = "opt" in line elif line.endswith(":"): label = line[:-1] if label in labels: @@ -225,6 +228,11 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: "sideset_enable": sideset_enable, } + if wrap is not None: + self.pio_kwargs["wrap"] = wrap + if wrap_target is not None: + self.pio_kwargs["wrap_target"] = wrap_target + self.assembled = array.array("H", assembled) if build_debuginfo: @@ -241,6 +249,12 @@ def print_c_program(self, name, qualifier="const"): linemap = self.debuginfo[0][:] # Use a copy since we destroy it program_lines = self.debuginfo[1].split("\n") + 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}_sideset_pin_count = {self.pio_kwargs['sideset_pin_count']};" ) diff --git a/tests/testpioasm.py b/tests/testpioasm.py index 64e9de5..ceb7620 100644 --- a/tests/testpioasm.py +++ b/tests/testpioasm.py @@ -137,8 +137,14 @@ def testMovReverse(self): self.assertAssemblesTo("mov x, :: x", [0b101_00000_001_10_001]) self.assertAssemblesTo("mov x, ::x", [0b101_00000_001_10_001]) + class TestWrap(AssembleChecks): def testWrap(self): self.assertAssemblyFails(".wrap") - self.assertPioKwargs("nop\n.wrap_target\nnop\nnop\n.wrap", - sideset_count=0, sideset_enable=False, wrap=2, wrap_target=1) + self.assertPioKwargs( + "nop\n.wrap_target\nnop\nnop\n.wrap", + sideset_pin_count=0, + sideset_enable=False, + wrap=2, + wrap_target=1, + ) From b70f116f90dd09fcea5241d366cb8fd924630e50 Mon Sep 17 00:00:00 2001 From: dherrada Date: Mon, 14 Feb 2022 15:35:02 -0500 Subject: [PATCH 030/124] Fixed readthedocs build Signed-off-by: dherrada --- .readthedocs.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index f8b2891..33c2a61 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -8,8 +8,12 @@ # Required version: 2 +build: + os: ubuntu-20.04 + tools: + python: "3" + python: - version: "3.x" install: - requirements: docs/requirements.txt - requirements: requirements.txt From 7389b2e34e9bbab90a7d8ef87b5f94447a0096a7 Mon Sep 17 00:00:00 2001 From: dherrada Date: Thu, 24 Mar 2022 16:36:58 -0400 Subject: [PATCH 031/124] Renamed examples/ subdirs to satisfy adabot --- examples/{getting-started => pioasm_getting-started}/README.md | 0 .../{getting-started => pioasm_getting-started}/led_brightness.py | 0 examples/{pico-examples => pioasm_pico-examples}/README.md | 0 examples/{pico-examples => pioasm_pico-examples}/blink.py | 0 examples/{pico-examples => pioasm_pico-examples}/hello.py | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename examples/{getting-started => pioasm_getting-started}/README.md (100%) rename examples/{getting-started => pioasm_getting-started}/led_brightness.py (100%) rename examples/{pico-examples => pioasm_pico-examples}/README.md (100%) rename examples/{pico-examples => pioasm_pico-examples}/blink.py (100%) rename examples/{pico-examples => pioasm_pico-examples}/hello.py (100%) diff --git a/examples/getting-started/README.md b/examples/pioasm_getting-started/README.md similarity index 100% rename from examples/getting-started/README.md rename to examples/pioasm_getting-started/README.md diff --git a/examples/getting-started/led_brightness.py b/examples/pioasm_getting-started/led_brightness.py similarity index 100% rename from examples/getting-started/led_brightness.py rename to examples/pioasm_getting-started/led_brightness.py diff --git a/examples/pico-examples/README.md b/examples/pioasm_pico-examples/README.md similarity index 100% rename from examples/pico-examples/README.md rename to examples/pioasm_pico-examples/README.md diff --git a/examples/pico-examples/blink.py b/examples/pioasm_pico-examples/blink.py similarity index 100% rename from examples/pico-examples/blink.py rename to examples/pioasm_pico-examples/blink.py diff --git a/examples/pico-examples/hello.py b/examples/pioasm_pico-examples/hello.py similarity index 100% rename from examples/pico-examples/hello.py rename to examples/pioasm_pico-examples/hello.py From 5eec7e9958f1202afc582d7ed7701b94197a9df4 Mon Sep 17 00:00:00 2001 From: dherrada Date: Thu, 24 Mar 2022 17:41:35 -0400 Subject: [PATCH 032/124] Renamed more examples --- examples/{rxuart.py => pioasm_rxuart.py} | 0 examples/{txuart.py => pioasm_txuart.py} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename examples/{rxuart.py => pioasm_rxuart.py} (100%) rename examples/{txuart.py => pioasm_txuart.py} (100%) diff --git a/examples/rxuart.py b/examples/pioasm_rxuart.py similarity index 100% rename from examples/rxuart.py rename to examples/pioasm_rxuart.py diff --git a/examples/txuart.py b/examples/pioasm_txuart.py similarity index 100% rename from examples/txuart.py rename to examples/pioasm_txuart.py From 95cfc1edb0646528fc85eeb003e6b7e768bb5f1a Mon Sep 17 00:00:00 2001 From: Eva Herrada <33632497+evaherrada@users.noreply.github.com> Date: Thu, 24 Mar 2022 18:37:07 -0400 Subject: [PATCH 033/124] Rename rotaryencoder.py to pioasm_rotaryencoder.py --- examples/{rotaryencoder.py => pioasm_rotaryencoder.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{rotaryencoder.py => pioasm_rotaryencoder.py} (100%) diff --git a/examples/rotaryencoder.py b/examples/pioasm_rotaryencoder.py similarity index 100% rename from examples/rotaryencoder.py rename to examples/pioasm_rotaryencoder.py From cac412615c51ea6bf516995b6994127fa1fa201d Mon Sep 17 00:00:00 2001 From: Kattni Rembor Date: Mon, 28 Mar 2022 15:52:04 -0400 Subject: [PATCH 034/124] Update Black to latest. Signed-off-by: Kattni Rembor --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1b9fadc..7467c1d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ repos: - repo: https://github.com/python/black - rev: 20.8b1 + rev: 22.3.0 hooks: - id: black - repo: https://github.com/fsfe/reuse-tool From 5aa37e1a4622553d914f55a429183ceda3894325 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Tue, 29 Mar 2022 18:19:18 -0400 Subject: [PATCH 035/124] "Reformatted per new black version" --- adafruit_pioasm.py | 2 +- examples/pioasm_i2sout.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index d63f5c0..1237f13 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -95,7 +95,7 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: if sideset_count == 0: raise RuntimeError("No side_set count set") sideset_value = int(instruction[-1]) - if sideset_value >= 2 ** sideset_count: + if sideset_value >= 2**sideset_count: raise RuntimeError("Sideset value too large") delay |= sideset_value << (5 - sideset_count - sideset_enable) delay |= sideset_enable << 4 diff --git a/examples/pioasm_i2sout.py b/examples/pioasm_i2sout.py index 36d437b..e57f69f 100644 --- a/examples/pioasm_i2sout.py +++ b/examples/pioasm_i2sout.py @@ -19,7 +19,7 @@ # signed 16 bit s16 = array.array("h", [0] * length) for i in range(length): - s16[i] = int(math.sin(math.pi * 2 * i / length) * (2 ** 15)) + s16[i] = int(math.sin(math.pi * 2 * i / length) * (2**15)) print(s16[i]) program = """ From 16adbe4464579c5260880b57938c7150d57bcb52 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 6 Apr 2022 08:41:58 -0500 Subject: [PATCH 036/124] The core doesn't like sideset_pin_count=0, so don't send it --- adafruit_pioasm.py | 9 +++++---- tests/testpioasm.py | 3 +-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index e616c79..a336c27 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -224,10 +224,12 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: # print(bin(assembled[-1])) self.pio_kwargs = { - "sideset_pin_count": sideset_count, "sideset_enable": sideset_enable, } + if sideset_count != 0: + self.pio_kwargs["sideset_pin_count"] = sideset_count + if wrap is not None: self.pio_kwargs["wrap"] = wrap if wrap_target is not None: @@ -255,9 +257,8 @@ def print_c_program(self, name, qualifier="const"): print( f"{qualifier} int {name}_wrap_target = {self.pio_kwargs.get('wrap_target', 0)};" ) - print( - f"{qualifier} int {name}_sideset_pin_count = {self.pio_kwargs['sideset_pin_count']};" - ) + 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']};" ) diff --git a/tests/testpioasm.py b/tests/testpioasm.py index ceb7620..3667e51 100644 --- a/tests/testpioasm.py +++ b/tests/testpioasm.py @@ -111,7 +111,7 @@ def testLimits(self): self.assertAssemblyFails(".side_set 1 opt\nnop side 0 [8]") def testCls(self): - self.assertPioKwargs("", sideset_pin_count=0, sideset_enable=False) + self.assertPioKwargs("", sideset_enable=False) self.assertPioKwargs(".side_set 1", sideset_pin_count=1, sideset_enable=False) self.assertPioKwargs( ".side_set 3 opt", sideset_pin_count=3, sideset_enable=True @@ -143,7 +143,6 @@ def testWrap(self): self.assertAssemblyFails(".wrap") self.assertPioKwargs( "nop\n.wrap_target\nnop\nnop\n.wrap", - sideset_pin_count=0, sideset_enable=False, wrap=2, wrap_target=1, From 8bd335f4be19eea56e16482cda845f01e24fdc53 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 6 Apr 2022 08:47:49 -0500 Subject: [PATCH 037/124] add a wrap example --- LICENSES/BSD-3-Clause.txt | 11 +++++++++++ examples/pioasm_wrap.py | 17 +++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 LICENSES/BSD-3-Clause.txt create mode 100644 examples/pioasm_wrap.py diff --git a/LICENSES/BSD-3-Clause.txt b/LICENSES/BSD-3-Clause.txt new file mode 100644 index 0000000..086d399 --- /dev/null +++ b/LICENSES/BSD-3-Clause.txt @@ -0,0 +1,11 @@ +Copyright (c) . + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/examples/pioasm_wrap.py b/examples/pioasm_wrap.py new file mode 100644 index 0000000..3796a18 --- /dev/null +++ b/examples/pioasm_wrap.py @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2022 Jeff Epler, written for Adafruit Industries +# SPDF-FileCopyrightText: 2020 Raspberry Pi (Trading) Ltd. +# +# SPDX-License-Identifier: BSD-3-Clause +import adafruit_pioasm + +program = adafruit_pioasm.Program( + """ + set pindirs, 1 +.wrap_target + set pins, 0 + set pins, 1 +.wrap""", + build_debuginfo=True, +) + +program.print_c_program("test") From 4267cf3f7979db35d6e7fe8c3d91a371721f8c7b Mon Sep 17 00:00:00 2001 From: Eva Herrada <33632497+evaherrada@users.noreply.github.com> Date: Thu, 21 Apr 2022 18:53:16 -0400 Subject: [PATCH 038/124] Update .gitignore --- .gitignore | 50 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 6559e83..544ec4a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,47 @@ -# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# SPDX-FileCopyrightText: 2022 Kattni Rembor, written for Adafruit Industries # -# SPDX-License-Identifier: Unlicense +# SPDX-License-Identifier: MIT +# Do not include files and directories created by your personal work environment, such as the IDE +# you use, except for those already listed here. Pull requests including changes to this file will +# not be accepted. + +# This .gitignore file contains rules for files generated by working with CircuitPython libraries, +# including building Sphinx, testing with pip, and creating a virual environment, as well as the +# MacOS and IDE-specific files generated by using MacOS in general, or the PyCharm or VSCode IDEs. + +# If you find that there are files being generated on your machine that should not be included in +# your git commit, you should create a .gitignore_global file on your computer to include the +# files created by your personal setup. To do so, follow the two steps below. + +# First, create a file called .gitignore_global somewhere convenient for you, and add rules for +# the files you want to exclude from git commits. + +# Second, configure Git to use the exclude file for all Git repositories by running the +# following via commandline, replacing "path/to/your/" with the actual path to your newly created +# .gitignore_global file: +# git config --global core.excludesfile path/to/your/.gitignore_global + +# CircuitPython-specific files *.mpy -.idea + +# Python-specific files __pycache__ -_build *.pyc + +# Sphinx build-specific files +_build + +# This file results from running `pip -e install .` in a local repository +*.egg-info + +# Virtual environment-specific files .env -.python-version -build*/ -bundles + +# MacOS-specific files *.DS_Store -.eggs -dist -**/*.egg-info + +# IDE-specific files +.idea .vscode -.venv +*~ From 243b52bb41b9f532ce3c1f1558f4a6ebde519904 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 22 Apr 2022 14:28:15 -0500 Subject: [PATCH 039/124] add pulsegroup example --- examples/pioasm_pulsegroup.py | 256 ++++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 examples/pioasm_pulsegroup.py diff --git a/examples/pioasm_pulsegroup.py b/examples/pioasm_pulsegroup.py new file mode 100644 index 0000000..cdc562a --- /dev/null +++ b/examples/pioasm_pulsegroup.py @@ -0,0 +1,256 @@ +# SPDX-FileCopyrightText: 2022 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT +# +# Heavy inspiration from Pimoroni's "PWM Cluster": +# https://github.com/pimoroni/pimoroni-pico/blob/main/drivers/pwm/pwm_cluster.cpp +# https://github.com/pimoroni/pimoroni-pico/blob/main/drivers/pwm/pwm_cluster.pio + +import array + +import board +import rp2pio +import adafruit_ticks +import ulab.numpy as np +from adafruit_motor import servo + + +import adafruit_pioasm + +_cycle_count = 3 +_program = adafruit_pioasm.Program( + """ +.wrap_target + out pins, 32 ; Immediately set the pins to their new state + out y, 32 ; Set the counter +count_check: + jmp y-- delay ; Check if the counter is 0, and if so wrap around. + ; If not decrement the counter and jump to the delay +.wrap + +delay: + jmp count_check [1] ; Wait a few cycles then jump back to the loop +""" +) + + +class PulseItem: + def __init__(self, group, index, phase, maxval): + self._group = group + self._index = index + self._phase = phase + self._value = 0 + self._maxval = maxval + self._turn_on = self._turn_off = None + self._mask = 1 << index + + @property + def frequency(self): + return self._group.frequency + + @property + def duty_cycle(self): + return self._value + + @duty_cycle.setter + def duty_cycle(self, value): + if value < 0 or value > self._maxval: + raise ValueError(f"value must be in the range(0, {self._maxval+1})") + self._value = value + self._recalculate() + + @property + def phase(self): + return self._phase + + @phase.setter + def phase(self, phase): + if phase < 0 or phase >= self._maxval: + raise ValueError(f"phase must be in the range(0, {self._maxval})") + self._phase = phase + self._recalculate() + + 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 + + def _get_turn_on(self): + maxval = self._maxval + if self._value == 0: + return None + if self._value == self._maxval: + return 0 + return self.phase % maxval + + def _get_turn_off(self): + maxval = self._maxval + if self._value == 0: + return None + if self._value == self._maxval: + return None + return (self._value + self.phase) % maxval + + def __str__(self): + return f"" + + +class PulseGroup: + def __init__( + self, + first_pin, + pin_count, + period=0.02, + maxval=65535, + stagger=True, + 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) + self._sm = rp2pio.StateMachine( + _program.assembled, + frequency=pio_frequency, + first_out_pin=first_pin, + out_pin_count=pin_count, + auto_pull=True, + pull_threshold=32, + **_program.pio_kwargs, + ) + self._auto_update = auto_update + self._items = [ + PulseItem(self, i, round(maxval * i / pin_count) if stagger else 0, maxval) + for i in range(pin_count) + ] + self._maxval = maxval + + @property + def frequency(self): + return self._frequency + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.deinit() + + def deinit(self): + self._sm.deinit() + del self._items[:] + + def __getitem__(self, i): + """Get an individual pulse generator""" + return self._items[i] + + def __len__(self): + return len(self._items) + + 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 + + if turn_on is not None: + this_change = changes.get(turn_on) + if this_change: + this_change[0] |= mask + else: + changes[turn_on] = [mask, 0] + + # start the cycle 'on' + if turn_off is not None and turn_off < turn_on: + changes[0][0] |= mask + + if turn_off is not None: + this_change = changes.get(turn_off) + if this_change: + this_change[1] |= mask + else: + changes[turn_off] = [0, mask] + + def make_sequence(): + sorted_changes = sorted(changes.items()) + # Note that the first change time is always 0! Loop over range(len) is + # to reduce allocations + old_time = 0 + value = 0 + for time, (turn_on, turn_off) in sorted_changes: + if time != 0: # never occurs on the first iteration + yield time - old_time - 1 + old_time = time + + value = (value | turn_on) & ~turn_off + yield value + + # the final delay value + yield self._maxval - old_time + + buf = array.array("L", make_sequence()) + + self._sm.start_continuous_write(buf) + + def _maybe_update(self): + if self._auto_update: + self.update() + + @property + def auto_update(self): + return self.auto_update + + @auto_update.setter + def auto_update(self, value): + self.auto_update = bool(value) + + def __str__(self): + return f"" + + +class CyclicSignal: + def __init__(self, data, phase=0): + self._data = data + self._phase = 0 + self.phase = phase + self._scale = len(self._data) - 1 + + @property + def phase(self): + return self._phase + + @phase.setter + def phase(self, value): + self._phase = value % 1 + + @property + def value(self): + idxf = self._phase * len(self._data) + idx = int(idxf) + frac = idxf % 1 + idx1 = (idx + 1) % len(self._data) + val = self._data[idx] + val1 = self._data[idx1] + return val + (val1 - val) * frac + + def advance(self, delta): + self._phase = (self._phase + delta) % 1 + + +pulsers = PulseGroup(board.SERVO_1, 18, auto_update=False) +servos = [servo.Servo(p) for p in pulsers] + +sine = np.sin(np.linspace(0, 2 * np.pi, 50, endpoint=False)) * 0.5 + 0.5 +print(sine) + +signals = [CyclicSignal(sine, i / len(servos)) for i in range(len(servos))] + +t0 = adafruit_ticks.ticks_ms() +while True: + t1 = adafruit_ticks.ticks_ms() + for servo, signal in zip(servos, signals): + signal.advance((t1 - t0) / 8000) + servo.fraction = signal.value + pulsers.update() + print(adafruit_ticks.ticks_diff(t1, t0), "ms") + t0 = t1 From 871640f1b81811c40c5f1ca753f8c2a0a6d11bd0 Mon Sep 17 00:00:00 2001 From: evaherrada Date: Fri, 22 Apr 2022 15:59:08 -0400 Subject: [PATCH 040/124] Patch: Replaced discord badge image --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index e6549ef..9a69451 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,7 @@ Introduction :target: https://docs.circuitpython.org/projects/pioasm/en/latest/ :alt: Documentation Status -.. image:: https://img.shields.io/discord/327254708534116352.svg +.. image:: https://github.com/adafruit/Adafruit_CircuitPython_Bundle/blob/main/badges/adafruit_discord.svg :target: https://adafru.it/discord :alt: Discord From 6c8a16eceff80ce9d050658f988e3abc9527e92c Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sat, 23 Apr 2022 13:56:58 -0500 Subject: [PATCH 041/124] update for PR in progress --- examples/pioasm_pulsegroup.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/examples/pioasm_pulsegroup.py b/examples/pioasm_pulsegroup.py index cdc562a..b2c54cc 100644 --- a/examples/pioasm_pulsegroup.py +++ b/examples/pioasm_pulsegroup.py @@ -190,7 +190,7 @@ def make_sequence(): buf = array.array("L", make_sequence()) - self._sm.start_continuous_write(buf) + self._sm.background_write(loop=buf) def _maybe_update(self): if self._auto_update: @@ -237,20 +237,21 @@ def advance(self, delta): self._phase = (self._phase + delta) % 1 -pulsers = PulseGroup(board.SERVO_1, 18, auto_update=False) -servos = [servo.Servo(p) for p in pulsers] +if __name__ == "__main__": + pulsers = PulseGroup(board.SERVO_1, 18, auto_update=False) + servos = [servo.Servo(p) for p in pulsers] -sine = np.sin(np.linspace(0, 2 * np.pi, 50, endpoint=False)) * 0.5 + 0.5 -print(sine) + sine = np.sin(np.linspace(0, 2 * np.pi, 50, endpoint=False)) * 0.5 + 0.5 + print(sine) -signals = [CyclicSignal(sine, i / len(servos)) for i in range(len(servos))] + signals = [CyclicSignal(sine, i / len(servos)) for i in range(len(servos))] -t0 = adafruit_ticks.ticks_ms() -while True: - t1 = adafruit_ticks.ticks_ms() - for servo, signal in zip(servos, signals): - signal.advance((t1 - t0) / 8000) - servo.fraction = signal.value - pulsers.update() - print(adafruit_ticks.ticks_diff(t1, t0), "ms") - t0 = t1 + t0 = adafruit_ticks.ticks_ms() + while True: + t1 = adafruit_ticks.ticks_ms() + for servo, signal in zip(servos, signals): + signal.advance((t1 - t0) / 8000) + servo.fraction = signal.value + pulsers.update() + print(adafruit_ticks.ticks_diff(t1, t0), "ms") + t0 = t1 From 2f1af955c6f9890a10f400161140d399e082e9ce Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sun, 24 Apr 2022 14:05:25 -0500 Subject: [PATCH 042/124] change discord badge --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 9a69451..b245e27 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,7 @@ Introduction :target: https://docs.circuitpython.org/projects/pioasm/en/latest/ :alt: Documentation Status -.. image:: https://github.com/adafruit/Adafruit_CircuitPython_Bundle/blob/main/badges/adafruit_discord.svg +.. image:: https://raw.githubusercontent.com/adafruit/Adafruit_CircuitPython_Bundle/main/badges/adafruit_discord.svg :target: https://adafru.it/discord :alt: Discord From 5429dc1f59d4d62753739ef87d60554e749a111a Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sat, 23 Apr 2022 13:54:55 -0500 Subject: [PATCH 043/124] accept 0x, 0b, 0o literals in most places numbers can be used Closes: #30 (though I don't know what octal syntax is accepted by official pioasm, we will accept python-style 0o567 not C-style 0567) --- adafruit_pioasm.py | 20 ++++++++++---------- tests/testpioasm.py | 14 +++++++++++++- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index fe7a036..5b4044b 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -69,7 +69,7 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: raise RuntimeError("Cannot have .wrap as first instruction") wrap = len(instructions) - 1 elif line.startswith(".side_set"): - sideset_count = int(line.split()[1]) + sideset_count = int(line.split()[1], 0) sideset_enable = "opt" in line elif line.endswith(":"): label = line[:-1] @@ -88,7 +88,7 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: instruction = splitter(instruction.strip()) delay = 0 if instruction[-1].endswith("]"): # Delay - delay = int(instruction[-1].strip("[]")) + delay = int(instruction[-1].strip("[]"), 0) if delay < 0: raise RuntimeError("Delay negative:", delay) if delay > max_delay: @@ -97,7 +97,7 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: if len(instruction) > 1 and instruction[-2] == "side": if sideset_count == 0: raise RuntimeError("No side_set count set") - sideset_value = int(instruction[-1]) + sideset_value = int(instruction[-1], 0) if sideset_value >= 2**sideset_count: raise RuntimeError("Sideset value too large") delay |= sideset_value << (5 - sideset_count - sideset_enable) @@ -113,7 +113,7 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: assembled.append(0b000_00000_000_00000) target = instruction[-1] if target[:1] in "0123456789": - assembled[-1] |= int(target) + assembled[-1] |= int(target, 0) elif instruction[-1] in labels: assembled[-1] |= labels[target] else: @@ -130,12 +130,12 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: elif instruction[0] == "wait": # instr delay p sr index assembled.append(0b001_00000_0_00_00000) - polarity = int(instruction[1]) + polarity = int(instruction[1], 0) 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]) + num = int(instruction[3], 0) if not 0 <= num <= 31: raise RuntimeError("Wait num out of range") assembled[-1] |= num @@ -145,7 +145,7 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: # instr delay src count assembled.append(0b010_00000_000_00000) assembled[-1] |= IN_SOURCES.index(instruction[1]) << 5 - count = int(instruction[-1]) + count = int(instruction[-1], 0) if not 1 <= count <= 32: raise RuntimeError("Count out of range") assembled[-1] |= count & 0x1F # 32 is 00000 so we mask the top @@ -153,7 +153,7 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: # instr delay dst count assembled.append(0b011_00000_000_00000) assembled[-1] |= OUT_DESTINATIONS.index(instruction[1]) << 5 - count = int(instruction[-1]) + count = int(instruction[-1], 0) if not 1 <= count <= 32: raise RuntimeError("Count out of range") assembled[-1] |= count & 0x1F # 32 is 00000 so we mask the top @@ -195,7 +195,7 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: if instruction[-1] == "rel": assembled[-1] |= 0x10 # Set the high bit of the irq value instruction.pop() - num = int(instruction[-1]) + num = int(instruction[-1], 0) if not 0 <= num <= 7: raise RuntimeError("Interrupt index out of range") assembled[-1] |= num @@ -214,7 +214,7 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: raise ValueError( f"Invalid set destination '{instruction[1]}'" ) from exc - value = int(instruction[-1]) + value = int(instruction[-1], 0) if not 0 <= value <= 31: raise RuntimeError("Set value out of range") assembled[-1] |= value diff --git a/tests/testpioasm.py b/tests/testpioasm.py index 3667e51..a2e9b3c 100644 --- a/tests/testpioasm.py +++ b/tests/testpioasm.py @@ -128,7 +128,6 @@ def testMovNonHappy(self): def testMovInvert(self): # test moving and inverting self.assertAssemblesTo("mov x, ~ x", [0b101_00000_001_01_001]) - self.assertAssemblesTo("mov x, ~ x", [0b101_00000_001_01_001]) self.assertAssemblesTo("mov x, ~x", [0b101_00000_001_01_001]) self.assertAssemblesTo("mov x, !x", [0b101_00000_001_01_001]) @@ -147,3 +146,16 @@ def testWrap(self): wrap=2, wrap_target=1, ) + + +class TestRadix(AssembleChecks): + def testOctal(self): + self.assertAssemblesTo(".side_set 0o1\nset x, 0o11", [0b111_00000_001_01001]) + + def testBinary(self): + self.assertAssemblesTo( + ".side_set 0b101\nnop side 0b10001", [0b101_10001_010_00_010] + ) + + def testHex(self): + self.assertAssemblesTo(".side_set 0x0\nnop [0x10]", [0b101_10000_010_00_010]) From c18585e1baccd7ff32d69a996e6eea6dccfb5b80 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 26 Apr 2022 09:48:52 -0500 Subject: [PATCH 044/124] disable default stagger; use 2.5ms stagger for servos --- examples/pioasm_pulsegroup.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/pioasm_pulsegroup.py b/examples/pioasm_pulsegroup.py index b2c54cc..753ac13 100644 --- a/examples/pioasm_pulsegroup.py +++ b/examples/pioasm_pulsegroup.py @@ -102,7 +102,7 @@ def __init__( pin_count, period=0.02, maxval=65535, - stagger=True, + stagger=False, auto_update=True, ): # pylint: disable=too-many-arguments """Create a pulse group with the given characteristics""" @@ -239,12 +239,19 @@ def advance(self, delta): if __name__ == "__main__": pulsers = PulseGroup(board.SERVO_1, 18, auto_update=False) + # Set the phase of each servo so that servo 0 starts at offset 0ms, servo 1 + # at offset 2.5ms, ... + # For up to 8 servos, this means their duty cycles do not overlap. Otherwise, + # servo 9 is also at offset 0ms, etc. + for j, p in enumerate(pulsers): + p.phase = 8192 * (j % 8) + servos = [servo.Servo(p) for p in pulsers] sine = np.sin(np.linspace(0, 2 * np.pi, 50, endpoint=False)) * 0.5 + 0.5 print(sine) - signals = [CyclicSignal(sine, i / len(servos)) for i in range(len(servos))] + signals = [CyclicSignal(sine, j / len(servos)) for j in range(len(servos))] t0 = adafruit_ticks.ticks_ms() while True: From 2c391efed0208c1076380451fe51e6245e42b436 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 4 May 2022 14:44:31 -0500 Subject: [PATCH 045/124] add morse code background example --- examples/pioasm_background_morse.py | 104 ++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 examples/pioasm_background_morse.py diff --git a/examples/pioasm_background_morse.py b/examples/pioasm_background_morse.py new file mode 100644 index 0000000..d6cdc89 --- /dev/null +++ b/examples/pioasm_background_morse.py @@ -0,0 +1,104 @@ +# SPDX-FileCopyrightText: 2022 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +"""Demonstrate background writing, including loop writing, with morse code. + +On any rp2040 board with board.LED, this will alternately send 'SOS' and 'TEST' +via the LED, demonstrating that Python code continues to run while the morse +code data is transmitted. Alternately, change one line below to make it send +'TEST' forever in a loop, again while Python code continues to run. + +The combination of "LED status" and duration is sent to the PIO as 16-bit number: +The top bit is 1 if the LED is turned on and 0 otherwise. The other 15 bits form a delay +value from 1 to 32767. A subset of the morse code 'alphabit' is created, with everthing +based on the 'DIT duration' of about 128ms (1MHz / 32 / 4000). + +https://en.wikipedia.org/wiki/Morse_code +""" + +import time +import array +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, +# then delays a length of time given by the next 15 bits of the value. +# By correctly choosing the durations, a message in morse code can be sent. +pio_code = Program( + """ + out x, 1 + mov pins, x + out x, 15 + busy_wait: + jmp x--, busy_wait [31] + """ +) + + +# The top bit of the command is the LED value, on or off +LED_ON = 0x8000 +LED_OFF = 0x0000 + +# The other 15 bits are a delay duration. +# It must be the case that 4 * DIT_DURATION < 32768 +DIT_DURATION = 4000 +DAH_DURATION = 3 * DIT_DURATION + +# Build up some elements of morse code, based on the wikipedia article. +DIT = array.array("H", [LED_ON | DIT_DURATION, LED_OFF | DIT_DURATION]) +DAH = array.array("H", [LED_ON | DAH_DURATION, LED_OFF | DIT_DURATION]) +# That is, two more DAH-length gaps for a total of three +LETTER_SPACE = array.array("H", [LED_OFF | (2 * DAH_DURATION)]) +# That is, four more DAH-length gaps (after a letter space) for a total of seven +WORD_SPACE = array.array("H", [LED_OFF | (4 * DIT_DURATION)]) + +# Letters and words can be created by concatenating ("+") the elements +E = DAH + LETTER_SPACE +O = DAH + DAH + DAH + LETTER_SPACE +S = DIT + DIT + DIT + LETTER_SPACE +T = DIT + LETTER_SPACE +SOS = S + O + S + WORD_SPACE +TEST = T + E + S + T + WORD_SPACE + +# 8 slots of the shortest possible led-off time +# A background write is 'complete' as soon as all data has been placed +# in the StateMachine's FIFO. This FIFO has either 4 or 8 entries. +# By adding 8 very short "led off" times, we can have our 'sm.writing' test +# tell us when the actual letter data has all been +FILLER = array.array("H", [LED_OFF | 1]) * 8 + +sm = StateMachine( + pio_code.assembled, + frequency=1_000_000, + first_out_pin=LED, + pull_threshold=16, + auto_pull=True, + out_shift_right=False, +) + +# To simply repeat 'TEST' forever, change to 'if True': +if False: # pylint: disable=using-constant-test + print("Sending out TEST forever", end="") + sm.background_write(loop=TEST) + while True: + print(end=".") + time.sleep(0.1) + +# But instead, let's alternate SOS and TEST, forever: + +while True: + for plain, morse in ( + ("SOS", SOS), + ("TEST", TEST), + ): + print(f"Sending out {plain}", end="") + sm.background_write(morse + FILLER) + while sm.writing: + print(end=".") + time.sleep(0.1) + print() + print("Message all sent to StateMachine") + time.sleep(1) + print() From 3a9a0288b795b7e5fabeca90dfeed93b639ab8f1 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 6 May 2022 08:27:43 -0500 Subject: [PATCH 046/124] match the latest core code --- examples/pioasm_neopixel.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/examples/pioasm_neopixel.py b/examples/pioasm_neopixel.py index 00ced1f..3d694e5 100644 --- a/examples/pioasm_neopixel.py +++ b/examples/pioasm_neopixel.py @@ -8,19 +8,22 @@ import microcontroller import adafruit_pioasm -# NeoPixels are 800khz bit streams. Zeroes are 1/3 duty cycle (~416ns) and ones -# are 2/3 duty cycle (~833ns). +# NeoPixels are 800khz bit streams. We are choosing zeros as <312ns hi, 936 lo> and ones +# and ones as <700 ns hi, 556 ns lo>. +# cycle. The first two instructions always run while only one of the two final +# instructions run per bit. We start with the low period because it can be +# longer while waiting for more data. program = """ .program ws2812 .side_set 1 .wrap_target bitloop: - out x 1 side 0 [1]; Side-set still takes place when instruction stalls - jmp !x do_zero side 1 [1]; Branch on the bit we shifted out. Positive pulse -do_one: - jmp bitloop side 1 [1]; Continue driving high, for a long pulse -do_zero: - nop side 0 [1]; Or drive low, for a short pulse + out x 1 side 0 [6]; Drive low. Side-set still takes place before instruction stalls. + jmp !x do_zero side 1 [3]; Branch on the bit we shifted out previous delay. Drive high. + do_one: + jmp bitloop side 1 [4]; Continue driving high, for a one (long pulse) + do_zero: + nop side 0 [4]; Or drive low, for a zero (short pulse) .wrap """ @@ -35,7 +38,7 @@ sm = rp2pio.StateMachine( assembled, - frequency=800000 * 6, # 800khz * 6 clocks per bit + frequency=12_800_000, # to get appropriate sub-bit times in PIO program first_sideset_pin=NEOPIXEL, auto_pull=True, out_shift_right=False, From 387abbbc4c5f3e15eb00d2abb42d192c65667cf7 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 6 May 2022 09:47:50 -0500 Subject: [PATCH 047/124] Add neopixel background example --- examples/pioasm_neopixel_bg.py | 125 +++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 examples/pioasm_neopixel_bg.py diff --git a/examples/pioasm_neopixel_bg.py b/examples/pioasm_neopixel_bg.py new file mode 100644 index 0000000..018d976 --- /dev/null +++ b/examples/pioasm_neopixel_bg.py @@ -0,0 +1,125 @@ +# SPDX-FileCopyrightText: 2022 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +"""Demonstrate background writing with NeoPixels + +The NeoPixelBackground class defined here is largely compatible with the +standard NeoPixel class, except that the ``show()`` method returns immediately, +writing data to the LEDs in the background. + +Writing the LED data in the background will allow more time for your +Python code to run, so it may be possible to slightly increase the refresh +rate of your LEDs or do more complicated processing. + +The demonstration code, under ``if __name__ == '__main__':`` is intended +for the Adafruit MacroPad, with 12 NeoPixel LEDs. It shows a cycling rainbow +pattern across all the LEDs. +""" + +import struct +import adafruit_pixelbuf +from ulab import numpy as np +from rp2pio import StateMachine +from adafruit_pioasm import Program + +# Pixel color order constants +RGB = "RGB" +"""Red Green Blue""" +GRB = "GRB" +"""Green Red Blue""" +RGBW = "RGBW" +"""Red Green Blue White""" +GRBW = "GRBW" +"""Green Red Blue White""" + +# NeoPixels are 800khz bit streams. We are choosing zeros as <312ns hi, 936 lo> and ones +# and ones as <700 ns hi, 556 ns lo>. +# cycle. The first two instructions always run while only one of the two final +# instructions run per bit. We start with the low period because it can be +# longer while waiting for more data. +_program = Program( + """ +.side_set 1 opt +.wrap_target + pull block side 0 + out y, 16 side 0 ; get count of NeoPixel bits + +bitloop: + pull ifempty side 0 ; drive low + out x 1 side 0 [5] + jmp !x do_zero side 1 [3] ; drive high and branch depending on bit val + jmp y--, bitloop side 1 [4] ; drive high for a one (long pulse) + jmp end_sequence side 0 ; sequence is over + +do_zero: + jmp y--, bitloop side 0 [4] ; drive low for a zero (short pulse) + +end_sequence: + pull block side 0 ; get fresh 16 bit delay value + out y, 16 side 0 ; get delay count +wait_reset: + jmp y--, wait_reset side 0 ; wait until delay elapses +.wrap + """ +) + + +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 + ): + if not pixel_order: + pixel_order = GRB if bpp == 3 else GRBW + elif isinstance(pixel_order, tuple): + order_list = [RGBW[order] for order in pixel_order] + pixel_order = "".join(order_list) + + byte_count = bpp * n + bit_count = byte_count * 8 + padding_count = byte_count % 2 + + if bit_count > 65536: + raise ValueError("Too many pixels") + + # backwards, so that ulab byteswap corrects it! + header = struct.pack(">H", (bit_count - 1) & 0xFFFF) + trailer = b"\0" * padding_count + struct.pack(">H", 3840) + + self._sm = StateMachine( + _program.assembled, + auto_pull=False, + first_sideset_pin=pin, + out_shift_right=False, + pull_threshold=16, + frequency=12_800_000, + **_program.pio_kwargs, + ) + + super().__init__( + n, + brightness=brightness, + byteorder=pixel_order, + auto_write=auto_write, + header=header, + trailer=trailer, + ) + + def _transmit(self, buf): + self._sm.background_write(np.frombuffer(buf, dtype=np.uint16).byteswap()) + + +if __name__ == "__main__": + import board + import rainbowio + import time + + NEOPIXEL = board.NEOPIXEL + NUM_PIXELS = 12 + pixels = NeoPixelBackground(NEOPIXEL, NUM_PIXELS) + i = 0 + while True: + pixels.fill(rainbowio.colorwheel(i := (i + 1) % 256)) + time.sleep(0.01) From 079fe9e6c594f6393f704608bb03747e2034b910 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 6 May 2022 09:50:37 -0500 Subject: [PATCH 048/124] Fix comments based on review notes --- examples/pioasm_neopixel.py | 4 ++-- examples/pioasm_neopixel_bg.py | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/examples/pioasm_neopixel.py b/examples/pioasm_neopixel.py index 3d694e5..0c043d5 100644 --- a/examples/pioasm_neopixel.py +++ b/examples/pioasm_neopixel.py @@ -8,9 +8,9 @@ import microcontroller import adafruit_pioasm -# NeoPixels are 800khz bit streams. We are choosing zeros as <312ns hi, 936 lo> and ones +# NeoPixels are 800khz bit streams. We are choosing zeros as <312ns hi, 936 lo> # and ones as <700 ns hi, 556 ns lo>. -# cycle. The first two instructions always run while only one of the two final +# The first two instructions always run while only one of the two final # instructions run per bit. We start with the low period because it can be # longer while waiting for more data. program = """ diff --git a/examples/pioasm_neopixel_bg.py b/examples/pioasm_neopixel_bg.py index 018d976..c3ec39a 100644 --- a/examples/pioasm_neopixel_bg.py +++ b/examples/pioasm_neopixel_bg.py @@ -33,11 +33,8 @@ GRBW = "GRBW" """Green Red Blue White""" -# NeoPixels are 800khz bit streams. We are choosing zeros as <312ns hi, 936 lo> and ones +# NeoPixels are 800khz bit streams. We are choosing zeros as <312ns hi, 936 lo> # and ones as <700 ns hi, 556 ns lo>. -# cycle. The first two instructions always run while only one of the two final -# instructions run per bit. We start with the low period because it can be -# longer while waiting for more data. _program = Program( """ .side_set 1 opt From 082e0a1dc6c81f1aa5cce494db2ff497c4fcf751 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 6 May 2022 15:45:02 -0500 Subject: [PATCH 049/124] enhance examples Works in conjunction with https://github.com/adafruit/circuitpython/pull/6360 * get rid of the need for 'filler' in morse demo * make neopixels work super fast without explicit byteswapping --- examples/pioasm_background_morse.py | 17 ++++--------- examples/pioasm_neopixel_bg.py | 38 ++++++++++++++++++++++++----- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/examples/pioasm_background_morse.py b/examples/pioasm_background_morse.py index d6cdc89..ac169ed 100644 --- a/examples/pioasm_background_morse.py +++ b/examples/pioasm_background_morse.py @@ -17,8 +17,8 @@ https://en.wikipedia.org/wiki/Morse_code """ -import time import array +import time from board import LED from rp2pio import StateMachine from adafruit_pioasm import Program @@ -62,13 +62,6 @@ SOS = S + O + S + WORD_SPACE TEST = T + E + S + T + WORD_SPACE -# 8 slots of the shortest possible led-off time -# A background write is 'complete' as soon as all data has been placed -# in the StateMachine's FIFO. This FIFO has either 4 or 8 entries. -# By adding 8 very short "led off" times, we can have our 'sm.writing' test -# tell us when the actual letter data has all been -FILLER = array.array("H", [LED_OFF | 1]) * 8 - sm = StateMachine( pio_code.assembled, frequency=1_000_000, @@ -94,11 +87,11 @@ ("TEST", TEST), ): print(f"Sending out {plain}", end="") - sm.background_write(morse + FILLER) - while sm.writing: + sm.background_write(morse) + sm.clear_txstall() + while not sm.txstall: print(end=".") time.sleep(0.1) print() - print("Message all sent to StateMachine") - time.sleep(1) + print("Message all sent to StateMachine (including emptying FIFO)") print() diff --git a/examples/pioasm_neopixel_bg.py b/examples/pioasm_neopixel_bg.py index c3ec39a..7306c30 100644 --- a/examples/pioasm_neopixel_bg.py +++ b/examples/pioasm_neopixel_bg.py @@ -12,6 +12,10 @@ Python code to run, so it may be possible to slightly increase the refresh rate of your LEDs or do more complicated processing. +Because the pixelbuf storage is also being written out 'live', it is possible +(even with auto-show 'false') to experience tearing, where the LEDs are a +combination of old and new values at the same time. + The demonstration code, under ``if __name__ == '__main__':`` is intended for the Adafruit MacroPad, with 12 NeoPixel LEDs. It shows a cycling rainbow pattern across all the LEDs. @@ -19,7 +23,6 @@ import struct import adafruit_pixelbuf -from ulab import numpy as np from rp2pio import StateMachine from adafruit_pioasm import Program @@ -95,28 +98,51 @@ def __init__( **_program.pio_kwargs, ) + self._first = True super().__init__( n, brightness=brightness, byteorder=pixel_order, - auto_write=auto_write, + auto_write=False, header=header, trailer=trailer, ) + self._auto_write = False + self._auto_writing = False + self.auto_write = auto_write + + @property + def auto_write(self): + return self._auto_write + + @auto_write.setter + def auto_write(self, value): + self._auto_write = bool(value) + if not value and self._auto_writing: + self._sm.background_write() + self._auto_writing = False + elif value: + self.show() + def _transmit(self, buf): - self._sm.background_write(np.frombuffer(buf, dtype=np.uint16).byteswap()) + if self._auto_write: + if not self._auto_writing: + self._sm.background_write(loop=memoryview(buf).cast("H"), swap=True) + self._auto_writing = True + else: + self._sm.background_write(memoryview(buf).cast("H"), swap=True) if __name__ == "__main__": import board import rainbowio - import time + import supervisor NEOPIXEL = board.NEOPIXEL NUM_PIXELS = 12 pixels = NeoPixelBackground(NEOPIXEL, NUM_PIXELS) i = 0 while True: - pixels.fill(rainbowio.colorwheel(i := (i + 1) % 256)) - time.sleep(0.01) + # Around 1 cycle per second + pixels.fill(rainbowio.colorwheel(supervisor.ticks_ms() // 4)) From 7e9076dae2382fe61a1418ad67827a545f93d8c0 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sat, 7 May 2022 09:47:39 -0500 Subject: [PATCH 050/124] switch bit count from 16 bits to 32 bits, explain how auto_write behaves --- examples/pioasm_neopixel_bg.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/examples/pioasm_neopixel_bg.py b/examples/pioasm_neopixel_bg.py index 7306c30..9d88cc1 100644 --- a/examples/pioasm_neopixel_bg.py +++ b/examples/pioasm_neopixel_bg.py @@ -6,7 +6,8 @@ The NeoPixelBackground class defined here is largely compatible with the standard NeoPixel class, except that the ``show()`` method returns immediately, -writing data to the LEDs in the background. +writing data to the LEDs in the background, and setting `auto_write` to true +causes the data to be continuously sent to the LEDs all the time. Writing the LED data in the background will allow more time for your Python code to run, so it may be possible to slightly increase the refresh @@ -43,7 +44,7 @@ .side_set 1 opt .wrap_target pull block side 0 - out y, 16 side 0 ; get count of NeoPixel bits + out y, 32 side 0 ; get count of NeoPixel bits bitloop: pull ifempty side 0 ; drive low @@ -56,8 +57,8 @@ jmp y--, bitloop side 0 [4] ; drive low for a zero (short pulse) end_sequence: - pull block side 0 ; get fresh 16 bit delay value - out y, 16 side 0 ; get delay count + pull block side 0 ; get fresh delay value + out y, 32 side 0 ; get delay count wait_reset: jmp y--, wait_reset side 0 ; wait until delay elapses .wrap @@ -79,21 +80,18 @@ def __init__( byte_count = bpp * n bit_count = byte_count * 8 - padding_count = byte_count % 2 + padding_count = -byte_count % 4 - if bit_count > 65536: - raise ValueError("Too many pixels") - - # backwards, so that ulab byteswap corrects it! - header = struct.pack(">H", (bit_count - 1) & 0xFFFF) - trailer = b"\0" * padding_count + struct.pack(">H", 3840) + # backwards, so that dma byteswap corrects it! + header = struct.pack(">L", bit_count - 1) + trailer = b"\0" * padding_count + struct.pack(">L", 3840) self._sm = StateMachine( _program.assembled, auto_pull=False, first_sideset_pin=pin, out_shift_right=False, - pull_threshold=16, + pull_threshold=32, frequency=12_800_000, **_program.pio_kwargs, ) @@ -128,10 +126,10 @@ def auto_write(self, value): def _transmit(self, buf): if self._auto_write: if not self._auto_writing: - self._sm.background_write(loop=memoryview(buf).cast("H"), swap=True) + self._sm.background_write(loop=memoryview(buf).cast("L"), swap=True) self._auto_writing = True else: - self._sm.background_write(memoryview(buf).cast("H"), swap=True) + self._sm.background_write(memoryview(buf).cast("L"), swap=True) if __name__ == "__main__": From e55ff9b97cd3cc6661f90e0fbef3daee0592f51c Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 9 May 2022 06:11:16 -0500 Subject: [PATCH 051/124] Add 7-segment examples --- examples/pioasm_7seg.py | 178 ++++++++++++++++++++++++ examples/pioasm_7seg_fader.py | 253 ++++++++++++++++++++++++++++++++++ 2 files changed, 431 insertions(+) create mode 100644 examples/pioasm_7seg.py create mode 100644 examples/pioasm_7seg_fader.py diff --git a/examples/pioasm_7seg.py b/examples/pioasm_7seg.py new file mode 100644 index 0000000..7bd5805 --- /dev/null +++ b/examples/pioasm_7seg.py @@ -0,0 +1,178 @@ +# SPDX-FileCopyrightText: 2022 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +"""Drive a 7-segment display entirely from the PIO peripheral + +By updating the buffer being written to the display, the shown digits can be changed. + +The main program repeatedly shows random digits which 'lock' after a short +time. After all digits have locked, it blanks for a short time and then repeats. +It also demonstrates the use of `asyncio` to perform multiple tasks. + +This example is designed for a Raspberry Pi Pico and bare LED display. For +simplicity, it is wired without any current limiting resistors, instead relying +on a combination of the RP2040's pin drive strength and the 1/4 duty cycle to +limit LED current to an acceptable level, and longevity of the display was not +a priority. + +Before integrating a variant of this example code in a project, evaluate +whether your design needs to add current-limiting resistors. + +https://www.adafruit.com/product/4864 +https://www.adafruit.com/product/865 + +Wiring: + * Pico GP15 to LED matrix 1 (E SEG) + * Pico GP14 to LED matrix 2 (D SEG) + * Pico GP13 to LED matrix 3 (DP SEG) + * Pico GP12 to LED matrix 4 (C SEG) + * Pico GP11 to LED matrix 5 (G SEG) + * Pico GP10 to LED matrix 6 (COM4) + * Pico GP9 to LED matrix 7 (COLON COM) + * Pico GP22 to LED matrix 8 (COLON SEG) + * Pico GP21 to LED matrix 9 (B SEG) + * Pico GP20 to LED matrix 10 (COM3) + * Pico GP19 to LED matrix 11 (COM2) + * Pico GP18 to LED matrix 12 (F SEG) + * Pico GP17 to LED matrix 13 (A SEG) + * Pico GP16 to LED matrix 14 (COM1) +""" + +import asyncio +import random +import array +import board +import rp2pio +import adafruit_pioasm + +_program = adafruit_pioasm.Program( + """ + out pins, 14 ; set the pins to their new state + """ +) + +# Display Pins 1-7 are GP 15-9 +# Display Pins 8-12 are GP 22-16 +COM1_WT = 1 << 7 +COM2_WT = 1 << 10 +COM3_WT = 1 << 11 +COM4_WT = 1 << 1 +COMC_WT = 1 << 0 + +SEGA_WT = 1 << 8 +SEGB_WT = 1 << 12 +SEGC_WT = 1 << 3 +SEGD_WT = 1 << 5 +SEGE_WT = 1 << 6 +SEGF_WT = 1 << 9 +SEGG_WT = 1 << 2 + +SEGDP_WT = 1 << 4 +SEGCOL_WT = 1 << 13 + +ALL_COM = COM1_WT | COM2_WT | COM3_WT | COM4_WT | COMC_WT + +SEG_WT = [ + SEGA_WT, + SEGB_WT, + SEGC_WT, + SEGD_WT, + SEGE_WT, + SEGF_WT, + SEGG_WT, + SEGDP_WT, + SEGCOL_WT, +] +COM_WT = [COM1_WT, COM2_WT, COM3_WT, COM4_WT, COMC_WT] + +DIGITS = [ + 0b0111111, # 0 + 0b0000110, # 1 + 0b1011011, # 2 + 0b1001111, # 3 + 0b1100110, # 4 + 0b1101101, # 5 + 0b1111100, # 6 + 0b0000111, # 7 + 0b1111111, # 8 + 0b1101111, # 9 +] + + +def make_digit_wt(v): + val = ALL_COM + seg = DIGITS[v] + for i in range(8): + if seg & (1 << i): + val |= SEG_WT[i] + return val + + +DIGITS_WT = [make_digit_wt(i) for i in range(10)] + + +class SMSevenSegment: + def __init__(self, first_pin=board.GP9): + self._buf = array.array("H", (DIGITS_WT[0] & ~COM_WT[i] for i in range(4))) + self._sm = rp2pio.StateMachine( + _program.assembled, + frequency=2000, + first_out_pin=first_pin, + out_pin_count=14, + auto_pull=True, + pull_threshold=14, + **_program.pio_kwargs, + ) + self._sm.background_write(loop=self._buf) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.deinit() + + def deinit(self): + self._sm.deinit() + + def __setitem__(self, i, v): + if v is None: + self._buf[i] = 0 + else: + self._buf[i] = DIGITS_WT[v] & ~COM_WT[i] + + +async def digit_locker(s, i, wait): + delay = 30 + d = random.randint(0, 9) + while delay < 300: + d = (d + random.randint(1, 9)) % 10 # Tick to a new digit other than 'd' + s[i] = d + await asyncio.sleep(delay / 1000) + if wait: + wait -= 1 + else: + delay = delay * 1.1 + + +def shuffle(seq): + for i in range(len(seq) - 1): + j = random.randrange(i + 1, len(seq)) + seq[i], seq[j] = seq[j], seq[i] + + +async def main(): + waits = [100, 175, 225, 250] + with SMSevenSegment(board.GP9) as s: + while True: + shuffle(waits) + await asyncio.gather( + *(digit_locker(s, i, di) for i, di in enumerate(waits)) + ) + await asyncio.sleep(1) + for i in range(4): + s[i] = None + await asyncio.sleep(0.5) + + +asyncio.run(main()) diff --git a/examples/pioasm_7seg_fader.py b/examples/pioasm_7seg_fader.py new file mode 100644 index 0000000..f6d5117 --- /dev/null +++ b/examples/pioasm_7seg_fader.py @@ -0,0 +1,253 @@ +# SPDX-FileCopyrightText: 2022 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +"""Drive a 7-segment display entirely from the PIO peripheral + +Each segment is driven with a 'breathing' waveform that runs at its own pace. +It also demonstrates the use of `asyncio` to perform multiple tasks. + +This example is designed for a Raspberry Pi Pico and bare LED display. For +simplicity, it is wired without any current limiting resistors, instead relying +on a combination of the RP2040's pin drive strength and the 1/45 duty cycle to +limit LED current to an acceptable level, and longevity of the display was not +a priority. + +Before integrating a variant of this example code in a project, evaluate +whether your design needs to add current-limiting resistors. + +https://www.adafruit.com/product/4864 +https://www.adafruit.com/product/865 + +Wiring: + * Pico GP15 to LED matrix 1 (E SEG) + * Pico GP14 to LED matrix 2 (D SEG) + * Pico GP13 to LED matrix 3 (DP SEG) + * Pico GP12 to LED matrix 4 (C SEG) + * Pico GP11 to LED matrix 5 (G SEG) + * Pico GP10 to LED matrix 6 (COM4) + * Pico GP9 to LED matrix 7 (COLON COM) + * Pico GP22 to LED matrix 8 (COLON SEG) + * Pico GP21 to LED matrix 9 (B SEG) + * Pico GP20 to LED matrix 10 (COM3) + * Pico GP19 to LED matrix 11 (COM2) + * Pico GP18 to LED matrix 12 (F SEG) + * Pico GP17 to LED matrix 13 (A SEG) + * Pico GP16 to LED matrix 14 (COM1) +""" + +import asyncio +import random +import array +import board +import rp2pio +from ulab import numpy as np +import adafruit_pioasm + +_pio_source = """ + mov pins, null ; turn all pins off + pull + out pindirs, {n} ; set the new direction + pull + out pins, {n} ; set the new values + pull + out y, 32 +delay: + jmp y--, delay + """ + +# Display Pins 1-7 are GP 15-9 [need to re-wire 9] +# Display Pins 8-12 are GP 22-16 [need to re-wire 22] +# GP# Display# Function +# 15 (+ 6) 1 E SEG +# 14 (+ 5) 2 D SEG +# 13 (+ 4) 3 DP SEG +# 12 (+ 3) 4 C SEG +# 11 (+ 2) 5 G SEG +# 10 (+ 1) 6 COM4 +# 9 (+ 0) 7 COLON COM +# 22 (+13) 8 COLON SEG +# 21 (+12) 9 B SEG +# 20 (+11) 10 COM3 +# 19 (+10) 11 COM2 +# 18 (+ 9) 12 F SEG +# 17 (+ 8) 13 A SEG +# 16 (+ 7) 14 COM1 + +COM1_WT = 1 << 7 +COM2_WT = 1 << 10 +COM3_WT = 1 << 11 +COM4_WT = 1 << 1 +COMC_WT = 1 << 0 + +SEGA_WT = 1 << 8 +SEGB_WT = 1 << 12 +SEGC_WT = 1 << 3 +SEGD_WT = 1 << 5 +SEGE_WT = 1 << 6 +SEGF_WT = 1 << 9 +SEGG_WT = 1 << 2 + +SEGDP_WT = 1 << 4 +SEGCOL_WT = 1 << 13 + +ALL_COM = COM1_WT | COM2_WT | COM3_WT | COM4_WT | COMC_WT + +SEG_WT = [ + SEGA_WT, + SEGB_WT, + SEGC_WT, + SEGD_WT, + SEGE_WT, + SEGF_WT, + SEGG_WT, + SEGDP_WT, + SEGCOL_WT, +] +COM_WT = [COM1_WT, COM2_WT, COM3_WT, COM4_WT, COMC_WT] + +DIGITS = [ + 0b0111111, # 0 + 0b0000110, # 1 + 0b1011011, # 2 + 0b1001111, # 3 + 0b1100110, # 4 + 0b1101101, # 5 + 0b1111100, # 6 + 0b0000111, # 7 + 0b1111111, # 8 + 0b1101111, # 9 +] + + +def make_digit_wt(v): + val = ALL_COM + seg = DIGITS[v] + for i in range(8): + if seg & (1 << i): + val |= SEG_WT[i] + return val + + +class LedFader: + def __init__( + self, first_pin, pin_count, cathode_weights, anode_weights, levels=64 + ): # pylint: disable=too-many-arguments + 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._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 + program.assembled, + frequency=125_000_000, + first_out_pin=first_pin, + out_pin_count=14, + auto_pull=True, + pull_threshold=14, + **program.pio_kwargs, + ) + print( + f"Note: approximate refresh rate {self._sm.frequency / self._max_count:.0f}Hz" + ) + self._sm.background_write(loop=self._stream) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.deinit() + + def deinit(self): + self._sm.deinit() + + def __setitem__(self, i, v): + if not 0 <= v < self._levels: + raise ValueError() + + c = i % len(self._cathode_weights) + r = i // len(self._cathode_weights) + if not v: + self._total = self._total - self._stream[3 * i + 2] + 1 + self._stream[3 * i] = 0 + self._stream[3 * i + 1] = 0 + self._stream[3 * i + 2] = 1 + else: + self._total = self._total - self._stream[3 * i + 2] + v + self._stream[3 * i] = self._cathode_weights[c] | self._anode_weights[r] + self._stream[3 * i + 1] = self._cathode_weights[c] + self._stream[3 * i + 2] = v + self._stream[3 * len(self) + 2] = self._max_count - self._total + + def __len__(self): + return len(self._stream) // 3 - 1 + + +class CyclicSignal: + def __init__(self, data, phase=0): + self._data = data + self._phase = 0 + self.phase = phase + self._scale = len(self._data) - 1 + + @property + def phase(self): + return self._phase + + @phase.setter + def phase(self, value): + self._phase = value % 1 + + @property + def value(self): + idxf = self._phase * len(self._data) + idx = int(idxf) + frac = idxf % 1 + idx1 = (idx + 1) % len(self._data) + val = self._data[idx] + val1 = self._data[idx1] + return val + (val1 - val) * frac + + def advance(self, delta): + self._phase = (self._phase + delta) % 1 + + +sine = (np.sin(np.linspace(0, 2 * np.pi, 50, endpoint=False)) * 0.5 + 0.5) ** 2.2 * 64 + + +async def segment_throbber(c, i): + signal = CyclicSignal(sine, random.random()) + velocity = random.random() * 0.04 + 0.005 + + while True: + signal.advance(velocity) + c[i] = int(signal.value) + await asyncio.sleep(0) + + +async def main(): + with LedFader( + board.GP9, + 14, + ( + SEGA_WT, + SEGB_WT, + SEGC_WT, + SEGD_WT, + SEGE_WT, + SEGF_WT, + SEGG_WT, + SEGDP_WT, + SEGCOL_WT, + ), + (COM1_WT, COM2_WT, COM3_WT, COM4_WT, COMC_WT), + ) as c: + await asyncio.gather(*(segment_throbber(c, i) for i in range(len(c)))) + + +asyncio.run(main()) From 3b495653c013cc938af14625cd3e49063879cfcf Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 10 May 2022 09:16:04 -0500 Subject: [PATCH 052/124] Simplify example into a counter --- examples/pioasm_7seg.py | 54 +++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/examples/pioasm_7seg.py b/examples/pioasm_7seg.py index 7bd5805..1961381 100644 --- a/examples/pioasm_7seg.py +++ b/examples/pioasm_7seg.py @@ -6,9 +6,7 @@ By updating the buffer being written to the display, the shown digits can be changed. -The main program repeatedly shows random digits which 'lock' after a short -time. After all digits have locked, it blanks for a short time and then repeats. -It also demonstrates the use of `asyncio` to perform multiple tasks. +The main program just counts up, looping back to 0000 after 9999. This example is designed for a Raspberry Pi Pico and bare LED display. For simplicity, it is wired without any current limiting resistors, instead relying @@ -39,9 +37,8 @@ * Pico GP16 to LED matrix 14 (COM1) """ -import asyncio -import random import array +import time import board import rp2pio import adafruit_pioasm @@ -141,38 +138,25 @@ def __setitem__(self, i, v): else: self._buf[i] = DIGITS_WT[v] & ~COM_WT[i] - -async def digit_locker(s, i, wait): - delay = 30 - d = random.randint(0, 9) - while delay < 300: - d = (d + random.randint(1, 9)) % 10 # Tick to a new digit other than 'd' - s[i] = d - await asyncio.sleep(delay / 1000) - if wait: - wait -= 1 - else: - delay = delay * 1.1 + def set_number(self, number): + for j in range(4): + self[3 - j] = number % 10 + number //= 10 -def shuffle(seq): - for i in range(len(seq) - 1): - j = random.randrange(i + 1, len(seq)) - seq[i], seq[j] = seq[j], seq[i] +def count(start=0): + val = start + while True: + yield val + val += 1 -async def main(): - waits = [100, 175, 225, 250] +def main(): with SMSevenSegment(board.GP9) as s: - while True: - shuffle(waits) - await asyncio.gather( - *(digit_locker(s, i, di) for i, di in enumerate(waits)) - ) - await asyncio.sleep(1) - for i in range(4): - s[i] = None - await asyncio.sleep(0.5) - - -asyncio.run(main()) + for i in count(): + s.set_number(i) + time.sleep(0.05) + + +if __name__ == "__main__": + main() From c321011de60cd0c1a00c7c1d3cc2d103c0cfbe50 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 10 May 2022 11:31:11 -0500 Subject: [PATCH 053/124] get rid of unused variable --- examples/pioasm_neopixel_bg.py | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/pioasm_neopixel_bg.py b/examples/pioasm_neopixel_bg.py index 9d88cc1..19c3948 100644 --- a/examples/pioasm_neopixel_bg.py +++ b/examples/pioasm_neopixel_bg.py @@ -140,7 +140,6 @@ def _transmit(self, buf): NEOPIXEL = board.NEOPIXEL NUM_PIXELS = 12 pixels = NeoPixelBackground(NEOPIXEL, NUM_PIXELS) - i = 0 while True: # Around 1 cycle per second pixels.fill(rainbowio.colorwheel(supervisor.ticks_ms() // 4)) From f48569464eb4eefb1510e0ae48f0ed68c22b17d4 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 11 May 2022 07:46:16 -0500 Subject: [PATCH 054/124] undo folder structure .. as requested by limor during guide review --- .../{pioasm_pico-examples/blink.py => pioasm_blink.py} | 0 examples/pioasm_getting-started/README.md | 7 ------- .../{pioasm_pico-examples/hello.py => pioasm_hello.py} | 0 .../led_brightness.py => pioasm_led_brightness.py} | 0 examples/pioasm_pico-examples/README.md | 7 ------- 5 files changed, 14 deletions(-) rename examples/{pioasm_pico-examples/blink.py => pioasm_blink.py} (100%) delete mode 100644 examples/pioasm_getting-started/README.md rename examples/{pioasm_pico-examples/hello.py => pioasm_hello.py} (100%) rename examples/{pioasm_getting-started/led_brightness.py => pioasm_led_brightness.py} (100%) delete mode 100644 examples/pioasm_pico-examples/README.md diff --git a/examples/pioasm_pico-examples/blink.py b/examples/pioasm_blink.py similarity index 100% rename from examples/pioasm_pico-examples/blink.py rename to examples/pioasm_blink.py diff --git a/examples/pioasm_getting-started/README.md b/examples/pioasm_getting-started/README.md deleted file mode 100644 index 639ed26..0000000 --- a/examples/pioasm_getting-started/README.md +++ /dev/null @@ -1,7 +0,0 @@ - - -These examples are adapted from [Getting started with MicroPython on the Raspberry Pi Pico](https://www.adafruit.com/product/4898). You can also get an [electonic copy](https://hackspace.raspberrypi.org/books/micropython-pico/). PIO is covered in Appendix C. diff --git a/examples/pioasm_pico-examples/hello.py b/examples/pioasm_hello.py similarity index 100% rename from examples/pioasm_pico-examples/hello.py rename to examples/pioasm_hello.py diff --git a/examples/pioasm_getting-started/led_brightness.py b/examples/pioasm_led_brightness.py similarity index 100% rename from examples/pioasm_getting-started/led_brightness.py rename to examples/pioasm_led_brightness.py diff --git a/examples/pioasm_pico-examples/README.md b/examples/pioasm_pico-examples/README.md deleted file mode 100644 index e6f42e1..0000000 --- a/examples/pioasm_pico-examples/README.md +++ /dev/null @@ -1,7 +0,0 @@ - - -These examples are adapted from the pio folder of the [Raspberry Pi Pico SDK Examples](https://github.com/raspberrypi/pico-examples). From d53adc1205f5cc2b26f8be11799995436b5d7e01 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Sun, 15 May 2022 12:49:38 -0400 Subject: [PATCH 055/124] Patch .pre-commit-config.yaml --- .pre-commit-config.yaml | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7467c1d..3343606 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,40 +3,40 @@ # SPDX-License-Identifier: Unlicense repos: -- repo: https://github.com/python/black + - repo: https://github.com/python/black rev: 22.3.0 hooks: - - id: black -- repo: https://github.com/fsfe/reuse-tool - rev: v0.12.1 + - id: black + - repo: https://github.com/fsfe/reuse-tool + rev: v0.14.0 hooks: - - id: reuse -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.3.0 + - id: reuse + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.2.0 hooks: - - id: check-yaml - - id: end-of-file-fixer - - id: trailing-whitespace -- repo: https://github.com/pycqa/pylint + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + - repo: https://github.com/pycqa/pylint rev: v2.11.1 hooks: - - id: pylint + - id: pylint name: pylint (library code) types: [python] args: - --disable=consider-using-f-string exclude: "^(docs/|examples/|tests/|setup.py$)" - - id: pylint + - 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 + - --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 + - --disable=missing-docstring,consider-using-f-string,duplicate-code From cf5dfc211a14ed53dbda543951bf790aca08b661 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Sun, 22 May 2022 00:18:55 -0400 Subject: [PATCH 056/124] Increase min lines similarity Signed-off-by: Alec Delaney --- .pylintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pylintrc b/.pylintrc index 08e12bf..1f42e5d 100644 --- a/.pylintrc +++ b/.pylintrc @@ -252,7 +252,7 @@ ignore-docstrings=yes ignore-imports=yes # Minimum lines number of a similarity. -min-similarity-lines=4 +min-similarity-lines=12 [BASIC] From d8d9e0b63dbb90dcd4024af55c8bb2994d1d627d Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Mon, 30 May 2022 14:25:04 -0400 Subject: [PATCH 057/124] Set language to "en" for documentation Signed-off-by: Alec Delaney --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 131c8d1..5fab004 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -63,7 +63,7 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. From d451e5462f57273fd1b2b91c056fc6c6b8526903 Mon Sep 17 00:00:00 2001 From: evaherrada Date: Tue, 7 Jun 2022 15:34:48 -0400 Subject: [PATCH 058/124] Added cp.org link to index.rst --- docs/index.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 3c680d9..da492bc 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -37,7 +37,8 @@ Table of Contents .. toctree:: :caption: Other Links - Download + Download from GitHub + Download Library Bundle CircuitPython Reference Documentation CircuitPython rp2pio Reference Documentation CircuitPython Support Forum From db09380c37f1953eefd0a1849e9464d3556ba45c Mon Sep 17 00:00:00 2001 From: evaherrada Date: Fri, 22 Jul 2022 13:59:10 -0400 Subject: [PATCH 059/124] Changed .env to .venv in README.rst --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index b245e27..b7ae53a 100644 --- a/README.rst +++ b/README.rst @@ -51,8 +51,8 @@ To install in a virtual environment in your current project: .. code-block:: shell mkdir project-name && cd project-name - python3 -m venv .env - source .env/bin/activate + python3 -m venv .venv + source .venv/bin/activate pip3 install adafruit-circuitpython-pioasm Usage Example From 0a0c1f4fafc14cff9980f0eea6667ce4277c672b Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Mon, 8 Aug 2022 22:05:55 -0400 Subject: [PATCH 060/124] Switched to pyproject.toml --- .github/workflows/build.yml | 22 ++++++------- .github/workflows/release.yml | 17 +++++----- optional_requirements.txt | 3 ++ pyproject.toml | 46 ++++++++++++++++++++++++--- requirements.txt | 5 ++- setup.py | 58 ----------------------------------- 6 files changed, 67 insertions(+), 84 deletions(-) create mode 100644 optional_requirements.txt delete mode 100644 setup.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f11c8c0..22f6582 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,13 +47,11 @@ jobs: pip install --force-reinstall Sphinx sphinx-rtd-theme pre-commit - name: Library version run: git describe --dirty --always --tags + - name: Setup problem matchers + uses: adafruit/circuitpython-action-library-ci-problem-matchers@v1 - name: Pre-commit hooks run: | pre-commit run --all-files - - name: Run tests - run: | - cd tests/ && python -m unittest discover - cd .. - name: Build assets run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location . - name: Archive bundles @@ -64,16 +62,16 @@ jobs: - name: Build docs working-directory: docs run: sphinx-build -E -W -b html . _build/html - - name: Check For setup.py + - name: Check For pyproject.toml id: need-pypi run: | - echo ::set-output name=setup-py::$( find . -wholename './setup.py' ) + echo ::set-output name=pyproject-toml::$( find . -wholename './pyproject.toml' ) - name: Build Python package - if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') + if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') run: | - pip install --upgrade setuptools wheel twine readme_renderer testresources - python setup.py sdist - python setup.py bdist_wheel --universal + pip install --upgrade build twine + for file in $(find -not -path "./.*" -not -path "./docs*" \( -name "*.py" -o -name "*.toml" \) ); do + sed -i -e "s/0.0.0-auto.0/1.2.3/" $file; + done; + python -m build twine check dist/* - - name: Setup problem matchers - uses: adafruit/circuitpython-action-library-ci-problem-matchers@v1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a65e5de..d1b4f8d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -61,25 +61,28 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - - name: Check For setup.py + - name: Check For pyproject.toml id: need-pypi run: | - echo ::set-output name=setup-py::$( find . -wholename './setup.py' ) + echo ::set-output name=pyproject-toml::$( find . -wholename './pyproject.toml' ) - name: Set up Python - if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') + if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') uses: actions/setup-python@v2 with: python-version: '3.x' - name: Install dependencies - if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') + if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') run: | python -m pip install --upgrade pip - pip install setuptools wheel twine + pip install --upgrade build twine - name: Build and publish - if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') + if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') env: TWINE_USERNAME: ${{ secrets.pypi_username }} TWINE_PASSWORD: ${{ secrets.pypi_password }} run: | - python setup.py sdist + for file in $(find -not -path "./.*" -not -path "./docs*" \( -name "*.py" -o -name "*.toml" \) ); do + sed -i -e "s/0.0.0-auto.0/${{github.event.release.tag_name}}/" $file; + done; + python -m build twine upload dist/* diff --git a/optional_requirements.txt b/optional_requirements.txt new file mode 100644 index 0000000..d4e27c4 --- /dev/null +++ b/optional_requirements.txt @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense diff --git a/pyproject.toml b/pyproject.toml index f3c35ae..20d23f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,44 @@ -# SPDX-FileCopyrightText: 2020 Diego Elio Pettenò +# SPDX-FileCopyrightText: 2022 Alec Delaney for Adafruit Industries # -# SPDX-License-Identifier: Unlicense +# SPDX-License-Identifier: MIT -[tool.black] -target-version = ['py35'] +[build-system] +requires = [ + "setuptools", + "wheel", +] + +[project] +name = "adafruit-circuitpython-pioasm" +description = "Simple assembler to convert pioasm to bytes" +version = "0.0.0-auto.0" +readme = "README.rst" +authors = [ + {name = "Adafruit Industries", email = "circuitpython@adafruit.com"} +] +urls = {Homepage = "https://github.com/adafruit/Adafruit_CircuitPython_PIOASM"} +keywords = [ + "adafruit", + "blinka", + "circuitpython", + "micropython", + "pioasm", + "rp2040", +] +license = {text = "MIT"} +classifiers = [ + "Intended Audience :: Developers", + "Topic :: Software Development :: Libraries", + "Topic :: Software Development :: Embedded Systems", + "Topic :: System :: Hardware", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", +] +dynamic = ["dependencies", "optional-dependencies"] + +[tool.setuptools] +py-modules = ["adafruit_pioasm"] + +[tool.setuptools.dynamic] +dependencies = {file = ["requirements.txt"]} +optional-dependencies = {optional = {file = ["optional_requirements.txt"]}} diff --git a/requirements.txt b/requirements.txt index 06c2f1e..7a984a4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ -# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries -# SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries LLC +# SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries # -# SPDX-License-Identifier: MIT +# SPDX-License-Identifier: Unlicense Adafruit-Blinka diff --git a/setup.py b/setup.py deleted file mode 100644 index 266b40e..0000000 --- a/setup.py +++ /dev/null @@ -1,58 +0,0 @@ -# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries -# SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries LLC -# -# SPDX-License-Identifier: MIT - -"""A setuptools based setup module. - -See: -https://packaging.python.org/en/latest/distributing.html -https://github.com/pypa/sampleproject -""" - -from setuptools import setup, find_packages - -# To use a consistent encoding -from codecs import open -from os import path - -here = path.abspath(path.dirname(__file__)) - -# Get the long description from the README file -with open(path.join(here, "README.rst"), encoding="utf-8") as f: - long_description = f.read() - -setup( - name="adafruit-circuitpython-pioasm", - use_scm_version=True, - setup_requires=["setuptools_scm"], - description="Simple assembler to convert pioasm to bytes", - long_description=long_description, - long_description_content_type="text/x-rst", - # The project's main homepage. - url="https://github.com/adafruit/Adafruit_CircuitPython_PIOASM", - # Author details - author="Adafruit Industries", - author_email="circuitpython@adafruit.com", - install_requires=[ - "Adafruit-Blinka", - ], - # Choose your license - license="MIT", - # See https://pypi.python.org/pypi?%3Aaction=list_classifiers - classifiers=[ - "Development Status :: 3 - Alpha", - "Intended Audience :: Developers", - "Topic :: Software Development :: Libraries", - "Topic :: System :: Hardware", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3", - ], - # What does your project relate to? - keywords="adafruit blinka circuitpython micropython pioasm rp2040", - # You can just specify the packages manually here if your project is - # simple. Or you can use find_packages(). - # TODO: IF LIBRARY FILES ARE A PACKAGE FOLDER, - # CHANGE `py_modules=['...']` TO `packages=['...']` - py_modules=["adafruit_pioasm"], -) From fca186158b425163614dd207b1d372a6f3c811c9 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Tue, 9 Aug 2022 12:03:54 -0400 Subject: [PATCH 061/124] Add setuptools-scm to build system requirements Signed-off-by: Alec Delaney --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 20d23f0..67c71f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,7 @@ requires = [ "setuptools", "wheel", + "setuptools-scm", ] [project] From 31fabd615068f12c77b314d048d4e5d35c9217e7 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Tue, 16 Aug 2022 18:09:15 -0400 Subject: [PATCH 062/124] Update version string --- adafruit_pioasm.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 5b4044b..a6a4ebf 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -17,7 +17,7 @@ splitter = re.compile(r",\s*|\s+(?:,\s*)?").split mov_splitter = re.compile("!|~|::").split -__version__ = "0.0.0-auto.0" +__version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PIOASM.git" CONDITIONS = ["", "!x", "x--", "!y", "y--", "x!=y", "pin", "!osre"] diff --git a/pyproject.toml b/pyproject.toml index 67c71f6..4cfa6af 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ requires = [ [project] name = "adafruit-circuitpython-pioasm" description = "Simple assembler to convert pioasm to bytes" -version = "0.0.0-auto.0" +version = "0.0.0+auto.0" readme = "README.rst" authors = [ {name = "Adafruit Industries", email = "circuitpython@adafruit.com"} From 16ca11e918191b0313e99b3de2042553488e7fba Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Tue, 16 Aug 2022 21:09:16 -0400 Subject: [PATCH 063/124] Fix version strings in workflow files --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 22f6582..cb2f60e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -71,7 +71,7 @@ jobs: run: | pip install --upgrade build twine for file in $(find -not -path "./.*" -not -path "./docs*" \( -name "*.py" -o -name "*.toml" \) ); do - sed -i -e "s/0.0.0-auto.0/1.2.3/" $file; + sed -i -e "s/0.0.0+auto.0/1.2.3/" $file; done; python -m build twine check dist/* diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d1b4f8d..f3a0325 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -82,7 +82,7 @@ jobs: TWINE_PASSWORD: ${{ secrets.pypi_password }} run: | for file in $(find -not -path "./.*" -not -path "./docs*" \( -name "*.py" -o -name "*.toml" \) ); do - sed -i -e "s/0.0.0-auto.0/${{github.event.release.tag_name}}/" $file; + sed -i -e "s/0.0.0+auto.0/${{github.event.release.tag_name}}/" $file; done; python -m build twine upload dist/* From dc9d986ce1d52436bd25cd414242b82bc97fcaf3 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Mon, 22 Aug 2022 21:36:33 -0400 Subject: [PATCH 064/124] Keep copyright up to date in documentation --- docs/conf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 5fab004..5f90765 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -6,6 +6,7 @@ import os import sys +import datetime sys.path.insert(0, os.path.abspath("..")) @@ -46,7 +47,8 @@ # General information about the project. project = "Adafruit PIOASM Library" -copyright = "2021 Scott Shawcroft" +current_year = str(datetime.datetime.now().year) +copyright = current_year + " Scott Shawcroft" author = "Scott Shawcroft" # The version info for the project you're documenting, acts as replacement for From 6cd4217524c9f1d8d13c9b404c99c14f02bca6be Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Tue, 23 Aug 2022 17:26:22 -0400 Subject: [PATCH 065/124] Use year duration range for copyright attribution --- docs/conf.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 5f90765..148956f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,8 +47,14 @@ # General information about the project. project = "Adafruit PIOASM Library" +creation_year = "2021" current_year = str(datetime.datetime.now().year) -copyright = current_year + " Scott Shawcroft" +year_duration = ( + current_year + if current_year == creation_year + else creation_year + " - " + current_year +) +copyright = year_duration + " Scott Shawcroft" author = "Scott Shawcroft" # The version info for the project you're documenting, acts as replacement for From 5f1285d5d8b57fcf5bb0a37339ea2bfd3e1724bc Mon Sep 17 00:00:00 2001 From: Paul Cutler Date: Tue, 4 Oct 2022 11:32:46 -0500 Subject: [PATCH 066/124] Fix issue #50 - E is a DIT and T is a DAH --- examples/pioasm_background_morse.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pioasm_background_morse.py b/examples/pioasm_background_morse.py index ac169ed..af5714f 100644 --- a/examples/pioasm_background_morse.py +++ b/examples/pioasm_background_morse.py @@ -55,10 +55,10 @@ WORD_SPACE = array.array("H", [LED_OFF | (4 * DIT_DURATION)]) # Letters and words can be created by concatenating ("+") the elements -E = DAH + LETTER_SPACE +E = DIT + LETTER_SPACE O = DAH + DAH + DAH + LETTER_SPACE S = DIT + DIT + DIT + LETTER_SPACE -T = DIT + LETTER_SPACE +T = DAH + LETTER_SPACE SOS = S + O + S + WORD_SPACE TEST = T + E + S + T + WORD_SPACE From 3994f4b234001a0123ae7916e668a1a694cc2062 Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 4 Nov 2022 00:02:50 -0400 Subject: [PATCH 067/124] Switching to composite actions --- .github/workflows/build.yml | 67 +---------------------- .github/workflows/release.yml | 88 ------------------------------ .github/workflows/release_gh.yml | 14 +++++ .github/workflows/release_pypi.yml | 14 +++++ 4 files changed, 30 insertions(+), 153 deletions(-) delete mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/release_gh.yml create mode 100644 .github/workflows/release_pypi.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb2f60e..041a337 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,68 +10,5 @@ jobs: test: runs-on: ubuntu-latest steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - name: Translate Repo Name For Build Tools filename_prefix - id: repo-name - run: | - echo ::set-output name=repo-name::$( - echo ${{ github.repository }} | - awk -F '\/' '{ print tolower($2) }' | - tr '_' '-' - ) - - name: Set up Python 3.x - uses: actions/setup-python@v2 - with: - python-version: "3.x" - - name: Versions - run: | - python3 --version - - name: Checkout Current Repo - uses: actions/checkout@v1 - with: - submodules: true - - name: Checkout tools repo - uses: actions/checkout@v2 - with: - repository: adafruit/actions-ci-circuitpython-libs - path: actions-ci - - name: Install dependencies - # (e.g. - apt-get: gettext, etc; pip: circuitpython-build-tools, requirements.txt; etc.) - run: | - source actions-ci/install.sh - - name: Pip install Sphinx, pre-commit - run: | - pip install --force-reinstall Sphinx sphinx-rtd-theme pre-commit - - name: Library version - run: git describe --dirty --always --tags - - name: Setup problem matchers - uses: adafruit/circuitpython-action-library-ci-problem-matchers@v1 - - name: Pre-commit hooks - run: | - pre-commit run --all-files - - name: Build assets - run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location . - - name: Archive bundles - uses: actions/upload-artifact@v2 - with: - name: bundles - path: ${{ github.workspace }}/bundles/ - - name: Build docs - working-directory: docs - run: sphinx-build -E -W -b html . _build/html - - name: Check For pyproject.toml - id: need-pypi - run: | - echo ::set-output name=pyproject-toml::$( find . -wholename './pyproject.toml' ) - - name: Build Python package - if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') - run: | - pip install --upgrade build twine - for file in $(find -not -path "./.*" -not -path "./docs*" \( -name "*.py" -o -name "*.toml" \) ); do - sed -i -e "s/0.0.0+auto.0/1.2.3/" $file; - done; - python -m build - twine check dist/* + - name: Run Build CI workflow + uses: adafruit/workflows-circuitpython-libs/build@main diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index f3a0325..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,88 +0,0 @@ -# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries -# -# SPDX-License-Identifier: MIT - -name: Release Actions - -on: - release: - types: [published] - -jobs: - upload-release-assets: - runs-on: ubuntu-latest - steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - name: Translate Repo Name For Build Tools filename_prefix - id: repo-name - run: | - echo ::set-output name=repo-name::$( - echo ${{ github.repository }} | - awk -F '\/' '{ print tolower($2) }' | - tr '_' '-' - ) - - name: Set up Python 3.x - uses: actions/setup-python@v2 - with: - python-version: "3.x" - - name: Versions - run: | - python3 --version - - name: Checkout Current Repo - uses: actions/checkout@v1 - with: - submodules: true - - name: Checkout tools repo - uses: actions/checkout@v2 - with: - repository: adafruit/actions-ci-circuitpython-libs - path: actions-ci - - name: Install deps - run: | - source actions-ci/install.sh - - name: Build assets - run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location . - - name: Upload Release Assets - # the 'official' actions version does not yet support dynamically - # supplying asset names to upload. @csexton's version chosen based on - # discussion in the issue below, as its the simplest to implement and - # allows for selecting files with a pattern. - # https://github.com/actions/upload-release-asset/issues/4 - #uses: actions/upload-release-asset@v1.0.1 - uses: csexton/release-asset-action@master - with: - pattern: "bundles/*" - github-token: ${{ secrets.GITHUB_TOKEN }} - - upload-pypi: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Check For pyproject.toml - id: need-pypi - run: | - echo ::set-output name=pyproject-toml::$( find . -wholename './pyproject.toml' ) - - name: Set up Python - if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Install dependencies - if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') - run: | - python -m pip install --upgrade pip - pip install --upgrade build twine - - name: Build and publish - if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') - env: - TWINE_USERNAME: ${{ secrets.pypi_username }} - TWINE_PASSWORD: ${{ secrets.pypi_password }} - run: | - for file in $(find -not -path "./.*" -not -path "./docs*" \( -name "*.py" -o -name "*.toml" \) ); do - sed -i -e "s/0.0.0+auto.0/${{github.event.release.tag_name}}/" $file; - done; - python -m build - twine upload dist/* diff --git a/.github/workflows/release_gh.yml b/.github/workflows/release_gh.yml new file mode 100644 index 0000000..041a337 --- /dev/null +++ b/.github/workflows/release_gh.yml @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: Build CI + +on: [pull_request, push] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Run Build CI workflow + uses: adafruit/workflows-circuitpython-libs/build@main diff --git a/.github/workflows/release_pypi.yml b/.github/workflows/release_pypi.yml new file mode 100644 index 0000000..041a337 --- /dev/null +++ b/.github/workflows/release_pypi.yml @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: Build CI + +on: [pull_request, push] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Run Build CI workflow + uses: adafruit/workflows-circuitpython-libs/build@main From 76ddce93782076cae8fd5a26a61f84656d2779ec Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 4 Nov 2022 00:47:00 -0400 Subject: [PATCH 068/124] Updated pylint version to 2.13.0 --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3343606..4c43710 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,7 +18,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/pycqa/pylint - rev: v2.11.1 + rev: v2.13.0 hooks: - id: pylint name: pylint (library code) From 549e65c1261fa309b20b07602ae5555b7d9f988f Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 4 Nov 2022 08:15:21 -0400 Subject: [PATCH 069/124] Update pylint to 2.15.5 --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4c43710..0e5fccc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,7 +18,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/pycqa/pylint - rev: v2.13.0 + rev: v2.15.5 hooks: - id: pylint name: pylint (library code) From 1a323f8bb52df315f4ad7762143d1d738b84cbb9 Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 4 Nov 2022 09:12:46 -0400 Subject: [PATCH 070/124] Fix release CI files --- .github/workflows/release_gh.yml | 14 +++++++++----- .github/workflows/release_pypi.yml | 15 ++++++++++----- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release_gh.yml b/.github/workflows/release_gh.yml index 041a337..b8aa8d6 100644 --- a/.github/workflows/release_gh.yml +++ b/.github/workflows/release_gh.yml @@ -2,13 +2,17 @@ # # SPDX-License-Identifier: MIT -name: Build CI +name: GitHub Release Actions -on: [pull_request, push] +on: + release: + types: [published] jobs: - test: + upload-release-assets: runs-on: ubuntu-latest steps: - - name: Run Build CI workflow - uses: adafruit/workflows-circuitpython-libs/build@main + - name: Run GitHub Release CI workflow + uses: adafruit/workflows-circuitpython-libs/release-gh@main + with: + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release_pypi.yml b/.github/workflows/release_pypi.yml index 041a337..65775b7 100644 --- a/.github/workflows/release_pypi.yml +++ b/.github/workflows/release_pypi.yml @@ -2,13 +2,18 @@ # # SPDX-License-Identifier: MIT -name: Build CI +name: PyPI Release Actions -on: [pull_request, push] +on: + release: + types: [published] jobs: - test: + upload-release-assets: runs-on: ubuntu-latest steps: - - name: Run Build CI workflow - uses: adafruit/workflows-circuitpython-libs/build@main + - name: Run PyPI Release CI workflow + uses: adafruit/workflows-circuitpython-libs/release-pypi@main + with: + pypi-username: ${{ secrets.pypi_username }} + pypi-password: ${{ secrets.pypi_password }} From 179db158444a949db8e4599230e994106fdf687f Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 4 Nov 2022 18:34:33 -0400 Subject: [PATCH 071/124] Update .pylintrc for v2.15.5 --- .pylintrc | 43 +++---------------------------------------- 1 file changed, 3 insertions(+), 40 deletions(-) diff --git a/.pylintrc b/.pylintrc index 1f42e5d..40208c3 100644 --- a/.pylintrc +++ b/.pylintrc @@ -26,7 +26,7 @@ jobs=1 # List of plugins (as comma separated values of python modules names) to load, # usually to register additional checkers. -load-plugins= +load-plugins=pylint.extensions.no_self_use # Pickle collected data for later comparisons. persistent=yes @@ -54,8 +54,8 @@ confidence= # --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,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call -disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error,bad-continuation,pointless-string-statement,unspecified-encoding +# 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 @@ -225,12 +225,6 @@ max-line-length=100 # Maximum number of lines in a module max-module-lines=1000 -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -no-space-check=trailing-comma,dict-separator - # 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 @@ -257,38 +251,22 @@ min-similarity-lines=12 [BASIC] -# Naming hint for argument names -argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct argument names argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ -# Naming hint for attribute names -attr-name-hint=(([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 -# Naming hint for class attribute names -class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - # Regular expression matching correct class attribute names class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ -# Naming hint for class names -# class-name-hint=[A-Z_][a-zA-Z0-9]+$ -class-name-hint=[A-Z_][a-zA-Z0-9_]+$ - # Regular expression matching correct class names # class-rgx=[A-Z_][a-zA-Z0-9]+$ class-rgx=[A-Z_][a-zA-Z0-9_]+$ -# Naming hint for constant names -const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - # Regular expression matching correct constant names const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ @@ -296,9 +274,6 @@ const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ # ones are exempt. docstring-min-length=-1 -# Naming hint for function names -function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct function names function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ @@ -309,21 +284,12 @@ 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 -# Naming hint for inline iteration names -inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ - # Regular expression matching correct inline iteration names inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ -# Naming hint for method names -method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct method names method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ -# Naming hint for module names -module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - # Regular expression matching correct module names module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ @@ -339,9 +305,6 @@ no-docstring-rgx=^_ # to this list to register other decorators that produce valid properties. property-classes=abc.abstractproperty -# Naming hint for variable names -variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct variable names variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ From 630b5b2f9fc0618b46fb9a192b0c59d1e7bdb709 Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Thu, 1 Sep 2022 20:16:31 -0400 Subject: [PATCH 072/124] Add .venv to .gitignore Signed-off-by: Alec Delaney <89490472+tekktrik@users.noreply.github.com> --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 544ec4a..db3d538 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ _build # Virtual environment-specific files .env +.venv # MacOS-specific files *.DS_Store From 7ff0a91ab0054208dea3c3c777bec4d1f498bfd4 Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Thu, 19 Jan 2023 23:39:55 -0500 Subject: [PATCH 073/124] Add upload url to release action Signed-off-by: Alec Delaney <89490472+tekktrik@users.noreply.github.com> --- .github/workflows/release_gh.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release_gh.yml b/.github/workflows/release_gh.yml index b8aa8d6..9acec60 100644 --- a/.github/workflows/release_gh.yml +++ b/.github/workflows/release_gh.yml @@ -16,3 +16,4 @@ jobs: uses: adafruit/workflows-circuitpython-libs/release-gh@main with: github-token: ${{ secrets.GITHUB_TOKEN }} + upload-url: ${{ github.event.release.upload_url }} From 9a18d6866066a9fc1734450ec911e079d27f3996 Mon Sep 17 00:00:00 2001 From: Tekktrik Date: Tue, 9 May 2023 20:26:25 -0400 Subject: [PATCH 074/124] Update pre-commit hooks Signed-off-by: Tekktrik --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0e5fccc..70ade69 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,21 +4,21 @@ repos: - repo: https://github.com/python/black - rev: 22.3.0 + rev: 23.3.0 hooks: - id: black - repo: https://github.com/fsfe/reuse-tool - rev: v0.14.0 + rev: v1.1.2 hooks: - id: reuse - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.2.0 + rev: v4.4.0 hooks: - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/pycqa/pylint - rev: v2.15.5 + rev: v2.17.4 hooks: - id: pylint name: pylint (library code) From fdc9da933dfa191d7794c0c8edd41292f7e64708 Mon Sep 17 00:00:00 2001 From: Tekktrik Date: Sun, 14 May 2023 13:00:32 -0400 Subject: [PATCH 075/124] Update .pylintrc, fix jQuery for docs Signed-off-by: Tekktrik --- .pylintrc | 2 +- docs/conf.py | 1 + docs/requirements.txt | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.pylintrc b/.pylintrc index 40208c3..f945e92 100644 --- a/.pylintrc +++ b/.pylintrc @@ -396,4 +396,4 @@ min-public-methods=1 # Exceptions that will emit a warning when being caught. Defaults to # "Exception" -overgeneral-exceptions=Exception +overgeneral-exceptions=builtins.Exception diff --git a/docs/conf.py b/docs/conf.py index 148956f..09ab75f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,6 +17,7 @@ # ones. extensions = [ "sphinx.ext.autodoc", + "sphinxcontrib.jquery", "sphinx.ext.intersphinx", "sphinx.ext.napoleon", "sphinx.ext.todo", diff --git a/docs/requirements.txt b/docs/requirements.txt index 88e6733..797aa04 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -3,3 +3,4 @@ # SPDX-License-Identifier: Unlicense sphinx>=4.0.0 +sphinxcontrib-jquery From 66db505fc6c41eca07f402e29c11d73298249b62 Mon Sep 17 00:00:00 2001 From: Tekktrik Date: Wed, 24 May 2023 12:57:35 -0400 Subject: [PATCH 076/124] Switch to pytest --- tests/__init__.py | 0 tests/pytest_helpers.py | 41 ++++++++++ tests/test_mov.py | 29 ++++++++ tests/test_nop.py | 83 +++++++++++++++++++++ tests/test_radix.py | 21 ++++++ tests/test_wrap.py | 19 +++++ tests/testpioasm.py | 161 ---------------------------------------- 7 files changed, 193 insertions(+), 161 deletions(-) delete mode 100644 tests/__init__.py create mode 100644 tests/pytest_helpers.py create mode 100644 tests/test_mov.py create mode 100644 tests/test_nop.py create mode 100644 tests/test_radix.py create mode 100644 tests/test_wrap.py delete mode 100644 tests/testpioasm.py diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/pytest_helpers.py b/tests/pytest_helpers.py new file mode 100644 index 0000000..678e9b7 --- /dev/null +++ b/tests/pytest_helpers.py @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2021 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +Pytest helper functions +""" + +import pytest + +import adafruit_pioasm + + +def nice_opcode(opcode): + opcode = f"{opcode:016b}" + return opcode[:3] + "_" + opcode[3:8] + "_" + opcode[8:] + + +def assert_assembles_to(source, expected): + actual = adafruit_pioasm.assemble(source) + expected_bin = [nice_opcode(x) for x in expected] + actual_bin = [nice_opcode(x) for x in actual] + assert ( + expected_bin == actual_bin + ), f"Assembling {source!r}: Expected {expected_bin}, got {actual_bin}" + + +def assert_assembly_fails(source, match=None, errtype=RuntimeError): + with pytest.raises(errtype, match=match): + adafruit_pioasm.assemble(source) + # if match: + # with pytest.raises(errtype, match=match): + # adafruit_pioasm.assemble(source) + # else: + # with pytest.raises(errtype): + # adafruit_pioasm.assemble(source) + + +def assert_pio_kwargs(source, **kw): + program = adafruit_pioasm.Program(source) + assert kw == program.pio_kwargs diff --git a/tests/test_mov.py b/tests/test_mov.py new file mode 100644 index 0000000..b6015cb --- /dev/null +++ b/tests/test_mov.py @@ -0,0 +1,29 @@ +# SPDX-FileCopyrightText: 2021 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +Tests mov +""" + +from pytest_helpers import assert_assembles_to, assert_assembly_fails + + +def test_mov_non_happy(): + # non happy path + assert_assembly_fails( + "mov x, blah", match="Invalid mov source 'blah'", errtype=ValueError + ) + + +def test_mov_invert(): + # test moving and inverting + assert_assembles_to("mov x, ~ x", [0b101_00000_001_01_001]) + assert_assembles_to("mov x, ~x", [0b101_00000_001_01_001]) + assert_assembles_to("mov x, !x", [0b101_00000_001_01_001]) + + +def test_mov_reverse(): + # test moving and reversing bits + assert_assembles_to("mov x, :: x", [0b101_00000_001_10_001]) + assert_assembles_to("mov x, ::x", [0b101_00000_001_10_001]) diff --git a/tests/test_nop.py b/tests/test_nop.py new file mode 100644 index 0000000..7fd7bca --- /dev/null +++ b/tests/test_nop.py @@ -0,0 +1,83 @@ +# SPDX-FileCopyrightText: 2021 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +Tests nop +""" + +from pytest_helpers import assert_assembles_to, assert_assembly_fails, assert_pio_kwargs + + +def test_nonsense(): + assert_assembly_fails("nope") + + +def test_nop(): + assert_assembles_to("nop", [0b101_00000_010_00_010]) + assert_assembles_to("nop\nnop", [0b101_00000_010_00_010, 0b101_00000_010_00_010]) + assert_assembles_to("nop [1]", [0b101_00001_010_00_010]) + assert_assembles_to("nop [31]", [0b101_11111_010_00_010]) + assert_assembles_to(".side_set 1\nnop side 1", [0b101_10000_010_00_010]) + assert_assembles_to(".side_set 1\nnop side 1 [15]", [0b101_11111_010_00_010]) + + +def test_sideset_opt(): + assert_assembles_to(".side_set 1 opt\nnop side 1", [0b101_11000_010_00_010]) + assert_assembles_to(".side_set 1 opt\nnop side 0", [0b101_10000_010_00_010]) + assert_assembles_to(".side_set 1 opt\nnop side 0 [1]", [0b101_10001_010_00_010]) + assert_assembles_to(".side_set 1 opt\nnop [1]", [0b101_00001_010_00_010]) + assert_assembles_to(".side_set 1 opt\nnop [7]", [0b101_00111_010_00_010]) + assert_assembles_to(".side_set 1 opt\nnop side 1 [1]", [0b101_11001_010_00_010]) + assert_assembles_to(".side_set 1 opt\nnop side 0 [7]", [0b101_10111_010_00_010]) + + +def test_set(): + # non happy path + assert_assembly_fails( + "set isr, 1", match="Invalid set destination 'isr'", errtype=ValueError + ) + + +def test_jmp(): + assert_assembles_to("l:\njmp l", [0b000_00000_000_00000]) + assert_assembles_to("l:\njmp 7", [0b000_00000_000_00111]) + assert_assembles_to("jmp l\nl:", [0b000_00000_000_00001]) + assert_assembles_to("jmp !x, l\nl:", [0b000_00000_001_00001]) + assert_assembles_to("jmp x--, l\nl:", [0b000_00000_010_00001]) + assert_assembles_to("jmp !y, l\nl:", [0b000_00000_011_00001]) + assert_assembles_to("jmp y--, l\nl:", [0b000_00000_100_00001]) + assert_assembles_to("jmp x!=y, l\nl:", [0b000_00000_101_00001]) + assert_assembles_to("jmp pin, l\nl:", [0b000_00000_110_00001]) + assert_assembles_to("jmp !osre, l\nl:", [0b000_00000_111_00001]) + # non happy path + assert_assembly_fails( + "jmp x--., l\nl:", match="Invalid jmp condition 'x--.'", errtype=ValueError + ) + + +def test_wait(): + assert_assembles_to("wait 0 gpio 0", [0b001_00000_0_00_00000]) + assert_assembles_to("wait 0 gpio 1", [0b001_00000_0_00_00001]) + assert_assembles_to("wait 1 gpio 2", [0b001_00000_1_00_00010]) + assert_assembles_to("wait 0 pin 0", [0b001_00000_0_01_00000]) + assert_assembles_to("wait 0 pin 1", [0b001_00000_0_01_00001]) + assert_assembles_to("wait 1 pin 2", [0b001_00000_1_01_00010]) + assert_assembles_to("wait 0 irq 0", [0b001_00000_0_10_00000]) + assert_assembles_to("wait 0 irq 0 rel", [0b001_00000_0_10_10000]) + assert_assembles_to("wait 1 irq 0", [0b001_00000_1_10_00000]) + assert_assembles_to("wait 0 irq 1 rel", [0b001_00000_0_10_10001]) + + +def test_limits(): + assert_assembly_fails(".side_set 1\nnop side 2") + assert_assembly_fails(".side_set 1\nnop side 2 [1]") + assert_assembly_fails("nop [32]") + assert_assembly_fails(".side_set 1\nnop side 0 [16]") + assert_assembly_fails(".side_set 1 opt\nnop side 0 [8]") + + +def test_cls(): + assert_pio_kwargs("", sideset_enable=False) + assert_pio_kwargs(".side_set 1", sideset_pin_count=1, sideset_enable=False) + assert_pio_kwargs(".side_set 3 opt", sideset_pin_count=3, sideset_enable=True) diff --git a/tests/test_radix.py b/tests/test_radix.py new file mode 100644 index 0000000..f3cfb9e --- /dev/null +++ b/tests/test_radix.py @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2021 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +Tests radix +""" + +from pytest_helpers import assert_assembles_to + + +def test_octal(): + assert_assembles_to(".side_set 0o1\nset x, 0o11", [0b111_00000_001_01001]) + + +def test_binary(): + assert_assembles_to(".side_set 0b101\nnop side 0b10001", [0b101_10001_010_00_010]) + + +def test_hex(): + assert_assembles_to(".side_set 0x0\nnop [0x10]", [0b101_10000_010_00_010]) diff --git a/tests/test_wrap.py b/tests/test_wrap.py new file mode 100644 index 0000000..b866a26 --- /dev/null +++ b/tests/test_wrap.py @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2021 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +Tests wrap +""" + +from pytest_helpers import assert_assembly_fails, assert_pio_kwargs + + +def test_wrap(): + assert_assembly_fails(".wrap") + assert_pio_kwargs( + "nop\n.wrap_target\nnop\nnop\n.wrap", + sideset_enable=False, + wrap=2, + wrap_target=1, + ) diff --git a/tests/testpioasm.py b/tests/testpioasm.py deleted file mode 100644 index a2e9b3c..0000000 --- a/tests/testpioasm.py +++ /dev/null @@ -1,161 +0,0 @@ -# SPDX-FileCopyrightText: 2021 Jeff Epler, written for Adafruit Industries -# -# SPDX-License-Identifier: MIT - -# pylint: disable=missing-module-docstring,invalid-name,missing-function-docstring,missing-class-docstring - -import pathlib -import sys -import unittest - -sys.path.insert(0, str(pathlib.Path(__file__).absolute().parent.parent)) - -import adafruit_pioasm # pylint: disable=wrong-import-position - - -def nice_opcode(o): - o = f"{o:016b}" - return o[:3] + "_" + o[3:8] + "_" + o[8:] - - -class AssembleChecks(unittest.TestCase): - def assertAssemblesTo(self, source, expected): - actual = adafruit_pioasm.assemble(source) - expected_bin = [nice_opcode(x) for x in expected] - actual_bin = [nice_opcode(x) for x in actual] - self.assertEqual( - expected_bin, - actual_bin, - f"Assembling {source!r}: Expected {expected_bin}, got {actual_bin}", - ) - - def assertAssemblyFails(self, source, match=None, errtype=RuntimeError): - if match: - self.assertRaisesRegex(errtype, match, adafruit_pioasm.assemble, source) - else: - self.assertRaises(errtype, adafruit_pioasm.assemble, source) - - def assertPioKwargs(self, source, **kw): - program = adafruit_pioasm.Program(source) - self.assertEqual(kw, program.pio_kwargs) - - -class TestNop(AssembleChecks): - def testNonsense(self): - self.assertAssemblyFails("nope") - - def testNop(self): - self.assertAssemblesTo("nop", [0b101_00000_010_00_010]) - self.assertAssemblesTo( - "nop\nnop", [0b101_00000_010_00_010, 0b101_00000_010_00_010] - ) - self.assertAssemblesTo("nop [1]", [0b101_00001_010_00_010]) - self.assertAssemblesTo("nop [31]", [0b101_11111_010_00_010]) - self.assertAssemblesTo(".side_set 1\nnop side 1", [0b101_10000_010_00_010]) - self.assertAssemblesTo(".side_set 1\nnop side 1 [15]", [0b101_11111_010_00_010]) - - def testSidesetOpt(self): - self.assertAssemblesTo(".side_set 1 opt\nnop side 1", [0b101_11000_010_00_010]) - self.assertAssemblesTo(".side_set 1 opt\nnop side 0", [0b101_10000_010_00_010]) - self.assertAssemblesTo( - ".side_set 1 opt\nnop side 0 [1]", [0b101_10001_010_00_010] - ) - self.assertAssemblesTo(".side_set 1 opt\nnop [1]", [0b101_00001_010_00_010]) - self.assertAssemblesTo(".side_set 1 opt\nnop [7]", [0b101_00111_010_00_010]) - self.assertAssemblesTo( - ".side_set 1 opt\nnop side 1 [1]", [0b101_11001_010_00_010] - ) - self.assertAssemblesTo( - ".side_set 1 opt\nnop side 0 [7]", [0b101_10111_010_00_010] - ) - - def testSet(self): - # non happy path - self.assertAssemblyFails( - "set isr, 1", match="Invalid set destination 'isr'", errtype=ValueError - ) - - def testJmp(self): - self.assertAssemblesTo("l:\njmp l", [0b000_00000_000_00000]) - self.assertAssemblesTo("l:\njmp 7", [0b000_00000_000_00111]) - self.assertAssemblesTo("jmp l\nl:", [0b000_00000_000_00001]) - self.assertAssemblesTo("jmp !x, l\nl:", [0b000_00000_001_00001]) - self.assertAssemblesTo("jmp x--, l\nl:", [0b000_00000_010_00001]) - self.assertAssemblesTo("jmp !y, l\nl:", [0b000_00000_011_00001]) - self.assertAssemblesTo("jmp y--, l\nl:", [0b000_00000_100_00001]) - self.assertAssemblesTo("jmp x!=y, l\nl:", [0b000_00000_101_00001]) - self.assertAssemblesTo("jmp pin, l\nl:", [0b000_00000_110_00001]) - self.assertAssemblesTo("jmp !osre, l\nl:", [0b000_00000_111_00001]) - # non happy path - self.assertAssemblyFails( - "jmp x--., l\nl:", match="Invalid jmp condition 'x--.'", errtype=ValueError - ) - - def testWait(self): - self.assertAssemblesTo("wait 0 gpio 0", [0b001_00000_0_00_00000]) - self.assertAssemblesTo("wait 0 gpio 1", [0b001_00000_0_00_00001]) - self.assertAssemblesTo("wait 1 gpio 2", [0b001_00000_1_00_00010]) - self.assertAssemblesTo("wait 0 pin 0", [0b001_00000_0_01_00000]) - self.assertAssemblesTo("wait 0 pin 1", [0b001_00000_0_01_00001]) - self.assertAssemblesTo("wait 1 pin 2", [0b001_00000_1_01_00010]) - self.assertAssemblesTo("wait 0 irq 0", [0b001_00000_0_10_00000]) - self.assertAssemblesTo("wait 0 irq 0 rel", [0b001_00000_0_10_10000]) - self.assertAssemblesTo("wait 1 irq 0", [0b001_00000_1_10_00000]) - self.assertAssemblesTo("wait 0 irq 1 rel", [0b001_00000_0_10_10001]) - - def testLimits(self): - self.assertAssemblyFails(".side_set 1\nnop side 2") - self.assertAssemblyFails(".side_set 1\nnop side 2 [1]") - self.assertAssemblyFails("nop [32]") - self.assertAssemblyFails(".side_set 1\nnop side 0 [16]") - self.assertAssemblyFails(".side_set 1 opt\nnop side 0 [8]") - - def testCls(self): - self.assertPioKwargs("", sideset_enable=False) - self.assertPioKwargs(".side_set 1", sideset_pin_count=1, sideset_enable=False) - self.assertPioKwargs( - ".side_set 3 opt", sideset_pin_count=3, sideset_enable=True - ) - - -class TestMov(AssembleChecks): - def testMovNonHappy(self): - # non happy path - self.assertAssemblyFails( - "mov x, blah", match="Invalid mov source 'blah'", errtype=ValueError - ) - - def testMovInvert(self): - # test moving and inverting - self.assertAssemblesTo("mov x, ~ x", [0b101_00000_001_01_001]) - self.assertAssemblesTo("mov x, ~x", [0b101_00000_001_01_001]) - self.assertAssemblesTo("mov x, !x", [0b101_00000_001_01_001]) - - def testMovReverse(self): - # test moving and reversing bits - self.assertAssemblesTo("mov x, :: x", [0b101_00000_001_10_001]) - self.assertAssemblesTo("mov x, ::x", [0b101_00000_001_10_001]) - - -class TestWrap(AssembleChecks): - def testWrap(self): - self.assertAssemblyFails(".wrap") - self.assertPioKwargs( - "nop\n.wrap_target\nnop\nnop\n.wrap", - sideset_enable=False, - wrap=2, - wrap_target=1, - ) - - -class TestRadix(AssembleChecks): - def testOctal(self): - self.assertAssemblesTo(".side_set 0o1\nset x, 0o11", [0b111_00000_001_01001]) - - def testBinary(self): - self.assertAssemblesTo( - ".side_set 0b101\nnop side 0b10001", [0b101_10001_010_00_010] - ) - - def testHex(self): - self.assertAssemblesTo(".side_set 0x0\nnop [0x10]", [0b101_10000_010_00_010]) From 38962e7113ed983a38850f57235edda1e0dd845f Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 27 Jul 2023 10:22:52 -0500 Subject: [PATCH 077/124] morse example: add missing pio_kwargs to constructor call This appears to be the cause of a user's problems in #55. --- examples/pioasm_background_morse.py | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/pioasm_background_morse.py b/examples/pioasm_background_morse.py index af5714f..d3dcdc9 100644 --- a/examples/pioasm_background_morse.py +++ b/examples/pioasm_background_morse.py @@ -69,6 +69,7 @@ pull_threshold=16, auto_pull=True, out_shift_right=False, + **pio_code.pio_kwargs, ) # To simply repeat 'TEST' forever, change to 'if True': From 3bacee4ad2fca6a3bbc52c39f95e8825dcbaba43 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sat, 5 Aug 2023 15:57:13 -0500 Subject: [PATCH 078/124] support .offset pseudo-op --- adafruit_pioasm.py | 6 ++++++ tests/test_pseudo.py | 13 +++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 tests/test_pseudo.py diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index a6a4ebf..81ce799 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -52,6 +52,7 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: sideset_enable = 0 wrap = None wrap_target = None + offset = -1 for i, line in enumerate(text_program.split("\n")): line = line.strip() if not line: @@ -62,6 +63,8 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: if program_name: raise RuntimeError("Multiple programs not supported") program_name = line.split()[1] + elif line.startswith(".offset"): + offset = int(line.split()[1], 0) elif line.startswith(".wrap_target"): wrap_target = len(instructions) elif line.startswith(".wrap"): @@ -227,6 +230,9 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: "sideset_enable": sideset_enable, } + if offset != -1: + self.pio_kwargs["offset"] = offset + if sideset_count != 0: self.pio_kwargs["sideset_pin_count"] = sideset_count diff --git a/tests/test_pseudo.py b/tests/test_pseudo.py new file mode 100644 index 0000000..261f03a --- /dev/null +++ b/tests/test_pseudo.py @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2021 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +Tests pseudo-ops +""" + +from pytest_helpers import assert_pio_kwargs + + +def test_offset(): + assert_pio_kwargs(".offset 7", offset=7, sideset_enable=False) From 9eed74c71fedfaf0266ea3653596e3f13295eba0 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Mon, 18 Sep 2023 16:23:44 -0500 Subject: [PATCH 079/124] "fix rtd theme " --- docs/conf.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 09ab75f..dbc6149 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -110,19 +110,10 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -on_rtd = os.environ.get("READTHEDOCS", None) == "True" - -if not on_rtd: # only import and set the theme if we're building docs locally - try: - import sphinx_rtd_theme - - html_theme = "sphinx_rtd_theme" - html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."] - except: - html_theme = "default" - html_theme_path = ["."] -else: - html_theme_path = ["."] +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 2bc0deb3ab8a4d9eaa2550e09c3e0c4cd25d852a Mon Sep 17 00:00:00 2001 From: foamyguy Date: Mon, 16 Oct 2023 14:30:31 -0500 Subject: [PATCH 080/124] unpin sphinx and add sphinx-rtd-theme to docs reqs Signed-off-by: foamyguy --- docs/requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 797aa04..979f568 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,5 +2,6 @@ # # SPDX-License-Identifier: Unlicense -sphinx>=4.0.0 +sphinx sphinxcontrib-jquery +sphinx-rtd-theme From 6ac60b4554d841d550fc1617ccd8039ef509c1f1 Mon Sep 17 00:00:00 2001 From: Gebhartj <34003931+Gebhartj@users.noreply.github.com> Date: Wed, 24 Jan 2024 08:09:03 +0100 Subject: [PATCH 081/124] repair inscorect directive .offset to .origin .offset directive do not exist in pioasm --- adafruit_pioasm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 81ce799..6471db7 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -63,7 +63,7 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: if program_name: raise RuntimeError("Multiple programs not supported") program_name = line.split()[1] - elif line.startswith(".offset"): + elif line.startswith(".origin"): offset = int(line.split()[1], 0) elif line.startswith(".wrap_target"): wrap_target = len(instructions) From 05e0ae9acd5848ea0dc1aeb9a86edea7c12b6a44 Mon Sep 17 00:00:00 2001 From: Gebhartj <34003931+Gebhartj@users.noreply.github.com> Date: Wed, 24 Jan 2024 08:19:53 +0100 Subject: [PATCH 082/124] Update test_pseudo.py --- tests/test_pseudo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_pseudo.py b/tests/test_pseudo.py index 261f03a..72e817c 100644 --- a/tests/test_pseudo.py +++ b/tests/test_pseudo.py @@ -10,4 +10,4 @@ def test_offset(): - assert_pio_kwargs(".offset 7", offset=7, sideset_enable=False) + assert_pio_kwargs(".origin 7", offset=7, sideset_enable=False) From d87b4461ae5e25475bbfa6bb5a3e0f917555e016 Mon Sep 17 00:00:00 2001 From: KB Sriram Date: Thu, 25 Apr 2024 22:50:39 -0700 Subject: [PATCH 083/124] Add tests for "out" and "in". Also added more descriptive errors for invalid source/destination values. Fixes https://github.com/adafruit/Adafruit_CircuitPython_PIOASM/issues/44 --- adafruit_pioasm.py | 14 +++++++-- tests/test_in.py | 69 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_out.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 tests/test_in.py create mode 100644 tests/test_out.py diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 6471db7..8ed258a 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -147,7 +147,11 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: elif instruction[0] == "in": # instr delay src count assembled.append(0b010_00000_000_00000) - assembled[-1] |= IN_SOURCES.index(instruction[1]) << 5 + source = instruction[1] + try: + assembled[-1] |= IN_SOURCES.index(source) << 5 + except ValueError as exc: + raise ValueError(f"Invalid in source '{source}'") from exc count = int(instruction[-1], 0) if not 1 <= count <= 32: raise RuntimeError("Count out of range") @@ -155,7 +159,13 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: elif instruction[0] == "out": # instr delay dst count assembled.append(0b011_00000_000_00000) - assembled[-1] |= OUT_DESTINATIONS.index(instruction[1]) << 5 + destination = instruction[1] + try: + assembled[-1] |= OUT_DESTINATIONS.index(destination) << 5 + except ValueError as 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") diff --git a/tests/test_in.py b/tests/test_in.py new file mode 100644 index 0000000..d2b76dd --- /dev/null +++ b/tests/test_in.py @@ -0,0 +1,69 @@ +# SPDX-FileCopyrightText: KB Sriram +# +# SPDX-License-Identifier: MIT + +""" +Tests in +""" + +import pytest +from pytest_helpers import assert_assembles_to, assert_assembly_fails + + +@pytest.mark.parametrize( + "source,expected", + [ + ("pins", 0b000), + ("x", 0b001), + ("y", 0b010), + ("null", 0b011), + ("isr", 0b110), + ("osr", 0b111), + ], +) +def test_in_sources(source: str, expected: int) -> None: + # delay src bitcount + encoding = 0b010_00000_000_10001 + # add in the expected source + encoding |= expected << 5 + assert_assembles_to(f"in {source}, 17", [encoding]) + + +@pytest.mark.parametrize("delay", [0, 1, 9, 17, 31]) +def test_in_delay(delay: int) -> None: + # delay src bitcount + encoding = 0b010_00000_000_10001 + # add in the expected delay + encoding |= delay << 8 + assert_assembles_to(f"in pins, 17 [{delay}]", [encoding]) + + +@pytest.mark.parametrize("bitcount", [1, 9, 17, 32]) +def test_in_bitcount(bitcount: int) -> None: + # delay dst bitcount + encoding = 0b010_00000_000_00000 + # add in the expected bitcount. Note that + # 32 should be encoded as 0, which we do by + # masking the bitcount with 0x1f + encoding |= bitcount & 0x1F + assert_assembles_to(f"in pins, {bitcount}", [encoding]) + + +def test_in_delay_with_sideset() -> None: + source = [ + ".side_set 2", + "in pins 17 side 2 [5]", + ] + assert_assembles_to("\n".join(source), [0b010_10_101_000_10001]) + + +def test_in_bad_source(): + assert_assembly_fails( + "in bad, 17", match="Invalid in source 'bad'", errtype=ValueError + ) + + +def test_in_bad_bitcount(): + assert_assembly_fails( + "in pins, 0", match="Count out of range", errtype=RuntimeError + ) diff --git a/tests/test_out.py b/tests/test_out.py new file mode 100644 index 0000000..5956fce --- /dev/null +++ b/tests/test_out.py @@ -0,0 +1,71 @@ +# SPDX-FileCopyrightText: KB Sriram +# +# SPDX-License-Identifier: MIT + +""" +Tests out +""" + +import pytest +from pytest_helpers import assert_assembles_to, assert_assembly_fails + + +@pytest.mark.parametrize( + "destination,expected", + [ + ("pins", 0b000), + ("x", 0b001), + ("y", 0b010), + ("null", 0b011), + ("pindirs", 0b100), + ("pc", 0b101), + ("isr", 0b110), + ("exec", 0b111), + ], +) +def test_out_destinations(destination: str, expected: int) -> None: + # delay dst bitcount + encoding = 0b011_00000_000_10001 + # add in the expected destination + encoding |= expected << 5 + assert_assembles_to(f"out {destination}, 17", [encoding]) + + +@pytest.mark.parametrize("delay", [0, 1, 9, 17, 31]) +def test_out_delay(delay: int) -> None: + # delay dst bitcount + encoding = 0b011_00000_000_10001 + # add in the expected delay + encoding |= delay << 8 + assert_assembles_to(f"out pins, 17 [{delay}]", [encoding]) + + +@pytest.mark.parametrize("bitcount", [1, 9, 17, 32]) +def test_out_bitcount(bitcount: int) -> None: + # delay dst bitcount + encoding = 0b011_00000_000_00000 + # add in the expected bitcount. Note that + # 32 should be encoded as 0, which we do by + # masking the bitcount with 0x1f + encoding |= bitcount & 0x1F + assert_assembles_to(f"out pins, {bitcount}", [encoding]) + + +def test_out_delay_with_sideset() -> None: + source = [ + ".side_set 2", + "out pins 17 side 2 [5]", + ] + assert_assembles_to("\n".join(source), [0b011_10_101_000_10001]) + + +def test_out_bad_destination(): + assert_assembly_fails( + "out bad, 17", match="Invalid out destination 'bad'", errtype=ValueError + ) + + +def test_out_bad_bitcount(): + assert_assembly_fails( + "out pins, 0", match="Count out of range", errtype=RuntimeError + ) From 3e6a8bc3920d4a203f560cbfb9a963760f511463 Mon Sep 17 00:00:00 2001 From: KB Sriram Date: Mon, 27 May 2024 12:30:52 -0700 Subject: [PATCH 084/124] Add type annotations for library and tests. Verified that `mypy --strict adafruit_pioasm.py tests` runs without errors. Fixes: https://github.com/adafruit/Adafruit_CircuitPython_PIOASM/issues/24 --- adafruit_pioasm.py | 29 +++++++++++++++-------------- tests/pytest_helpers.py | 19 +++++++++++++------ tests/test_in.py | 4 ++-- tests/test_mov.py | 6 +++--- tests/test_nop.py | 16 ++++++++-------- tests/test_out.py | 4 ++-- tests/test_pseudo.py | 2 +- tests/test_radix.py | 6 +++--- tests/test_wrap.py | 2 +- 9 files changed, 48 insertions(+), 40 deletions(-) diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 8ed258a..514d1ba 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -11,6 +11,11 @@ * Author(s): Scott Shawcroft """ +try: + from typing import List, MutableSequence +except ImportError: + pass + import array import re @@ -40,14 +45,14 @@ class Program: # pylint: disable=too-few-public-methods """ - def __init__(self, text_program: str, *, build_debuginfo=False) -> None: + 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 = [] + assembled: List[int] = [] program_name = None labels = {} linemap = [] - instructions = [] + instructions: List[str] = [] sideset_count = 0 sideset_enable = 0 wrap = None @@ -86,9 +91,8 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: max_delay = 2 ** (5 - sideset_count - sideset_enable) - 1 assembled = [] - for instruction in instructions: - # print(instruction) - instruction = splitter(instruction.strip()) + for line in instructions: + instruction = splitter(line.strip()) delay = 0 if instruction[-1].endswith("]"): # Delay delay = int(instruction[-1].strip("[]"), 0) @@ -253,16 +257,13 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: self.assembled = array.array("H", assembled) - if build_debuginfo: - self.debuginfo = (linemap, text_program) - else: - self.debuginfo = None + self.debuginfo = (linemap, text_program) if build_debuginfo else None - def print_c_program(self, name, qualifier="const"): + def print_c_program(self, name: str, qualifier: str = "const") -> None: """Print the program into a C program snippet""" if self.debuginfo is None: - linemap = None - program_lines = None + linemap = [] + program_lines = [] else: linemap = self.debuginfo[0][:] # Use a copy since we destroy it program_lines = self.debuginfo[1].split("\n") @@ -306,7 +307,7 @@ def print_c_program(self, name, qualifier="const"): print() -def assemble(program_text: str) -> array.array: +def assemble(program_text: str) -> MutableSequence[int]: """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/pytest_helpers.py b/tests/pytest_helpers.py index 678e9b7..72ac7cf 100644 --- a/tests/pytest_helpers.py +++ b/tests/pytest_helpers.py @@ -6,17 +6,22 @@ Pytest helper functions """ +try: + from typing import Any, List, Optional, Type +except ImportError: + pass + import pytest import adafruit_pioasm -def nice_opcode(opcode): - opcode = f"{opcode:016b}" - return opcode[:3] + "_" + opcode[3:8] + "_" + opcode[8:] +def nice_opcode(opcode: int) -> str: + nice = f"{opcode:016b}" + return nice[:3] + "_" + nice[3:8] + "_" + nice[8:] -def assert_assembles_to(source, expected): +def assert_assembles_to(source: str, expected: List[int]) -> None: actual = adafruit_pioasm.assemble(source) expected_bin = [nice_opcode(x) for x in expected] actual_bin = [nice_opcode(x) for x in actual] @@ -25,7 +30,9 @@ def assert_assembles_to(source, expected): ), f"Assembling {source!r}: Expected {expected_bin}, got {actual_bin}" -def assert_assembly_fails(source, match=None, errtype=RuntimeError): +def assert_assembly_fails( + source: str, match: Optional[str] = None, errtype: Type[Exception] = RuntimeError +) -> None: with pytest.raises(errtype, match=match): adafruit_pioasm.assemble(source) # if match: @@ -36,6 +43,6 @@ def assert_assembly_fails(source, match=None, errtype=RuntimeError): # adafruit_pioasm.assemble(source) -def assert_pio_kwargs(source, **kw): +def assert_pio_kwargs(source: str, **kw: Any) -> None: program = adafruit_pioasm.Program(source) assert kw == program.pio_kwargs diff --git a/tests/test_in.py b/tests/test_in.py index d2b76dd..9c7eeb7 100644 --- a/tests/test_in.py +++ b/tests/test_in.py @@ -57,13 +57,13 @@ def test_in_delay_with_sideset() -> None: assert_assembles_to("\n".join(source), [0b010_10_101_000_10001]) -def test_in_bad_source(): +def test_in_bad_source() -> None: assert_assembly_fails( "in bad, 17", match="Invalid in source 'bad'", errtype=ValueError ) -def test_in_bad_bitcount(): +def test_in_bad_bitcount() -> None: assert_assembly_fails( "in pins, 0", match="Count out of range", errtype=RuntimeError ) diff --git a/tests/test_mov.py b/tests/test_mov.py index b6015cb..52492d7 100644 --- a/tests/test_mov.py +++ b/tests/test_mov.py @@ -9,21 +9,21 @@ from pytest_helpers import assert_assembles_to, assert_assembly_fails -def test_mov_non_happy(): +def test_mov_non_happy() -> None: # non happy path assert_assembly_fails( "mov x, blah", match="Invalid mov source 'blah'", errtype=ValueError ) -def test_mov_invert(): +def test_mov_invert() -> None: # test moving and inverting assert_assembles_to("mov x, ~ x", [0b101_00000_001_01_001]) assert_assembles_to("mov x, ~x", [0b101_00000_001_01_001]) assert_assembles_to("mov x, !x", [0b101_00000_001_01_001]) -def test_mov_reverse(): +def test_mov_reverse() -> None: # test moving and reversing bits assert_assembles_to("mov x, :: x", [0b101_00000_001_10_001]) assert_assembles_to("mov x, ::x", [0b101_00000_001_10_001]) diff --git a/tests/test_nop.py b/tests/test_nop.py index 7fd7bca..68bd3c5 100644 --- a/tests/test_nop.py +++ b/tests/test_nop.py @@ -9,11 +9,11 @@ from pytest_helpers import assert_assembles_to, assert_assembly_fails, assert_pio_kwargs -def test_nonsense(): +def test_nonsense() -> None: assert_assembly_fails("nope") -def test_nop(): +def test_nop() -> None: assert_assembles_to("nop", [0b101_00000_010_00_010]) assert_assembles_to("nop\nnop", [0b101_00000_010_00_010, 0b101_00000_010_00_010]) assert_assembles_to("nop [1]", [0b101_00001_010_00_010]) @@ -22,7 +22,7 @@ def test_nop(): assert_assembles_to(".side_set 1\nnop side 1 [15]", [0b101_11111_010_00_010]) -def test_sideset_opt(): +def test_sideset_opt() -> None: assert_assembles_to(".side_set 1 opt\nnop side 1", [0b101_11000_010_00_010]) assert_assembles_to(".side_set 1 opt\nnop side 0", [0b101_10000_010_00_010]) assert_assembles_to(".side_set 1 opt\nnop side 0 [1]", [0b101_10001_010_00_010]) @@ -32,14 +32,14 @@ def test_sideset_opt(): assert_assembles_to(".side_set 1 opt\nnop side 0 [7]", [0b101_10111_010_00_010]) -def test_set(): +def test_set() -> None: # non happy path assert_assembly_fails( "set isr, 1", match="Invalid set destination 'isr'", errtype=ValueError ) -def test_jmp(): +def test_jmp() -> None: assert_assembles_to("l:\njmp l", [0b000_00000_000_00000]) assert_assembles_to("l:\njmp 7", [0b000_00000_000_00111]) assert_assembles_to("jmp l\nl:", [0b000_00000_000_00001]) @@ -56,7 +56,7 @@ def test_jmp(): ) -def test_wait(): +def test_wait() -> None: assert_assembles_to("wait 0 gpio 0", [0b001_00000_0_00_00000]) assert_assembles_to("wait 0 gpio 1", [0b001_00000_0_00_00001]) assert_assembles_to("wait 1 gpio 2", [0b001_00000_1_00_00010]) @@ -69,7 +69,7 @@ def test_wait(): assert_assembles_to("wait 0 irq 1 rel", [0b001_00000_0_10_10001]) -def test_limits(): +def test_limits() -> None: assert_assembly_fails(".side_set 1\nnop side 2") assert_assembly_fails(".side_set 1\nnop side 2 [1]") assert_assembly_fails("nop [32]") @@ -77,7 +77,7 @@ def test_limits(): assert_assembly_fails(".side_set 1 opt\nnop side 0 [8]") -def test_cls(): +def test_cls() -> None: assert_pio_kwargs("", sideset_enable=False) assert_pio_kwargs(".side_set 1", sideset_pin_count=1, sideset_enable=False) assert_pio_kwargs(".side_set 3 opt", sideset_pin_count=3, sideset_enable=True) diff --git a/tests/test_out.py b/tests/test_out.py index 5956fce..67bc978 100644 --- a/tests/test_out.py +++ b/tests/test_out.py @@ -59,13 +59,13 @@ def test_out_delay_with_sideset() -> None: assert_assembles_to("\n".join(source), [0b011_10_101_000_10001]) -def test_out_bad_destination(): +def test_out_bad_destination() -> None: assert_assembly_fails( "out bad, 17", match="Invalid out destination 'bad'", errtype=ValueError ) -def test_out_bad_bitcount(): +def test_out_bad_bitcount() -> None: 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 72e817c..66d93c2 100644 --- a/tests/test_pseudo.py +++ b/tests/test_pseudo.py @@ -9,5 +9,5 @@ from pytest_helpers import assert_pio_kwargs -def test_offset(): +def test_offset() -> None: assert_pio_kwargs(".origin 7", offset=7, sideset_enable=False) diff --git a/tests/test_radix.py b/tests/test_radix.py index f3cfb9e..19775f4 100644 --- a/tests/test_radix.py +++ b/tests/test_radix.py @@ -9,13 +9,13 @@ from pytest_helpers import assert_assembles_to -def test_octal(): +def test_octal() -> None: assert_assembles_to(".side_set 0o1\nset x, 0o11", [0b111_00000_001_01001]) -def test_binary(): +def test_binary() -> None: assert_assembles_to(".side_set 0b101\nnop side 0b10001", [0b101_10001_010_00_010]) -def test_hex(): +def test_hex() -> None: assert_assembles_to(".side_set 0x0\nnop [0x10]", [0b101_10000_010_00_010]) diff --git a/tests/test_wrap.py b/tests/test_wrap.py index b866a26..05eed73 100644 --- a/tests/test_wrap.py +++ b/tests/test_wrap.py @@ -9,7 +9,7 @@ from pytest_helpers import assert_assembly_fails, assert_pio_kwargs -def test_wrap(): +def test_wrap() -> None: assert_assembly_fails(".wrap") assert_pio_kwargs( "nop\n.wrap_target\nnop\nnop\n.wrap", From be66c02eb3199e5540888b3375fcf3374206b0c0 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 10 Jul 2024 08:09:46 -0500 Subject: [PATCH 085/124] Better diagnose the incorrect lines like "side 1" or "[5]" Before this, the exception would be 'IndexError: list index out of range'. Now it is 'Unknown instruction: side' or similar. (These need an instruction, even if it's a `nop`: `nop side 1 [5]`) --- adafruit_pioasm.py | 6 +++--- tests/test_misc.py | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 tests/test_misc.py diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index 514d1ba..fa5a529 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -94,14 +94,14 @@ def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: for line in instructions: instruction = splitter(line.strip()) delay = 0 - if instruction[-1].endswith("]"): # Delay + if len(instruction) > 1 and instruction[-1].endswith("]"): # Delay delay = int(instruction[-1].strip("[]"), 0) if delay < 0: raise RuntimeError("Delay negative:", delay) if delay > max_delay: raise RuntimeError("Delay too long:", delay) instruction.pop() - if len(instruction) > 1 and instruction[-2] == "side": + if len(instruction) > 2 and instruction[-2] == "side": if sideset_count == 0: raise RuntimeError("No side_set count set") sideset_value = int(instruction[-1], 0) @@ -236,7 +236,7 @@ def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: raise RuntimeError("Set value out of range") assembled[-1] |= value else: - raise RuntimeError("Unknown instruction:" + instruction[0]) + raise RuntimeError(f"Unknown instruction: {instruction[0]}") assembled[-1] |= delay << 8 # print(bin(assembled[-1])) diff --git a/tests/test_misc.py b/tests/test_misc.py new file mode 100644 index 0000000..ea07e51 --- /dev/null +++ b/tests/test_misc.py @@ -0,0 +1,39 @@ +# SPDX-FileCopyrightText: KB Sriram +# +# SPDX-License-Identifier: MIT + +""" +Tests out +""" + +from pytest_helpers import assert_assembly_fails + + +def test_invalid_sideset() -> None: + source = [ + ".side_set 2", + "side 2 [5]", + ] + assert_assembly_fails( + "\n".join(source), match="Unknown instruction: side", errtype=RuntimeError + ) + + source = [ + ".side_set 2", + "side 2", + ] + assert_assembly_fails( + "\n".join(source), match="Unknown instruction: side", errtype=RuntimeError + ) + + +def test_invalid_delay() -> None: + 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 + ) From 755d5c61977011cc85d74e0f057af44453c64945 Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 9 Aug 2024 14:26:54 -0700 Subject: [PATCH 086/124] 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 087/124] 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 088/124] 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 089/124] 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 090/124] 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 091/124] 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 092/124] 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 093/124] 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 094/124] 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 095/124] 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 096/124] 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 097/124] 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 098/124] 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 099/124] 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 100/124] 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 101/124] 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 102/124] 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 103/124] 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 104/124] 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 105/124] 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 106/124] 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 107/124] 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 108/124] 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 109/124] 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 110/124] 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 111/124] 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 112/124] 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 113/124] 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 114/124] 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 115/124] 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 116/124] 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 117/124] 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 118/124] 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 119/124] 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 120/124] 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 121/124] 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 122/124] 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 123/124] 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 """ ) From 47bb463da004575ac28a366617db6a6d3d6f27aa Mon Sep 17 00:00:00 2001 From: foamyguy Date: Wed, 4 Jun 2025 10:00:20 -0500 Subject: [PATCH 124/124] update rtd.yml file Signed-off-by: foamyguy --- .readthedocs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 88bca9f..255dafd 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -12,7 +12,7 @@ sphinx: configuration: docs/conf.py build: - os: ubuntu-20.04 + os: ubuntu-lts-latest tools: python: "3"