Merge 8f52bea49d into 2b219924fd
This commit is contained in:
commit
cd0f6ece6c
9 changed files with 1940 additions and 46 deletions
|
|
@ -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
|
||||
=============
|
||||
|
||||
|
|
|
|||
|
|
@ -29,10 +29,20 @@ CONDITIONS = ["", "!x", "x--", "!y", "y--", "x!=y", "pin", "!osre"]
|
|||
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]
|
||||
FIFO_TYPES = {
|
||||
"auto": 0,
|
||||
"txrx": 0,
|
||||
"tx": 0,
|
||||
"rx": 0,
|
||||
"txput": 1,
|
||||
"txget": 1,
|
||||
"putget": 1,
|
||||
}
|
||||
|
||||
|
||||
class Program: # pylint: disable=too-few-public-methods
|
||||
|
|
@ -58,18 +68,65 @@ class Program: # pylint: disable=too-few-public-methods
|
|||
wrap = None
|
||||
wrap_target = None
|
||||
offset = -1
|
||||
pio_version = 0
|
||||
fifo_type = "auto"
|
||||
mov_status_type = None
|
||||
mov_status_n = None
|
||||
in_count = None
|
||||
in_shift_right = None
|
||||
auto_push = None
|
||||
push_threshold = None
|
||||
out_count = None
|
||||
out_shift_right = None
|
||||
auto_pull = None
|
||||
pull_threshold = None
|
||||
set_count = 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(
|
||||
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}"
|
||||
)
|
||||
|
||||
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.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")
|
||||
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"):
|
||||
offset = int(line.split()[1], 0)
|
||||
require_before_instruction()
|
||||
offset = int_in_range(words[1], 0, 32, ".origin")
|
||||
elif line.startswith(".wrap_target"):
|
||||
wrap_target = len(instructions)
|
||||
elif line.startswith(".wrap"):
|
||||
|
|
@ -79,6 +136,87 @@ class Program: # pylint: disable=too-few-public-methods
|
|||
elif line.startswith(".side_set"):
|
||||
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_n = 0
|
||||
mov_status_type = words[1]
|
||||
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])
|
||||
elif words[1] == "irq":
|
||||
required_version = 1
|
||||
idx = 2
|
||||
if words[idx] == "next":
|
||||
mov_status_n = 0x10
|
||||
idx += 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_n |= int_in_range(words[idx + 1], 0, 8, "mov_status irq")
|
||||
require_version(required_version, line)
|
||||
elif words[0] == ".out":
|
||||
require_before_instruction()
|
||||
out_count = int_in_range(words[1], 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(
|
||||
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 words[0] == ".set":
|
||||
require_before_instruction()
|
||||
set_count = int_in_range(
|
||||
words[1], 5 if pio_version == 0 else 1, 6, ".set count"
|
||||
)
|
||||
|
||||
elif line.endswith(":"):
|
||||
label = line[:-1]
|
||||
if label in labels:
|
||||
|
|
@ -89,12 +227,21 @@ class Program: # pylint: disable=too-few-public-methods
|
|||
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:
|
||||
for line in instructions: # pylint: disable=too-many-nested-blocks
|
||||
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)
|
||||
|
|
@ -138,16 +285,46 @@ class Program: # pylint: disable=too-few-public-methods
|
|||
# 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")
|
||||
if instruction[2] == "jmppin":
|
||||
require_version(1, "wait jmppin")
|
||||
num = 0
|
||||
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:
|
||||
idx = 3
|
||||
assembled[-1] |= WAIT_SOURCES.index(instruction[2]) << 5
|
||||
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":
|
||||
assembled[-1] |= 0x10 # Set the high bit of the irq value
|
||||
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
|
||||
|
||||
elif instruction[0] == "in":
|
||||
# instr delay src count
|
||||
assembled.append(0b010_00000_000_00000)
|
||||
|
|
@ -185,8 +362,19 @@ class Program: # pylint: disable=too-few-public-methods
|
|||
assembled[-1] |= 0x40
|
||||
elif instruction[0] == "mov":
|
||||
# instr delay dst op src
|
||||
if instruction[1].startswith("rxfifo["): # mov rxfifo[], isr
|
||||
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")
|
||||
elif instruction[2].startswith("rxfifo["): # mov osr, rxfifo[]
|
||||
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")
|
||||
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:
|
||||
|
|
@ -207,21 +395,39 @@ class Program: # pylint: disable=too-few-public-methods
|
|||
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
|
||||
instruction.pop()
|
||||
num = int(instruction[-1], 0)
|
||||
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":
|
||||
|
||||
irq_type = 0
|
||||
idx = 1
|
||||
if instruction[idx] == "wait":
|
||||
assembled[-1] |= 0x20
|
||||
elif instruction[1] == "clear":
|
||||
idx += 1
|
||||
elif instruction[idx] == "clear":
|
||||
assembled[-1] |= 0x40
|
||||
# All other values are the default of set without waiting
|
||||
idx += 1
|
||||
|
||||
if instruction[idx] == "prev":
|
||||
irq_type = 1
|
||||
require_version(1, "irq prev")
|
||||
idx += 1
|
||||
elif instruction[idx] == "next":
|
||||
irq_type = 3
|
||||
require_version(1, "irq next")
|
||||
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[idx], 0, 8, "irq index")
|
||||
assembled[-1] |= num
|
||||
instruction.pop()
|
||||
|
||||
elif instruction[0] == "set":
|
||||
# instr delay dst data
|
||||
assembled.append(0b111_00000_000_00000)
|
||||
|
|
@ -247,6 +453,9 @@ class Program: # pylint: disable=too-few-public-methods
|
|||
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
|
||||
|
||||
|
|
@ -255,10 +464,51 @@ class Program: # pylint: disable=too-few-public-methods
|
|||
if wrap_target is not None:
|
||||
self.pio_kwargs["wrap_target"] = wrap_target
|
||||
|
||||
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 is not None:
|
||||
self.pio_kwargs["set_pin_count"] = set_count
|
||||
|
||||
if out_count not in (None, 32):
|
||||
self.pio_kwargs["out_pin_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_pin_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
|
||||
|
||||
@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:
|
||||
|
|
|
|||
84
examples/pioasm_rp2350_fifo.py
Normal file
84
examples/pioasm_rp2350_fifo.py
Normal file
|
|
@ -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)
|
||||
1240
tests/all_pio_instructions.py
Normal file
1240
tests/all_pio_instructions.py
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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}"
|
||||
|
|
|
|||
31
tests/test_all.py
Normal file
31
tests/test_all.py
Normal file
|
|
@ -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)
|
||||
|
|
@ -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")
|
||||
|
|
|
|||
146
tests/test_version.py
Normal file
146
tests/test_version.py
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
# 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, assert_assembles_to
|
||||
|
||||
|
||||
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, 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",
|
||||
pio_version=1,
|
||||
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_n=5,
|
||||
)
|
||||
assert_pio_kwargs(
|
||||
".mov_status rxfifo < 8",
|
||||
sideset_enable=0,
|
||||
mov_status_type="rxfifo",
|
||||
mov_status_n=8,
|
||||
)
|
||||
assert_assembly_fails(".mov_status rxfifo < -1")
|
||||
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_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_n=3,
|
||||
)
|
||||
assert_assembly_fails(".pio_version 1\n.mov_status irq prev set 9")
|
||||
|
||||
|
||||
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_pin_count=16,
|
||||
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_pio_kwargs(
|
||||
".out 16 right",
|
||||
sideset_enable=0,
|
||||
out_pin_count=16,
|
||||
auto_pull=False,
|
||||
out_shift_right=True,
|
||||
)
|
||||
|
||||
|
||||
def test_dot_set() -> None:
|
||||
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 3 right", pio_version=1, sideset_enable=0, set_pin_count=3
|
||||
)
|
||||
|
||||
|
||||
def test_irq_v1() -> None:
|
||||
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:
|
||||
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_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])
|
||||
|
||||
|
||||
def test_wait_v1() -> None:
|
||||
assert_assembly_fails("wait 0 jmppin")
|
||||
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 next 5", [0b001_00000_0_10_11_101])
|
||||
assert_assembles_to(prefix + "wait 1 irq prev 4", [0b001_00000_1_10_01_100])
|
||||
135
tools/make_all.py
Normal file
135
tools/make_all.py
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
# SPDX-FileCopyrightText: 2024 Jeff Epler, written for Adafruit Industries
|
||||
#
|
||||
# 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
|
||||
|
||||
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("}")
|
||||
Loading…
Reference in a new issue