Oberon RTK

Test Spec Grammar

Grammar reference for test specification files used by gdbtestengine

Grammar reference for .spec files used by gdbtestengine.

File Structure

A spec file is a sequence of blank lines, comments, directives, and procedure blocks.

spec-file = { blank | comment | directive | proc-block }

Comments

comment = '--' { any-char }

Comments run from -- to the end of the line. They may appear on their own line or after a statement.

Hexadecimal Style

The engine auto-detects the hex notation from the first hex literal in the spec. All output (reports, live progress) mirrors the same style.

Style Syntax Example
Oberon digit { hex-digit } 'H' 040013800H
C '0x' hex-digit { hex-digit } 0x40013800

Oberon hex literals that start with a letter require a leading zero (eg. 0FFH, not FFH).

Lexical Elements

Tokens

Token Pattern / character(s)
IDENT [a-zA-Z_][a-zA-Z0-9_]*
INTEGER '-'? [0-9]+
HEX '0x' [0-9a-fA-F]+ or [0-9][0-9a-fA-F]* 'H'
DOT .
PLUS +
MINUS -
EQUALS =
HASH #
LESS <
GREATER >
LESSEQUAL <=
GREATEREQUAL >=
LBRACKET [
RBRACKET ]
COLON :

A bare - followed by a digit is a negative INTEGER when the context is unary (start of token stream, or after =, #, <, >, <=, >=, [). Otherwise it is a MINUS token.

Keywords

All keywords are case-sensitive IDENTs:

proc   at   entry   exit   line
check  trace  halt
start  stop  option  when  after  all  checks  hits
arg  local  return  mem
TRUE  FALSE  NIL
as  signed  unsigned

Values

value         = integer | hex-literal | bool | nil | const-ref
integer       = INTEGER
hex-literal   = HEX
bool          = 'TRUE' | 'FALSE'
nil           = 'NIL'
const-ref     = module-name '.' const-name

A const-ref references a named constant from an Oberon module. It is resolved at engine start-up from the .alst files (CONST sections). Cross-module constants (eg. CLK.PLL1 where PLL1 = MCU.X) are resolved via import aliases.

Signedness

Comparison semantics depend on the value type:

Value type Default
integer signed
hex unsigned
const-ref unsigned
NIL unsigned
TRUE / FALSE unsigned

An explicit override may follow the expected value:

signedness    = 'as' ( 'signed' | 'unsigned' )

Example: check arg x >= 0 as unsigned

Addresses

Static addresses are resolved at engine start-up.

address       = hex-literal | const-ref

In location specifiers only, a plain integer is also accepted as a module-relative byte offset (see Locations below).

Variable References

Variable references support composable field and array access:

var-ref       = IDENT { '.' IDENT | '[' INTEGER ']' }

Examples: x, cfg.src, arr[2], rec.data[3].field

Array indices use [N] (single integer in brackets). The parser distinguishes array indices from bit-ranges by lookahead: [N] is an index, [H:L] is a bit-range.

Qualified Names

Procedure references use a module-qualified form:

qual-name     = module-name '.' proc-name

Example: CLK.ConfigPLL, UART.Configure

Indirect Memory

An indirect memory reference is evaluated at run-time. The base is read from a variable or static address, then an optional offset is applied.

indirect      = '[' source { ('+' | '-') offset-value } ']'
source        = 'arg' var-ref
              | 'local' var-ref
              | var-ref
              | hex-literal
              | const-ref
offset-value  = integer | hex-literal | const-ref

The arg and local keywords are optional when the variable name is unambiguous (see Optional Keywords below).

Examples:

[arg dev + 10H]                 -- arg dev, offset +10H
[local buf - 4]                 -- local buf, offset -4
[0x40000000 + 0x100]            -- static address + offset
[arg dev + UARTdef.USART1]       -- const-ref offset

Bit-Range

A bit-range extracts a contiguous field from a 32-bit word. It may appear after any target (mem, arg, local, or composed access).

bit-range     = '[' high ':' low ']'
high          = INTEGER
low           = INTEGER

Example: check mem MCU.FLASH_ACR [3:0] = 0

Locations

Locations specify where in a procedure to set a breakpoint.

location      = 'at' location-spec
location-spec = 'entry'
              | 'exit'
              | 'line' INTEGER
              | HEX
              | INTEGER
Form Meaning
at entry Post-prologue address (after push + sub sp)
at exit Pre-epilogue address (before add sp + pop)
at line N Source line number (from .alst files)
at 0xADDR Absolute hex address
at N Module-relative byte offset (decimal, from .alst files)

The entry and exit addresses are computed from the prologue and epilogue instructions in the .alst file. All four procedure kinds are handled (normal, leaf, interrupt, leaf_interrupt).

The at N form (plain decimal integer) is the module-relative byte offset – the number shown after the dot in .alst assembly lines.

Targets

A target identifies what to check or trace at a breakpoint hit.

target        = explicit-target [ bit-range ]
              | auto-target     [ bit-range ]

explicit-target = 'arg' var-ref
                | 'local' var-ref
                | 'return'
                | 'mem' address
                | 'mem' indirect

auto-target     = indirect           -- auto-detected as mem
                | hex-literal        -- auto-detected as mem
                | const-ref          -- auto-detected as mem
                | var-ref            -- auto-detected as arg or local

Optional Keywords

The arg, local, and mem keywords may be omitted when the meaning is unambiguous:

  • A bare [...] expression is treated as mem [...].
  • A bare hex literal is treated as mem address.
  • A bare Module.Const is treated as mem Module.Const.
  • A bare identifier (or dotted name) is matched against the procedure's parameters and locals by the resolver.

Comparison Operators

Six Oberon-style operators are available for check actions:

Operator Token Meaning
= EQUALS equal
# HASH not equal
> GREATER greater than
< LESS less than
>= GREATEREQUAL greater or equal
<= LESSEQUAL less or equal

Comparisons are performed as 32-bit values. The signedness rule determines whether the comparison is signed or unsigned (see Values / Signedness above).

Boolean guard: ordered operators (>, <, >=, <=) are not permitted with TRUE or FALSE expected values. Use = or # only.

Actions

Actions appear under a location block, indented by four spaces.

action        = check-action | trace-action

check-action  = 'check' target op value [ signedness ] [ 'halt' ]
trace-action  = 'trace' target

op            = '=' | '#' | '>' | '<' | '>=' | '<='

check

Evaluates the target and compares the result to the expected value using the given operator. Records PASS or FAIL.

The optional halt keyword causes the engine to stop execution if the check fails. On halt, breakpoints are cleared and the target MCU remains halted at the failure point.

trace

Evaluates the target and records its value. No comparison is performed; the result is always TRACE (neither PASS nor FAIL).

Directives

Directives appear at the top level (no indentation).

option

option-dir    = 'option' option-name '=' option-value
Option Default Description
timeout 30 Wall-clock timeout in seconds
max-hits 500 Maximum total breakpoint hits
halt-enabled true Honour per-action halt keywords
halt-on-fail false Halt on any check failure

Setting halt-enabled = false disables all per-action halt keywords without editing the spec. Setting halt-on-fail = true halts on every check failure regardless of per-action annotations.

start

start-dir     = 'start' 'proc' qual-name location [ 'when' condition ]

Defines a sentinel breakpoint that opens the execution window. Before the start sentinel fires, all check and trace actions are suppressed.

If no start directive is present, the window is open from the beginning.

stop

stop-dir      = 'stop' 'proc' qual-name location [ 'when' condition ]
              | 'stop' 'after' 'all' 'checks'
              | 'stop' 'after' INTEGER 'hits'
              | 'stop' 'after' INTEGER 's'
Form Meaning
stop proc M.P at loc Stop when procedure location is hit
stop after all checks Stop once every check action has been evaluated
stop after N hits Stop after N total breakpoint hits
stop after N s Wall-clock timeout (same as option timeout)

Conditions

condition     = 'when' 'hits' '=' INTEGER
              | 'when' var-ref '=' value

Each sentinel maintains its own hit counter. when hits = N fires on the Nth hit at that sentinel's breakpoint.

Procedure Blocks

proc-block    = 'proc' qual-name NEWLINE
                  { location-block }

location-block = INDENT location NEWLINE
                   { action-line }

action-line    = INDENT INDENT action NEWLINE

Indentation is two spaces per level: locations at column 2, actions at column 4.

Complete Example

-- SignalSync init-phase test
option timeout = 15
option max-hits = 50

start proc CLK.ConfigPLL at entry
stop after 6 hits

proc CLK.ConfigPLL
  at entry
    check arg pllId = CLK.PLL1
    check arg cfg.src = CLK.PLLsrc_HSI16
    check mem MCU.FLASH_ACR [3:0] = 0H
    trace mem MCU.RCC_CFGR1

proc UART.Configure
  at entry
    check arg dev # NIL
    check arg dev.uartId = UARTdef.USART1
    check arg dev.CR1 = 050013800H
    trace arg dev.BRR

proc SignalSync.run
  at entry
    trace arg tid
  at exit
    check local count > 0

Environment Variables

When running inside GDB, the engine reads its configuration from environment variables (set via (gdb) set environment or the shell).

Variable Default Description
SPEC Spec filename or relative path (required)
RTK_PROJECT_DIR cwd Project root (must contain rdb/, *.elf, *.map)
RTK_GDB_TESTS_SPECS_DIR rdb-gdb-tests/specs Spec subdirectory within project
RTK_REPORTS_DIR Report output directory

Spec path resolution:

  • With RTK_GDB_TESTS_SPECS_DIR: project_dir / RTK_GDB_TESTS_SPECS_DIR / SPEC
  • Without: project_dir / SPEC

Report directory defaults to reports/ alongside the spec file.

Last updated: 28 March 2026