gen-pio-oberon
Generate Oberon modules from PIO assembly code for RP2040 and RP2350 microcontrollers
Name
gen-pio-oberon – generate an Oberon module from PIO assembly code
Synopsis
gen-pio-oberon <input_file> [-o <module_name>] [-v <version>] [--verbose]
Description
gen-pio-oberon converts PIO (Programmable I/O) assembly code into
an Oberon source module that can be compiled and linked with an
Astrobe program. The generated module provides a GetCode
procedure that populates a PIO.Program record with the assembled
instructions and configuration data, ready for loading into the
PIO hardware on RP2040 and RP2350 microcontrollers.
How It Works
The tool performs three steps:
- extracts PIO assembly source from the input file
- runs the external
pioasmassembler to produce JSON output containing the assembled instructions and metadata - generates an Oberon module from the JSON data
The PIO assembly listing is included as a comment in the generated module for reference.
Input Formats
PIO assembly code can be provided in two ways:
Embedded in an Oberon module (.mod) – the PIO code is placed
inside a comment block delimited by PIOBEGIN and PIOEND
keywords:
(*
PIOBEGIN
.program square_wave
set pindirs 1
loop:
set pins 1 [1]
set pins 0
jmp loop
PIOEND
*)
This keeps the PIO source together with the Oberon program that uses it.
Separate PIO file (.pio) – a standalone file containing
only PIO assembly code, without the delimiters.
Generated Module
The output module contains a single exported procedure:
PROCEDURE GetCode*(progName: ARRAY OF CHAR; VAR prog: PIO.Program);
The caller passes the PIO program name and receives
the fully populated PIO.Program record containing:
- assembled instructions as 16-bit hex values
- instruction count
- wrap target and wrap indices
- origin address
- sideset configuration (size, optional, pindirs)
- public labels and symbols
If the input file contains multiple PIO programs, GetCode selects
the matching program by name.
Using the Generated Module
In the Oberon program, import the generated module and call
GetCode to retrieve the PIO program data:
PIOsquarePio.GetCode("square_wave", prog);
PIO.PutCode(pio, sm, prog);
PIO.ConfigWrap(pio, sm, prog);
PIO.PutCode loads the instructions into the PIO instruction
memory, and PIO.ConfigWrap configures the wrap addresses for
efficient looping.
Supported platforms: Windows and macOS.
Prerequisites
The pioasm assembler (version 2.x or later) must be on the
system PATH. Earlier versions do not support JSON output. See
External Tools for
installation details.
Arguments
- input_file
- input file containing PIO assembly code. Can be an Oberon
module (
.mod) with embedded PIO code in aPIOBEGIN/PIOENDblock, or a standalone PIO file (.pio).
Options
- -o module_name
- name of the generated Oberon module. Default: the input file
stem with
Pioappended (for example,PIOsquare.modproducesPIOsquarePio.mod). - -v version
- PIO instruction set version passed to
pioasm(0 or 1). Default: 1. Version 0 is the original RP2040 instruction set; version 1 adds RP2350 extensions. - --verbose
- print details: file paths, processing steps, pioasm version.
Output
The generated module is written to the same directory as the input
file. The default name is the input file stem with Pio appended
and the .mod extension (for example, PIOsquare.mod produces
PIOsquarePio.mod).
Two temporary files are created during processing:
_<stem>.pio.pio– extracted PIO assembly (input to pioasm)_<stem>.pio.json– pioasm JSON output
These files are prefixed with _ and can be deleted after the
module is generated.
Examples
Complete Example
The program module PIOsquare.mod contains two PIO programs
embedded in a comment block:
(*
PIOBEGIN
.pio_version 0
.program square_wave
set pindirs 1
loop:
set pins 1 [1]
set pins 0
jmp loop
.program square_wave_asym
set pindirs 1
.wrap_target
set pins 1 [1]
set pins 0
.wrap
PIOEND
*)
Run gen-pio-oberon on the module:
gen-pio-oberon PIOsquare.mod
Created Oberon module PIOsquarePio.mod in .
This generates PIOsquarePio.mod containing a GetCode procedure
with the assembled instructions for both programs. The generated
module selects the program by name:
PROCEDURE GetCode*(progName: ARRAY OF CHAR; VAR prog: PIO.Program);
VAR i: INTEGER;
BEGIN
IF progName = "square_wave" THEN
prog.name := "square_wave";
prog.wrapTarget := 0;
prog.wrap := 3;
prog.origin := -1;
prog.sideset.size := 0;
prog.sideset.optional := FALSE;
prog.sideset.pindirs := FALSE;
prog.instr[0] := 0E081H;
prog.instr[1] := 0E101H;
prog.instr[2] := 0E000H;
prog.instr[3] := 00001H;
prog.numInstr := 4;
...
ELSIF progName = "square_wave_asym" THEN
...
END
END GetCode;
The Oberon program imports the generated module and loads the PIO code into the hardware:
PIOsquarePio.GetCode("square_wave", prog);
PIO.PutCode(pioDev, prog.instr, offset, prog.numInstr);
PIO.ConfigWrap(pioDev, SM0, prog.wrapTarget, prog.wrap);
PIO.SetStartAddr(pioDev, SM0, offset);
Other Invocations
Generate from a standalone PIO file:
gen-pio-oberon square_wave.pio
Generate with a custom module name:
gen-pio-oberon PIOsquare.mod -o SquareWavePio
Generate for RP2040 instruction set (version 0):
gen-pio-oberon PIOsquare.mod -v 0
Diagnostics
- "input file must be '.mod' or '.pio'" – the input file has an unsupported extension
- "could not open and read input file" – the input file does not exist or cannot be read
- "no PIOBEGIN … PIOEND block found" – a
.modinput file does not contain a valid PIO code block - "'pioasm' found problems" – the pioasm assembler reported errors in the PIO assembly code
Notes
gen-pio-oberon can be added to Astrobe's Tools menu for convenient
access during development. Configure the entry in Astrobe's
Tools.ini file.
Each PIO state machine on the RP2040 and RP2350 has a maximum of
32 instructions. The .wrap and .wrap_target directives in PIO
assembly create efficient loops without using a jump instruction,
preserving instruction space.
See Also
Last updated: 7 March 2026