rdlfmt 0.1.1

A formatter for SystemRDL
Documentation

rdlfmt

A formatter for SystemRDL 2.0, following the PeakRDL style guide.

Before:

addrmap top{
  reg {
      field{sw=rw;
    hw=r;} data[31:0];   // payload
  }ctrl @0x0;


  reg{field{sw=r;hw=w;}status[7:0];}stat@0x4;
};

After:

addrmap top {
    reg {
        field {
            sw = rw;
            hw = r;
        } data[31:0]; // payload
    } ctrl @ 0x0;

    reg {
        field {
            sw = r; hw = w;
        } status[7:0];
    } stat @ 0x4;
};

Install

A prebuilt binary, needing no toolchain of any kind:

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/fischeti/rdlfmt/releases/latest/download/rdlfmt-installer.sh | sh

On Windows:

powershell -c "irm https://github.com/fischeti/rdlfmt/releases/latest/download/rdlfmt-installer.ps1 | iex"

From PyPI, which is convenient if you already manage peakrdl this way. The package is the Rust binary in a wheel, not a Python program, so it pulls in nothing else:

uv tool install rdlfmt

Or without installing at all:

uvx rdlfmt --check .

From crates.io, if you have a Rust toolchain:

cargo install rdlfmt

Or build from source (Rust 1.88+):

cargo build --release

As a PeakRDL plugin

Installed into the same environment as PeakRDL, the wheel also registers a peakrdl fmt subcommand:

uv tool install peakrdl-cli --with rdlfmt
peakrdl fmt --check .

Usage

Format a file, rewriting it in place — this is the default, and what you want most of the time:

rdlfmt regs.rdl

Format every .rdl file in a directory tree:

rdlfmt .

Check without writing anything. Exits 1 if any file is not formatted, which is the one for CI:

rdlfmt --check .

Same, but show what would change:

rdlfmt --diff regs.rdl

Write to stdout and leave the file alone:

rdlfmt --stdout regs.rdl

With no path at all, it reads stdin and writes stdout:

cat regs.rdl | rdlfmt

What it does

The style guide's rules, applied mechanically: four spaces per level and never tabs, opening brace on the same line as the statement it belongs to, closing brace on its own line followed by the instance name, spaces around assignment and expression operators, no space before the ; that follows a }.

Line breaks between statements are yours. rdlfmt neither forces one statement per line nor joins them, so grouping you put there on purpose survives — which is also how the style guide's sw/hw exception is accommodated without a special case. Note the two registers in the example above: the first one's properties stay split, the second's stay joined. Runs of blank lines collapse to one.

There is nothing to configure, deliberately. A formatter earns its value by ending arguments, not by relocating them into a config file.

Comments and preprocessor directives survive. The parser builds a lossless concrete syntax tree, so every byte of the input is present in the tree — a `ifdef or a trailing // comment is data to be placed, not noise to be dropped.

What it will not do

Format a file it did not fully understand. If the input has syntax errors, rdlfmt reports them and refuses, rather than reformatting a structure it had to guess at.

Change your code. Every result is verified before it is returned: the output is re-lexed and compared token by token against the input, and if anything but whitespace moved, the output is withheld and you get a bug report instead of a damaged file. That check is what makes rewriting files in place the default.

Library

The formatter is also a library:

let formatted = rdlfmt::format(source)?;

rdlfmt::syntax exposes the lexer and the CST underneath, if you want the tree rather than the text. Turn off default features to drop the CLI dependencies.

License

Dual-licensed under either of

at your option.