Contributing to Yosys

Reporting bugs

We fix well-reported bugs the fastest. A good bug report is an issue on the issue tracker and includes the following information:

Title

briefly describe the issue, for example:

techmap of wide mux with undefined inputs raises error during synth_xilinx

  • tells us what’s happening (“raises error”)

  • gives the command affected (techmap)

  • an overview of the input design (“wide mux with undefined inputs”)

  • and some context where it was found (“during synth_xilinx”)

Reproduction Steps

The reproduction steps should be a minimal, complete and verifiable example MVCE. Providing an MVCE with your bug report drastically increases the likelihood that someone will be able to help resolve your issue. Make sure that your report input is free of any problems as reported by the check command. One way to minimize a design is to use the bugpoint command. You can learn more in the how-to guide for bugpoint.

The reproduction steps are ideally a code-block (starting and ending with triple backquotes) containing the minimized design (Verilog or RTLIL), followed by a code-block containing the minimized yosys script OR a command line call to yosys with code-formatting (starting and ending with single backquotes).

min.v
```verilog
// minimized Verilog design
```

min.ys
```
read_verilog min.v
# minimum sequence of commands to reproduce error
```

OR

`yosys -p ': minimum sequence of commands;' min.v`

Alternatively, you can provide a single code-block which includes the minimized design as a “here document” followed by the sequence of commands which reproduce the error

```
read_rtlil <<EOF
# minimized RTLIL design
EOF
# minimum sequence of commands
```

Don’t forget to mention:

  • any important environment variables or command line options

  • if the problem occurs for a range of values/designs, what is that range

  • if you’re using an external tool, such as valgrind, to detect the issue, what version of that tool are you using and what options are you giving it

Warning

Please try to avoid the use of any external plugins/tools in the reproduction steps if they are not directly related to the issue being raised. This includes frontend plugins such as GHDL or slang; use write_rtlil on the minimized design instead. This also includes tools which provide a wrapper around Yosys such as OpenLane; you should instead minimize your input and reproduction steps to just the Yosys part.

Expected Behaviour

Describe what you’d expect to happen when we follow the reproduction steps if the bug was fixed.

If you have a similar design/script that doesn’t give the error, include it here as a reference. If the bug is that an error should be raised but isn’t, note if there are any other commands with similar error messages.

Actual Behaviour

Describe what you actually see when you follow the reproduction steps.

This can include:

  • any error messages

  • any details relevant to the crash that were found with --trace or --debug flags

  • the part of the source code that triggers the bug

    • if possible, use a permalink to the source on GitHub

    • you can browse the source repository for a certain commit with the failure and open the source file, select the relevant lines (click on the line number for the first relevant line, then while holding shift click on the line number for the last relevant line), click on the ... that appears and select “Copy permalink”

    • should look something like https://github.com/YosysHQ/yosys/blob/<commit_hash>/path/to/file#L139-L147

    • clicking on “Preview” should reveal a code block containing the lines of source specified, with a link to the source file at the given commit

Additional Details

Anything else you think might be helpful or relevant when verifying or fixing the bug.

Once you have created the issue, any additional details can be added as a comment on that issue. You can include any additional context as to what you were doing when you first encountered the bug.

If this issue discovered through the use of a fuzzer, ALWAYS declare that. If you’ve minimized the script, consider including the bugpoint script you used, or the original script, for example:

Minimized with
```
read_verilog design.v
# original sequence of commands prior to error
bugpoint -script <failure.ys> -grep "<string>"
write_rtlil min.il
```

OR

Minimized from
`yosys -p ': original sequence of commands to produce error;' design.v`

If possible, it may also help to share the original un-minimized design. If the design is too big for a comment, consider turning it into a Gist

Contributing code

Code that matters

