A fast, modern, and opinionated VHDL code formatter designed to improve readability and enforce consistency across your projects.
Note
I am currently rewriting the project in Rust. While the new version is significantly improved in nearly every metric, I am still having some troubles with the comment handling. If you're interested in testing the latest progress, you can build from the rewrite-in-rust branch.
TODO
Run vhdl-fmt <file> to format a VHDL file. By default, the formatted output is written to stdout.
vhdl-fmt file.vhd| Flag | Alias | Description |
|---|---|---|
--write |
-w |
Overwrite the input file(s) with the formatted output. |
--check |
-c |
Verify whether the input file(s) are correctly formatted. Exits with a non-zero status if any file is not. |
--location <path> |
-l <path> |
Specify a custom configuration file location. |
--help |
-h |
Display this help message. |
--version |
-v |
Print the formatter version. |
vhdl-fmt can be configured using a YAML file. By default, it searches for a vhdl-fmt.yaml in the current working directory. An alternative path can be provided via the -l / --location option.
The example below shows all supported configuration options along with their default values:
line_length: 100
indentation:
size: 4
casing:
keywords: "preserve" # | "lower_case" | "UPPER_CASE"
identifiers: "preserve" # | "lower_case" | "UPPER_CASE"
constants: "preserve" # | "lower_case" | "UPPER_CASE"To not conflict with existing guidelines, the formatter preserves the original casing by default.
The formatter deliberately enforces a specific style. The following behaviors are not configurable:
-
Indentation always uses spaces. Tabs are automatically converted to spaces.
-
Multiple consecutive blank lines are collapsed into a single empty line.
-
Lines exceeding the maximum length are wrapped, with operators aligned for readability, for example:
if (lhs(flag_pos_inf_c) = '1' and rhs(flag_pos_inf_c) = '1') or (lhs(flag_neg_inf_c) = '1' or compare_eq = '1') then end if; output_value <= input_operand_left + input_operand_right + accumulator_value + pipeline_offset + correction_term + rounding_bias;
-
Declarations (
:,:=), assignments (<=,:=), and similar constructs within the same logical block (not separated by comments or blank lines) are aligned. -
If a label is used (e.g.
my_proc: process), the corresponding end label is enforced (end process my_proc;). -
Certain blocks always use explicit end keywords:
end architectureend caseend entityend ifend loopend process
-
Line endings (
\nvs.\r\n) are preserved. This should be controlled via.editorconfigor.gitattributes.
Contributions are welcome. Please fork the repository and open a pull request with your changes.
For a consistent development experience, using the provided dev container is recommended.
If you prefer a local setup, the following tools are used in development and CI:
clang(clang,clang-format,clang-tidy)cmakeconangersemininja
Notes:
-
clangversion 21 is required and may not be available by default on all Linux distributions. -
gersemiandclang-formatare optional locally, but CI will fail if formatting does not match. -
If you install
gersemiandconanvia a Python package manager (e.g.uv), the following may help:uv venv uv pip install conan gersemi source .venv/bin/activateYou may use
active.fishor whatever shell you use. Exit the environment withdeactivate.
The project provides a Makefile with several convenient targets to simplify common development tasks:
make: Builds the project in debug mode.make BUILD_TYPE=Release: Builds the project in release mode with additional optimizations enabled.make test: Executes the full test suite.make format: Formats the source files according to the project’s formatting rules.make check-format: Verifies that all files are correctly formatted.make lint-diff: Runsclang-tidyonly on files that have changed, which significantly reduces execution time.make coverage-show: Generates the coverage report and opens the HTML report in the browser.
You can always inspect the Makefile to discover additional targets and available shortcuts.
When this project was started, we were not aware of the existence of vhdl-style-guide, which also provides formatting capabilities.
A brief comparison highlights the differences:
-
vhdl-style-guide
- Highly configurable
- Includes certain style guide and linting features that are not provided by
vhdl-fmt
-
vhdl-fmt
- Strongly opinionated, enforces a consistent style
- Significantly faster (up to 30x–50x), making it well suited for editor format-on-save workflows