If you’re adding complex functionality, or modifying core parts of yosys, we highly recommend discussing your motivation and approach ahead of time on the Discourse forum. Please, be as explicit and concrete as possible when explaining the motivation for what you’re building. Additionally, if you do so on the forum first before you starting hacking away at C++, you might solve your problem without writing a single line of code!

PRs are considered for relevance, priority, and quality based on their descriptions first, code second.

Before you build or fix something, also search for existing issues.

We have open developer ‘jour fixe’ (Dev JF) meetings where developers from YosysHQ and the community come together to discuss open issues and PRs. This is also a good place to talk to us about how to implement larger PRs.

Making sense

Given enough effort, the behavior of any code can be figured out to any desired extent. However, the author of the code is by far in the best position to make this as easy as possible.

Yosys is a long-standing project and has accumulated a lot of C-style code that’s not written to be read, just written to run. We improve this bit by bit when opportunities arise, but it is what it is. New additions are expected to be a lot cleaner.

The purpose and behavior of the code changed should be described clearly. Your change should contain exactly what it needs to match that description. This means:

  • nothing more than that - no dead code, no undocumented features

  • nothing missing - if something is partially built, that’s fine, but you have to make that clear. For example, some passes only support some types of cells

Here are some software engineering approaches that help:

  • Use abstraction to model the problem and hide details

    • Maximize the usage of types (structs over loose variables), not necessarily in an object-oriented way

    • Use functions, scopes, type aliases

  • In new passes, make sure the logic behind how and why it works is actually provided in coherent comments, and that variable and type naming is consistent with the terms you use in the description.

  • The logic of the implementation should be described in mathematical or algorithm theory terms. Correctness, termination, computational complexity. Make it clear if you’re re-implementing a classic data structure for logic synthesis or graph traversal etc.

  • There’s various ways of traversing the design with use-def indices (for getting drivers and driven signals) available in Yosys. They have advantages and sometimes disadvantages. Prefer not re-implementing these

  • Prefer references over pointers, and smart pointers over raw pointers

  • Aggressively deduplicate code. Within functions, within passes, across passes, even against existing code

  • Prefer declaring things const

  • Prefer range-based for loops over C-style

Common mistakes

  • Deleting design objects invalidates iterators. Defer deletions or hold a copy of the list of pointers to design objects

  • Deleting wires can get sketchy and is intended to be done solely by the opt_clean pass so just don’t do it

  • Iterating over an entire design and checking if things are selected is more inefficient than using the selected_* methods

  • Remember to call fixup_ports at the end if you’re modifying module interfaces

Testing your change

Untested code can’t be maintained. Inevitable codebase-wide changes are likely to break anything untested. Tests also help reviewers understand the purpose of the code change in practice.

Your code needs to come with tests. If it’s a feature, a test that covers representative examples of the added behavior. If it’s a bug fix, it should reproduce the original isolated bug. But in some situations, adding a test isn’t viable. If you can’t provide a test, explain this decision.

Prefer writing unit tests (tests/unit) for isolated tests to the internals of more serious code changes, like those to the core of yosys, or more algorithmic ones.

The rest of the test suite is mostly based on running Yosys on various Yosys and Tcl scripts that manually call Yosys commands. See Testing Yosys for more information about how our test suite is structured. The basic test writing approach is checking for the presence of some kind of object or pattern with -assert-count in Selections.

It’s often best to use equivalence checking with equiv_opt -assert or similar to prove that the changes done to the design by a modified pass preserve equivalence. But some code isn’t meant to preserve equivalence. Sometimes proving equivalence takes an impractically long time for larger inputs. Also beware, the equiv_ passes are a bit quirky and might even have incorrect results in unusual situations.

Coding style

Yosys is written in C++20.

In general Yosys uses int instead of size_t. To avoid compiler warnings for implicit type casts, always use GetSize(foobar) instead of foobar.size(). (GetSize() is defined in kernel/yosys.h)

For auto formatting code, a .clang-format file is present top-level. Yosys code is using tabs for indentation. A tab is 8 characters wide, but prefer not relying on it. A continuation of a statement in the following line is indented by two additional tabs. Lines are as long as you want them to be. A good rule of thumb is to break lines at about column 150. Opening braces can be put on the same or next line as the statement opening the block (if, switch, for, while, do). Put the opening brace on its own line for larger blocks, especially blocks that contains blank lines. Remove trailing whitespace on sight.

Otherwise stick to the Linux Kernel Coding Style.

Pull requests (PRs)

If you are working on something to add to Yosys, or fix something that isn’t working quite right, make a pull request (PR).

An open PR, even as a draft, tells everyone that you’re working on it and they don’t have to. It can also be a useful way to solicit feedback on in-progress changes.

We use labels to help categorise issues and PRs. If a label seems relevant to your work, please do add it; this also includes the labels beginning with ‘status-’. The ‘merge-’ labels are used by maintainers for tracking and communicating which PRs are ready and pending merge; please do not use these labels if you are not a maintainer.

Git style

We don’t have a strict commit message style.

Some style hints:

  • Refactor and document existing code if you touch it, but in separate commits from your functional changes

  • Prefer smaller commits organized by good chunks. Git has a lot of features like fixup commits, interactive rebase with autosquash

Reviewing PRs

Reviewing PRs is a totally valid form of external contributing to the project!

Who’s the reviewer?

YosysHQ GmbH is a company with a mandate to make decisions for the good of YosysHQ open source software. It was co-founded by Claire Xenia Wolf, the original author of Yosys. Within it, we allocate reviews based on expertise with the topic at hand as well as member time constraints. However, decisions including reviews are also contributed by people external to the company.

If you’re intimately acquainted with a part of the codebase, we will be happy to defer to your experience and have you review PRs. The official way we like is our CODEOWNERS file in the git repository. What we’re looking for in code owners is activity and trust. For activity, if you’re only interested in a yosys pass for example for the time you spend writing a thesis, it might be better to focus on writing good tests and docs in the PRs you submit rather than to commit to code ownership and therefore to be responsible for fixing things and reviewing other people’s PRs at various unexpected points later. If you’re prolific in some part of the codebase and not a code owner, we still value your experience and may tag you in PRs.

As a matter of fact, the purpose of code ownership is to avoid maintainer burnout by removing orphaned parts of the codebase. If you become a code owner and stop being responsive, in the future, we might decide to remove such code if convenient and costly to maintain. It’s simply more respectful of the users’ time to explicitly cut something out than let it “bitrot”. Larger projects like LLVM or linux could not survive without such things, but Yosys is far smaller, and there are implicit expectations of stability we aim to respect within reason.

Sometimes, multiple maintainers may add review comments. This is considered healthy collaborative even if it might create disagreement at times. If somebody is already reviewing a PR, others, even non-maintainers are free to leave comments with extra observations and alternate perspectives in a collaborative spirit.

How to review

First, read everything above about contributing. Those are the values you should gently enforce as a reviewer. They’re ordered by importance, but explicitly, descriptions are more important than code, long-form comments describing the design are more important than piecemeal comments, etc.

If a PR is poorly described, incomplete, tests are broken, or if the author is not responding, please don’t feel pressured to take over their role by reverse engineering the code or fixing things for them, unless there are good reasons to do so.

If a PR author submits LLM outputs they haven’t understood themselves, they will not be able to implement feedback. Take this into consideration as well. We do not ban LLM code from the codebase, we ban bad code.

Reviewers may have diverse styles of communication while reviewing - one may do one thorough review, another may prefer a back and forth with the basics out the way before digging into the code. Generally, PRs may have several requests for modifications and long discussions, but often they just are good enough to merge as-is.

The CI is required to go green for merging. New contributors need a CI run to be triggered by a maintainer before their PRs take up computing resources. It’s a single click from the github web interface. We test on various platforms and compilers. Sanitizer builds are only tested on the main branch.