mirror of https://github.com/YosysHQ/yosys.git
merge
This commit is contained in:
commit
99cf75531f
|
|
@ -6,6 +6,8 @@ body:
|
|||
attributes:
|
||||
value: >
|
||||
|
||||
Learn more [here](https://yosyshq.readthedocs.io/projects/yosys/en/latest/yosys_internals/extending_yosys/contributing.html#reporting-bugs) about how to report bugs. We fix well-reported bugs the fastest.
|
||||
|
||||
If you have a general question, please ask it on the [Discourse forum](https://yosyshq.discourse.group/).
|
||||
|
||||
|
||||
|
|
|
|||
111
CONTRIBUTING.md
111
CONTRIBUTING.md
|
|
@ -1,70 +1,63 @@
|
|||
# Introduction
|
||||
# Contributing to Yosys
|
||||
|
||||
Thanks for thinking about contributing to the Yosys project. If this is your
|
||||
Thanks for considering helping out. If this is your
|
||||
first time contributing to an open source project, please take a look at the
|
||||
following guide:
|
||||
following guide about the basics:
|
||||
https://opensource.guide/how-to-contribute/#orienting-yourself-to-a-new-project.
|
||||
|
||||
Information about the Yosys coding style is available on our Read the Docs:
|
||||
https://yosys.readthedocs.io/en/latest/yosys_internals/extending_yosys/contributing.html.
|
||||
## Asking questions
|
||||
|
||||
# Using the issue tracker
|
||||
|
||||
The [issue tracker](https://github.com/YosysHQ/yosys/issues) is used for
|
||||
tracking bugs or other problems with Yosys or its documentation. It is also the
|
||||
place to go for requesting new features.
|
||||
When [creating a new issue](https://github.com/YosysHQ/yosys/issues/new/choose),
|
||||
we have a few templates available. Please make use of these! It will make it
|
||||
much easier for someone to respond and help.
|
||||
|
||||
### Bug reports
|
||||
|
||||
Before you submit an issue, please check out the [how-to guide for
|
||||
`bugpoint`](https://yosys.readthedocs.io/en/latest/using_yosys/bugpoint.html).
|
||||
This guide will take you through the process of using the [`bugpoint`
|
||||
command](https://yosys.readthedocs.io/en/latest/cmd/bugpoint.html) in Yosys to
|
||||
produce a [minimal, complete and verifiable
|
||||
example](https://stackoverflow.com/help/minimal-reproducible-example) (MVCE).
|
||||
Providing an MVCE with your bug report drastically increases the likelihood that
|
||||
someone will be able to help resolve your issue.
|
||||
|
||||
|
||||
# Using pull requests
|
||||
|
||||
If you are working on something to add to Yosys, or fix something that isn't
|
||||
working quite right, make a [PR](https://github.com/YosysHQ/yosys/pulls)! 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. See below to find the best way to [ask us
|
||||
questions](#asking-questions).
|
||||
|
||||
In general, all changes to the code are done as a PR, with [Continuous
|
||||
Integration (CI)](https://github.com/YosysHQ/yosys/actions) tools that
|
||||
automatically run the full suite of tests compiling and running Yosys. Please
|
||||
make use of this! If you're adding a feature: add a test! Not only does it
|
||||
verify that your feature is working as expected, but it can also be a handy way
|
||||
for people to see how the feature is used. If you're fixing a bug: add a test!
|
||||
If you can, do this first; it's okay if the test starts off failing - you
|
||||
already know there is a bug. CI also helps to make sure that your changes still
|
||||
work under a range of compilers, settings, and targets.
|
||||
|
||||
|
||||
### Labels
|
||||
|
||||
We use [labels](https://github.com/YosysHQ/yosys/labels) to help categorise
|
||||
issues and PRs. If a label seems relevant to your work, please do add it; this
|
||||
also includes the labels beggining 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.
|
||||
|
||||
|
||||
# Asking questions
|
||||
|
||||
If you have a question about how to use Yosys, please ask on our [Discourse forum](https://yosyshq.discourse.group/) or in our [discussions
|
||||
page](https://github.com/YosysHQ/yosys/discussions).
|
||||
If you have a question about how to use Yosys, please ask on our [Discourse forum](https://yosyshq.discourse.group/).
|
||||
The Discourse is also a great place to ask questions about developing or
|
||||
contributing to Yosys.
|
||||
|
||||
We have open [dev 'jour fixe' (JF) meetings](https://docs.google.com/document/d/1SapA6QAsJcsgwsdKJDgnGR2mr97pJjV4eeXg_TVJhRU/edit?usp=sharing) 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.
|
||||
|
||||
## Using the issue tracker
|
||||
|
||||
The [issue tracker](https://github.com/YosysHQ/yosys/issues) is used for
|
||||
tracking bugs or other problems with Yosys or its documentation. It is also the
|
||||
place to go for requesting new features.
|
||||
|
||||
### Bug reports
|
||||
|
||||
Learn more [here](https://yosyshq.readthedocs.io/projects/yosys/en/latest/yosys_internals/extending_yosys/contributing.html#reporting-bugs) about how to report bugs. We fix well-reported bugs the fastest.
|
||||
|
||||
## Contributing code
|
||||
|
||||
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](https://yosyshq.discourse.group/).
|
||||
|
||||
### Using pull requests
|
||||
|
||||
If you are working on something to add to Yosys, or fix something that isn't
|
||||
working quite right,
|
||||
make a [pull request (PR)](https://github.com/YosysHQ/yosys/pulls).
|
||||
|
||||
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. See above to find the best way to [ask us questions](#asking-questions).
|
||||
|
||||
### Continuous integration
|
||||
|
||||
[Continuous Integration (CI)](https://github.com/YosysHQ/yosys/actions) tools
|
||||
automatically compile Yosys and run it with the full suite of tests.
|
||||
If you're a first time contributor, a maintainer has to trigger a run for you.
|
||||
We test on various platforms, compilers. Sanitizer builds are only tested
|
||||
on the main branch.
|
||||
|
||||
### Labels
|
||||
|
||||
We use [labels](https://github.com/YosysHQ/yosys/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.
|
||||
|
||||
|
||||
### Coding style
|
||||
|
||||
Learn more [here](https://yosys.readthedocs.io/en/latest/yosys_internals/extending_yosys/contributing.html).
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -177,7 +177,7 @@ ifeq ($(OS), Haiku)
|
|||
CXXFLAGS += -D_DEFAULT_SOURCE
|
||||
endif
|
||||
|
||||
YOSYS_VER := 0.61+0
|
||||
YOSYS_VER := 0.61+44
|
||||
YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1)
|
||||
YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2)
|
||||
YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'.' -f3)
|
||||
|
|
|
|||
|
|
@ -246,6 +246,8 @@ Building the documentation
|
|||
|
||||
Note that there is no need to build the manual if you just want to read it.
|
||||
Simply visit https://yosys.readthedocs.io/en/latest/ instead.
|
||||
If you're offline, you can read the sources, replacing `.../en/latest`
|
||||
with `docs/source`.
|
||||
|
||||
In addition to those packages listed above for building Yosys from source, the
|
||||
following are used for building the website:
|
||||
|
|
|
|||
2
abc
2
abc
|
|
@ -1 +1 @@
|
|||
Subproject commit 799ba632239b2a4db2bacda81de4e6efdc486b0c
|
||||
Subproject commit 01ad37aada7566964219c993818af75234f93ce0
|
||||
|
|
@ -1,57 +1,16 @@
|
|||
Contributing to Yosys
|
||||
=====================
|
||||
|
||||
.. note::
|
||||
|
||||
For information on making a pull request on github, refer to our
|
||||
|CONTRIBUTING|_ file.
|
||||
|
||||
.. |CONTRIBUTING| replace:: :file:`CONTRIBUTING.md`
|
||||
.. _CONTRIBUTING: https://github.com/YosysHQ/yosys/blob/main/CONTRIBUTING.md
|
||||
|
||||
Coding Style
|
||||
------------
|
||||
|
||||
Formatting of code
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- Yosys code is using tabs for indentation. Tabs are 8 characters.
|
||||
|
||||
- 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.
|
||||
|
||||
- Otherwise stick to the `Linux Kernel Coding Style`_.
|
||||
|
||||
.. _Linux Kernel Coding Style: https://www.kernel.org/doc/Documentation/process/coding-style.rst
|
||||
|
||||
|
||||
C++ Language
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Yosys is written in C++17.
|
||||
|
||||
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 :file:`kernel/yosys.h`)
|
||||
|
||||
Use range-based for loops whenever applicable.
|
||||
|
||||
|
||||
Reporting bugs
|
||||
--------------
|
||||
|
||||
- use the `bug report template`_
|
||||
A good bug report includes the following information:
|
||||
|
||||
.. _bug report template: https://github.com/YosysHQ/yosys/issues/new?template=bug_report.yml
|
||||
|
||||
- short title briefly describing the issue, e.g.
|
||||
Title
|
||||
~~~~~
|
||||
|
||||
briefly describe the issue, for example:
|
||||
|
||||
techmap of wide mux with undefined inputs raises error during synth_xilinx
|
||||
|
||||
|
|
@ -64,10 +23,18 @@ Reporting bugs
|
|||
Reproduction Steps
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- 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)
|
||||
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.
|
||||
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).
|
||||
|
||||
.. code-block:: markdown
|
||||
|
||||
|
|
@ -86,9 +53,9 @@ Reproduction Steps
|
|||
|
||||
`yosys -p ': minimum sequence of commands;' min.v`
|
||||
|
||||
- alternatively 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
|
||||
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
|
||||
|
||||
+ see :doc:`/using_yosys/more_scripting/load_design` for more on heredocs.
|
||||
|
||||
|
|
@ -101,7 +68,9 @@ Reproduction Steps
|
|||
# minimum sequence of commands
|
||||
```
|
||||
|
||||
- any environment variables or command line options should also be mentioned
|
||||
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
|
||||
|
|
@ -115,46 +84,58 @@ Reproduction Steps
|
|||
around Yosys such as OpenLane; you should instead minimize your input and
|
||||
reproduction steps to just the Yosys part.
|
||||
|
||||
"Expected Behaviour"
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
.. _MVCE: https://stackoverflow.com/help/minimal-reproducible-example
|
||||
.. _bugpoint: https://yosys.readthedocs.io/en/latest/cmd/bugpoint.html
|
||||
.. _how-to guide for bugpoint: https://yosys.readthedocs.io/en/latest/using_yosys/bugpoint.html
|
||||
|
||||
- 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, are there any other
|
||||
commands with similar error messages
|
||||
|
||||
|
||||
"Actual Behaviour"
|
||||
Expected Behaviour
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- any error messages go here
|
||||
- any details relevant to the crash that were found with ``--trace`` or
|
||||
``--debug`` flags
|
||||
- if you identified the point of failure in the source code, you could mention
|
||||
it here, or as a comment below
|
||||
Describe what you'd expect to happen when we follow the reproduction steps
|
||||
if the bug was fixed.
|
||||
|
||||
+ if possible, use a permalink to the source on GitHub
|
||||
+ you can browse the source repository for a certain commit with the failure
|
||||
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
|
||||
* 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
|
||||
* 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
|
||||
Additional Details
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- once you have created the issue, any additional details can be added as a
|
||||
comment on that issue
|
||||
- could include any additional context as to what you were doing when you first
|
||||
encountered the bug
|
||||
- was this issue discovered through the use of a fuzzer
|
||||
- if you've minimized the script, consider including the `bugpoint` script you
|
||||
used, or the original script, e.g.
|
||||
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:
|
||||
|
||||
.. code-block:: markdown
|
||||
|
||||
|
|
@ -171,8 +152,226 @@ Additional details
|
|||
Minimized from
|
||||
`yosys -p ': original sequence of commands to produce error;' design.v`
|
||||
|
||||
- if you're able to, 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`_
|
||||
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`_
|
||||
|
||||
.. _Gist: https://gist.github.com/
|
||||
|
||||
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`_.
|
||||
|
||||
.. _`Discourse forum`: https://yosyshq.discourse.group/
|
||||
.. _`issues`: https://github.com/YosysHQ/yosys/issues
|
||||
|
||||
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 (:file:`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 :doc:`/yosys_internals/extending_yosys/test_suites` 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
|
||||
:doc:`/using_yosys/more_scripting/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.
|
||||
|
||||
.. Changes to core parts of Yosys or passes that are included in synthesis flows
|
||||
.. can change runtime and memory usage - for the better or for worse. This strongly
|
||||
.. depends on the design involved. Such risky changes should then be benchmarked
|
||||
.. with various designs.
|
||||
|
||||
.. TODO Emil benchmarking
|
||||
|
||||
Coding style
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Yosys is written in C++17.
|
||||
|
||||
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 :file:`kernel/yosys.h`)
|
||||
|
||||
For auto formatting code, a :file:`.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`_.
|
||||
|
||||
.. _Linux Kernel Coding Style: https://www.kernel.org/doc/Documentation/process/coding-style.rst
|
||||
|
||||
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?
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Yosys HQ is a company with the inherited mandate to make decisions on behalf
|
||||
of the open source project. As such, we at HQ are collectively the maintainers.
|
||||
Within HQ, we allocate reviews based on expertise with the topic at hand
|
||||
as well as member time constraints.
|
||||
|
||||
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 expectations
|
||||
|
||||
.. TODO this deserves its own section elsewhere I think? But it would be distracting elsewhere
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -3196,9 +3196,11 @@ struct VerificPass : public Pass {
|
|||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
#ifdef VERIFIC_SYSTEMVERILOG_SUPPORT
|
||||
log(" verific {-vlog95|-vlog2k|-sv2005|-sv2009|-sv2012|-sv} <verilog-file>..\n");
|
||||
log(" verific {-vlog95|-vlog2k|-sv2005|-sv2009|-sv2012|\n");
|
||||
log(" -sv2017|-sv} <verilog-file>..\n");
|
||||
log("\n");
|
||||
log("Load the specified Verilog/SystemVerilog files into Verific.\n");
|
||||
log("Note that -sv option will use latest supported SystemVerilog standard.\n");
|
||||
log("\n");
|
||||
log("All files specified in one call to this command are one compilation unit.\n");
|
||||
log("Files passed to different calls to this command are treated as belonging to\n");
|
||||
|
|
@ -3243,7 +3245,7 @@ struct VerificPass : public Pass {
|
|||
#endif
|
||||
#ifdef VERIFIC_SYSTEMVERILOG_SUPPORT
|
||||
log(" verific {-f|-F} [-vlog95|-vlog2k|-sv2005|-sv2009|\n");
|
||||
log(" -sv2012|-sv|-formal] <command-file>\n");
|
||||
log(" -sv2012|-sv2017|-sv|-formal] <command-file>\n");
|
||||
log("\n");
|
||||
log("Load and execute the specified command file.\n");
|
||||
log("Override verilog parsing mode can be set.\n");
|
||||
|
|
@ -3963,7 +3965,8 @@ struct VerificPass : public Pass {
|
|||
}
|
||||
|
||||
if (GetSize(args) > argidx && (args[argidx] == "-vlog95" || args[argidx] == "-vlog2k" || args[argidx] == "-sv2005" ||
|
||||
args[argidx] == "-sv2009" || args[argidx] == "-sv2012" || args[argidx] == "-sv" || args[argidx] == "-formal"))
|
||||
args[argidx] == "-sv2009" || args[argidx] == "-sv2012" || args[argidx] == "-sv2017" || args[argidx] == "-sv" ||
|
||||
args[argidx] == "-formal"))
|
||||
{
|
||||
Array file_names;
|
||||
unsigned verilog_mode;
|
||||
|
|
@ -3976,7 +3979,11 @@ struct VerificPass : public Pass {
|
|||
verilog_mode = veri_file::SYSTEM_VERILOG_2005;
|
||||
else if (args[argidx] == "-sv2009")
|
||||
verilog_mode = veri_file::SYSTEM_VERILOG_2009;
|
||||
else if (args[argidx] == "-sv2012" || args[argidx] == "-sv" || args[argidx] == "-formal")
|
||||
else if (args[argidx] == "-sv2012")
|
||||
verilog_mode = veri_file::SYSTEM_VERILOG_2012;
|
||||
else if (args[argidx] == "-sv2017")
|
||||
verilog_mode = veri_file::SYSTEM_VERILOG_2017;
|
||||
else if (args[argidx] == "-sv" || args[argidx] == "-formal")
|
||||
verilog_mode = veri_file::SYSTEM_VERILOG;
|
||||
else
|
||||
log_abort();
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@
|
|||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* ConstEval provides on-demand constant propagation by traversing input cones
|
||||
* with caching
|
||||
*/
|
||||
struct ConstEval
|
||||
{
|
||||
RTLIL::Module *module;
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include <optional>
|
||||
#include <set>
|
||||
#include <string_view>
|
||||
#include <sstream>
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
|
|
@ -187,7 +188,7 @@ struct IdStringCollector {
|
|||
trace(selection_var.selected_modules);
|
||||
trace(selection_var.selected_members);
|
||||
}
|
||||
void trace_named(const RTLIL::NamedObject named) {
|
||||
void trace_named(const RTLIL::NamedObject &named) {
|
||||
trace_keys(named.attributes);
|
||||
trace(named.name);
|
||||
}
|
||||
|
|
@ -1550,6 +1551,13 @@ void RTLIL::Design::pop_selection()
|
|||
push_full_selection();
|
||||
}
|
||||
|
||||
std::string RTLIL::Design::to_rtlil_str(bool only_selected) const
|
||||
{
|
||||
std::ostringstream f;
|
||||
RTLIL_BACKEND::dump_design(f, const_cast<RTLIL::Design*>(this), only_selected);
|
||||
return f.str();
|
||||
}
|
||||
|
||||
std::vector<RTLIL::Module*> RTLIL::Design::selected_modules(RTLIL::SelectPartials partials, RTLIL::SelectBoxes boxes) const
|
||||
{
|
||||
bool include_partials = partials == RTLIL::SELECT_ALL;
|
||||
|
|
@ -4333,6 +4341,13 @@ RTLIL::SigSpec RTLIL::Module::FutureFF(RTLIL::IdString name, const RTLIL::SigSpe
|
|||
return sig;
|
||||
}
|
||||
|
||||
std::string RTLIL::Module::to_rtlil_str() const
|
||||
{
|
||||
std::ostringstream f;
|
||||
RTLIL_BACKEND::dump_module(f, "", const_cast<RTLIL::Module*>(this), design, false);
|
||||
return f.str();
|
||||
}
|
||||
|
||||
RTLIL::Wire::Wire()
|
||||
{
|
||||
static unsigned int hashidx_count = 123456789;
|
||||
|
|
@ -4360,6 +4375,13 @@ RTLIL::Wire::~Wire()
|
|||
#endif
|
||||
}
|
||||
|
||||
std::string RTLIL::Wire::to_rtlil_str() const
|
||||
{
|
||||
std::ostringstream f;
|
||||
RTLIL_BACKEND::dump_wire(f, "", this);
|
||||
return f.str();
|
||||
}
|
||||
|
||||
#ifdef YOSYS_ENABLE_PYTHON
|
||||
static std::map<unsigned int, RTLIL::Wire*> all_wires;
|
||||
std::map<unsigned int, RTLIL::Wire*> *RTLIL::Wire::get_all_wires(void)
|
||||
|
|
@ -4382,6 +4404,13 @@ RTLIL::Memory::Memory()
|
|||
#endif
|
||||
}
|
||||
|
||||
std::string RTLIL::Memory::to_rtlil_str() const
|
||||
{
|
||||
std::ostringstream f;
|
||||
RTLIL_BACKEND::dump_memory(f, "", this);
|
||||
return f.str();
|
||||
}
|
||||
|
||||
RTLIL::Process::Process() : module(nullptr)
|
||||
{
|
||||
static unsigned int hashidx_count = 123456789;
|
||||
|
|
@ -4389,6 +4418,13 @@ RTLIL::Process::Process() : module(nullptr)
|
|||
hashidx_ = hashidx_count;
|
||||
}
|
||||
|
||||
std::string RTLIL::Process::to_rtlil_str() const
|
||||
{
|
||||
std::ostringstream f;
|
||||
RTLIL_BACKEND::dump_proc(f, "", this);
|
||||
return f.str();
|
||||
}
|
||||
|
||||
RTLIL::Cell::Cell() : module(nullptr)
|
||||
{
|
||||
static unsigned int hashidx_count = 123456789;
|
||||
|
|
@ -4410,6 +4446,13 @@ RTLIL::Cell::~Cell()
|
|||
#endif
|
||||
}
|
||||
|
||||
std::string RTLIL::Cell::to_rtlil_str() const
|
||||
{
|
||||
std::ostringstream f;
|
||||
RTLIL_BACKEND::dump_cell(f, "", this);
|
||||
return f.str();
|
||||
}
|
||||
|
||||
#ifdef YOSYS_ENABLE_PYTHON
|
||||
static std::map<unsigned int, RTLIL::Cell*> all_cells;
|
||||
std::map<unsigned int, RTLIL::Cell*> *RTLIL::Cell::get_all_cells(void)
|
||||
|
|
|
|||
|
|
@ -2043,6 +2043,8 @@ struct RTLIL::Design
|
|||
void run_pass(std::string command);
|
||||
|
||||
static std::map<unsigned int, RTLIL::Design*> *get_all_designs(void);
|
||||
|
||||
std::string to_rtlil_str(bool only_selected = true) const;
|
||||
};
|
||||
|
||||
struct RTLIL::Module : public RTLIL::NamedObject
|
||||
|
|
@ -2406,9 +2408,7 @@ public:
|
|||
RTLIL::SigSpec OriginalTag (RTLIL::IdString name, const std::string &tag, const RTLIL::SigSpec &sig_a, const std::string &src = "");
|
||||
RTLIL::SigSpec FutureFF (RTLIL::IdString name, const RTLIL::SigSpec &sig_e, const std::string &src = "");
|
||||
|
||||
std::string rtlil_dump();
|
||||
unsigned int rtlil_hash();
|
||||
|
||||
std::string to_rtlil_str() const;
|
||||
#ifdef YOSYS_ENABLE_PYTHON
|
||||
static std::map<unsigned int, RTLIL::Module*> *get_all_modules(void);
|
||||
#endif
|
||||
|
|
@ -2462,6 +2462,7 @@ public:
|
|||
return zero_index + start_offset;
|
||||
}
|
||||
|
||||
std::string to_rtlil_str() const;
|
||||
#ifdef YOSYS_ENABLE_PYTHON
|
||||
static std::map<unsigned int, RTLIL::Wire*> *get_all_wires(void);
|
||||
#endif
|
||||
|
|
@ -2479,6 +2480,8 @@ struct RTLIL::Memory : public RTLIL::NamedObject
|
|||
Memory();
|
||||
|
||||
int width, start_offset, size;
|
||||
|
||||
std::string to_rtlil_str() const;
|
||||
#ifdef YOSYS_ENABLE_PYTHON
|
||||
~Memory();
|
||||
static std::map<unsigned int, RTLIL::Memory*> *get_all_memorys(void);
|
||||
|
|
@ -2537,6 +2540,8 @@ public:
|
|||
template<typename T> void rewrite_sigspecs(T &functor);
|
||||
template<typename T> void rewrite_sigspecs2(T &functor);
|
||||
|
||||
std::string to_rtlil_str() const;
|
||||
|
||||
#ifdef YOSYS_ENABLE_PYTHON
|
||||
static std::map<unsigned int, RTLIL::Cell*> *get_all_cells(void);
|
||||
#endif
|
||||
|
|
@ -2615,6 +2620,7 @@ public:
|
|||
template<typename T> void rewrite_sigspecs(T &functor);
|
||||
template<typename T> void rewrite_sigspecs2(T &functor);
|
||||
RTLIL::Process *clone() const;
|
||||
std::string to_rtlil_str() const;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -115,16 +115,45 @@ struct DebugPass : public Pass {
|
|||
log("\n");
|
||||
log("Execute the specified command with debug log messages enabled\n");
|
||||
log("\n");
|
||||
log(" debug -on\n");
|
||||
log(" debug -off\n");
|
||||
log("\n");
|
||||
log("Enable or disable debug log messages globally\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
size_t argidx;
|
||||
bool mode_on = false;
|
||||
bool mode_off = false;
|
||||
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
// .. parse options ..
|
||||
if (args[argidx] == "-on") {
|
||||
mode_on = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-off") {
|
||||
mode_off = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (mode_on && mode_off)
|
||||
log_cmd_error("Cannot specify both -on and -off\n");
|
||||
|
||||
if (mode_on) {
|
||||
log_force_debug++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode_off) {
|
||||
if (log_force_debug > 0)
|
||||
log_force_debug--;
|
||||
return;
|
||||
}
|
||||
|
||||
log_force_debug++;
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ OBJS += passes/opt/opt_lut_ins.o
|
|||
OBJS += passes/opt/opt_ffinv.o
|
||||
OBJS += passes/opt/pmux2shiftx.o
|
||||
OBJS += passes/opt/muxpack.o
|
||||
OBJS += passes/opt/opt_balance_tree.o
|
||||
|
||||
OBJS += passes/opt/peepopt.o
|
||||
GENFILES += passes/opt/peepopt_pm.h
|
||||
|
|
|
|||
|
|
@ -0,0 +1,379 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
* 2019 Eddie Hung <eddie@fpgeh.com>
|
||||
* 2024 Akash Levy <akash@silimate.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include <deque>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
|
||||
struct OptBalanceTreeWorker {
|
||||
// Module and signal map
|
||||
Module *module;
|
||||
SigMap sigmap;
|
||||
|
||||
// Counts of each cell type that are getting balanced
|
||||
dict<IdString, int> cell_count;
|
||||
|
||||
// Check if cell is of the right type and has matching input/output widths
|
||||
// Only allow cells with "natural" output widths (no truncation) to prevent
|
||||
// equivalence issues when rebalancing (see YosysHQ/yosys#5605)
|
||||
bool is_right_type(Cell* cell, IdString cell_type) {
|
||||
if (cell->type != cell_type)
|
||||
return false;
|
||||
|
||||
int y_width = cell->getParam(ID::Y_WIDTH).as_int();
|
||||
int a_width = cell->getParam(ID::A_WIDTH).as_int();
|
||||
int b_width = cell->getParam(ID::B_WIDTH).as_int();
|
||||
|
||||
// Calculate the "natural" output width for this operation
|
||||
int natural_width;
|
||||
if (cell_type == ID($add)) {
|
||||
// Addition produces max(A_WIDTH, B_WIDTH) + 1 (for carry bit)
|
||||
natural_width = std::max(a_width, b_width) + 1;
|
||||
} else if (cell_type == ID($mul)) {
|
||||
// Multiplication produces A_WIDTH + B_WIDTH
|
||||
natural_width = a_width + b_width;
|
||||
} else {
|
||||
// Logic operations ($and/$or/$xor) produce max(A_WIDTH, B_WIDTH)
|
||||
natural_width = std::max(a_width, b_width);
|
||||
}
|
||||
|
||||
// Only allow cells where Y_WIDTH >= natural width (no truncation)
|
||||
// This prevents rebalancing chains where truncation semantics matter
|
||||
return y_width >= natural_width;
|
||||
}
|
||||
|
||||
// Create a balanced binary tree from a vector of source signals
|
||||
SigSpec create_balanced_tree(vector<SigSpec> &sources, IdString cell_type, Cell* cell) {
|
||||
// Base case: if we have no sources, return an empty signal
|
||||
if (sources.size() == 0)
|
||||
return SigSpec();
|
||||
|
||||
// Base case: if we have only one source, return it
|
||||
if (sources.size() == 1)
|
||||
return sources[0];
|
||||
|
||||
// Base case: if we have two sources, create a single cell
|
||||
if (sources.size() == 2) {
|
||||
// Create a new cell of the same type
|
||||
Cell* new_cell = module->addCell(NEW_ID, cell_type);
|
||||
|
||||
// Copy attributes from reference cell
|
||||
new_cell->attributes = cell->attributes;
|
||||
|
||||
// Create output wire
|
||||
int out_width = cell->getParam(ID::Y_WIDTH).as_int();
|
||||
if (cell_type == ID($add))
|
||||
out_width = max(sources[0].size(), sources[1].size()) + 1;
|
||||
else if (cell_type == ID($mul))
|
||||
out_width = sources[0].size() + sources[1].size();
|
||||
Wire* out_wire = module->addWire(NEW_ID, out_width);
|
||||
|
||||
// Connect ports and fix up parameters
|
||||
new_cell->setPort(ID::A, sources[0]);
|
||||
new_cell->setPort(ID::B, sources[1]);
|
||||
new_cell->setPort(ID::Y, out_wire);
|
||||
new_cell->fixup_parameters();
|
||||
new_cell->setParam(ID::A_SIGNED, cell->getParam(ID::A_SIGNED));
|
||||
new_cell->setParam(ID::B_SIGNED, cell->getParam(ID::B_SIGNED));
|
||||
|
||||
// Update count and return output wire
|
||||
cell_count[cell_type]++;
|
||||
return out_wire;
|
||||
}
|
||||
|
||||
// Recursive case: split sources into two groups and create subtrees
|
||||
int mid = (sources.size() + 1) / 2;
|
||||
vector<SigSpec> left_sources(sources.begin(), sources.begin() + mid);
|
||||
vector<SigSpec> right_sources(sources.begin() + mid, sources.end());
|
||||
|
||||
SigSpec left_tree = create_balanced_tree(left_sources, cell_type, cell);
|
||||
SigSpec right_tree = create_balanced_tree(right_sources, cell_type, cell);
|
||||
|
||||
// Create a cell to combine the two subtrees
|
||||
Cell* new_cell = module->addCell(NEW_ID, cell_type);
|
||||
|
||||
// Copy attributes from reference cell
|
||||
new_cell->attributes = cell->attributes;
|
||||
|
||||
// Create output wire
|
||||
int out_width = cell->getParam(ID::Y_WIDTH).as_int();
|
||||
if (cell_type == ID($add))
|
||||
out_width = max(left_tree.size(), right_tree.size()) + 1;
|
||||
else if (cell_type == ID($mul))
|
||||
out_width = left_tree.size() + right_tree.size();
|
||||
Wire* out_wire = module->addWire(NEW_ID, out_width);
|
||||
|
||||
// Connect ports and fix up parameters
|
||||
new_cell->setPort(ID::A, left_tree);
|
||||
new_cell->setPort(ID::B, right_tree);
|
||||
new_cell->setPort(ID::Y, out_wire);
|
||||
new_cell->fixup_parameters();
|
||||
new_cell->setParam(ID::A_SIGNED, cell->getParam(ID::A_SIGNED));
|
||||
new_cell->setParam(ID::B_SIGNED, cell->getParam(ID::B_SIGNED));
|
||||
|
||||
// Update count and return output wire
|
||||
cell_count[cell_type]++;
|
||||
return out_wire;
|
||||
}
|
||||
|
||||
OptBalanceTreeWorker(Module *module, const vector<IdString> cell_types) : module(module), sigmap(module) {
|
||||
// Do for each cell type
|
||||
for (auto cell_type : cell_types) {
|
||||
// Index all of the nets in the module
|
||||
dict<SigSpec, Cell*> sig_to_driver;
|
||||
dict<SigSpec, pool<Cell*>> sig_to_sink;
|
||||
for (auto cell : module->selected_cells())
|
||||
{
|
||||
for (auto &conn : cell->connections())
|
||||
{
|
||||
if (cell->output(conn.first))
|
||||
sig_to_driver[sigmap(conn.second)] = cell;
|
||||
|
||||
if (cell->input(conn.first))
|
||||
{
|
||||
SigSpec sig = sigmap(conn.second);
|
||||
if (sig_to_sink.count(sig) == 0)
|
||||
sig_to_sink[sig] = pool<Cell*>();
|
||||
sig_to_sink[sig].insert(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Need to check if any wires connect to module ports
|
||||
pool<SigSpec> input_port_sigs;
|
||||
pool<SigSpec> output_port_sigs;
|
||||
for (auto wire : module->selected_wires())
|
||||
if (wire->port_input || wire->port_output) {
|
||||
SigSpec sig = sigmap(wire);
|
||||
for (auto bit : sig) {
|
||||
if (wire->port_input)
|
||||
input_port_sigs.insert(bit);
|
||||
if (wire->port_output)
|
||||
output_port_sigs.insert(bit);
|
||||
}
|
||||
}
|
||||
|
||||
// Actual logic starts here
|
||||
pool<Cell*> consumed_cells;
|
||||
for (auto cell : module->selected_cells())
|
||||
{
|
||||
// If consumed or not the correct type, skip
|
||||
if (consumed_cells.count(cell) || !is_right_type(cell, cell_type))
|
||||
continue;
|
||||
|
||||
// BFS, following all chains until they hit a cell of a different type
|
||||
// Pick the longest one
|
||||
auto y = sigmap(cell->getPort(ID::Y));
|
||||
pool<Cell*> sinks;
|
||||
pool<Cell*> current_loads = sig_to_sink[y];
|
||||
pool<Cell*> next_loads;
|
||||
while (!current_loads.empty())
|
||||
{
|
||||
// Find each sink and see what they are
|
||||
for (auto x : current_loads)
|
||||
{
|
||||
// If not the correct type, don't follow any further
|
||||
// (but add the originating cell to the list of sinks)
|
||||
if (!is_right_type(x, cell_type))
|
||||
{
|
||||
sinks.insert(cell);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto xy = sigmap(x->getPort(ID::Y));
|
||||
|
||||
// If this signal drives a port, add it to the sinks
|
||||
// (even though it may not be the end of a chain)
|
||||
for (auto bit : xy) {
|
||||
if (output_port_sigs.count(bit) && !consumed_cells.count(x)) {
|
||||
sinks.insert(x);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Search signal's fanout
|
||||
auto& next = sig_to_sink[xy];
|
||||
for (auto z : next)
|
||||
next_loads.insert(z);
|
||||
}
|
||||
|
||||
// If we couldn't find any downstream loads, stop.
|
||||
// Create a reduction for each of the max-length chains we found
|
||||
if (next_loads.empty())
|
||||
{
|
||||
for (auto s : current_loads)
|
||||
{
|
||||
// Not one of our gates? Don't follow any further
|
||||
if (!is_right_type(s, cell_type))
|
||||
continue;
|
||||
|
||||
sinks.insert(s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Otherwise, continue down the chain
|
||||
current_loads = next_loads;
|
||||
next_loads.clear();
|
||||
}
|
||||
|
||||
// We have our list of sinks, now go tree balance the chains
|
||||
for (auto head_cell : sinks)
|
||||
{
|
||||
// Avoid duplication if we already were covered
|
||||
if (consumed_cells.count(head_cell))
|
||||
continue;
|
||||
|
||||
// Get sources of the chain
|
||||
dict<SigSpec, int> sources;
|
||||
dict<SigSpec, bool> signeds;
|
||||
int inner_cells = 0;
|
||||
std::deque<Cell*> bfs_queue = {head_cell};
|
||||
while (bfs_queue.size())
|
||||
{
|
||||
Cell* x = bfs_queue.front();
|
||||
bfs_queue.pop_front();
|
||||
|
||||
for (IdString port: {ID::A, ID::B}) {
|
||||
auto sig = sigmap(x->getPort(port));
|
||||
Cell* drv = sig_to_driver[sig];
|
||||
bool drv_ok = drv && is_right_type(drv, cell_type);
|
||||
for (auto bit : sig) {
|
||||
if (input_port_sigs.count(bit) && !consumed_cells.count(drv)) {
|
||||
drv_ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (drv_ok) {
|
||||
inner_cells++;
|
||||
bfs_queue.push_back(drv);
|
||||
} else {
|
||||
sources[sig]++;
|
||||
signeds[sig] = x->getParam(port == ID::A ? ID::A_SIGNED : ID::B_SIGNED).as_bool();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inner_cells)
|
||||
{
|
||||
// Create a tree
|
||||
log_debug(" Creating tree for %s with %d sources and %d inner cells...\n", log_id(head_cell), GetSize(sources), inner_cells);
|
||||
|
||||
// Build a vector of all source signals
|
||||
vector<SigSpec> source_signals;
|
||||
vector<bool> signed_flags;
|
||||
for (auto &source : sources) {
|
||||
for (int i = 0; i < source.second; i++) {
|
||||
source_signals.push_back(source.first);
|
||||
signed_flags.push_back(signeds[source.first]);
|
||||
}
|
||||
}
|
||||
|
||||
// If not all signed flags are the same, do not balance
|
||||
if (!std::all_of(signed_flags.begin(), signed_flags.end(), [&](bool flag) { return flag == signed_flags[0]; })) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create the balanced tree
|
||||
SigSpec tree_output = create_balanced_tree(source_signals, cell_type, head_cell);
|
||||
|
||||
// Connect the tree output to the head cell's output
|
||||
SigSpec head_output = sigmap(head_cell->getPort(ID::Y));
|
||||
int connect_width = std::min(head_output.size(), tree_output.size());
|
||||
module->connect(head_output.extract(0, connect_width), tree_output.extract(0, connect_width));
|
||||
if (head_output.size() > tree_output.size()) {
|
||||
SigBit sext_bit = head_cell->getParam(ID::A_SIGNED).as_bool() ? head_output[connect_width - 1] : State::S0;
|
||||
module->connect(head_output.extract(connect_width, head_output.size() - connect_width), SigSpec(sext_bit, head_output.size() - connect_width));
|
||||
}
|
||||
|
||||
// Mark consumed cell for removal
|
||||
consumed_cells.insert(head_cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all consumed cells, which now have been replaced by trees
|
||||
for (auto cell : consumed_cells)
|
||||
module->remove(cell);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct OptBalanceTreePass : public Pass {
|
||||
OptBalanceTreePass() : Pass("opt_balance_tree", "$and/$or/$xor/$add/$mul cascades to trees") { }
|
||||
void help() override {
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" opt_balance_tree [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This pass converts cascaded chains of $and/$or/$xor/$add/$mul cells into\n");
|
||||
log("trees of cells to improve timing.\n");
|
||||
log("\n");
|
||||
log(" -arith\n");
|
||||
log(" only convert arithmetic cells.\n");
|
||||
log("\n");
|
||||
log(" -logic\n");
|
||||
log(" only convert logic cells.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override {
|
||||
log_header(design, "Executing OPT_BALANCE_TREE pass (cell cascades to trees).\n");
|
||||
log_experimental("open_balance_tree");
|
||||
|
||||
// Handle arguments
|
||||
size_t argidx;
|
||||
vector<IdString> cell_types = {ID($and), ID($or), ID($xor), ID($add), ID($mul)};
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-arith") {
|
||||
cell_types = {ID($add), ID($mul)};
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-logic") {
|
||||
cell_types = {ID($and), ID($or), ID($xor)};
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
// Count of all cells that were packed
|
||||
dict<IdString, int> cell_count;
|
||||
for (auto module : design->selected_modules()) {
|
||||
OptBalanceTreeWorker worker(module, cell_types);
|
||||
for (auto cell : worker.cell_count) {
|
||||
cell_count[cell.first] += cell.second;
|
||||
}
|
||||
}
|
||||
|
||||
// Log stats
|
||||
for (auto cell_type : cell_types)
|
||||
log("Converted %d %s cells into trees.\n", cell_count[cell_type], log_id(cell_type));
|
||||
|
||||
// Clean up
|
||||
Yosys::run_pass("clean -purge");
|
||||
}
|
||||
} OptBalanceTreePass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
@ -554,31 +554,27 @@ struct SimInstance
|
|||
if (shared->debug)
|
||||
log("[%s] eval %s (%s)\n", hiername(), log_id(cell), log_id(cell->type));
|
||||
|
||||
// Simple (A -> Y) and (A,B -> Y) cells
|
||||
if (has_a && !has_c && !has_d && !has_s && has_y) {
|
||||
set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b)));
|
||||
return;
|
||||
}
|
||||
bool err = false;
|
||||
RTLIL::Const eval_state;
|
||||
if (has_a && !has_c && !has_d && !has_s && has_y)
|
||||
// Simple (A -> Y) and (A,B -> Y) cells
|
||||
eval_state = CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), &err);
|
||||
else if (has_a && has_b && has_c && !has_d && !has_s && has_y)
|
||||
// (A,B,C -> Y) cells
|
||||
eval_state = CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_c), &err);
|
||||
else if (has_a && !has_b && !has_c && !has_d && has_s && has_y)
|
||||
// (A,S -> Y) cells
|
||||
eval_state = CellTypes::eval(cell, get_state(sig_a), get_state(sig_s), &err);
|
||||
else if (has_a && has_b && !has_c && !has_d && has_s && has_y)
|
||||
// (A,B,S -> Y) cells
|
||||
eval_state = CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_s), &err);
|
||||
else
|
||||
err = true;
|
||||
|
||||
// (A,B,C -> Y) cells
|
||||
if (has_a && has_b && has_c && !has_d && !has_s && has_y) {
|
||||
set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_c)));
|
||||
return;
|
||||
}
|
||||
|
||||
// (A,S -> Y) cells
|
||||
if (has_a && !has_b && !has_c && !has_d && has_s && has_y) {
|
||||
set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_s)));
|
||||
return;
|
||||
}
|
||||
|
||||
// (A,B,S -> Y) cells
|
||||
if (has_a && has_b && !has_c && !has_d && has_s && has_y) {
|
||||
set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_s)));
|
||||
return;
|
||||
}
|
||||
|
||||
log_warning("Unsupported evaluable cell type: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
|
||||
if (err)
|
||||
log_warning("Unsupported evaluable cell type: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
|
||||
else
|
||||
set_state(sig_y, eval_state);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,6 @@ struct DffunmapPass : public Pass {
|
|||
continue;
|
||||
|
||||
FfData ff(&initvals, cell);
|
||||
IdString name = cell->name;
|
||||
|
||||
if (!ff.has_clk)
|
||||
continue;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,61 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
namespace RTLIL {
|
||||
|
||||
TEST(RtlilStrTest, DesignToString) {
|
||||
Design design;
|
||||
Module *mod = design.addModule(ID(my_module));
|
||||
mod->addWire(ID(my_wire), 1);
|
||||
|
||||
std::string design_str = design.to_rtlil_str();
|
||||
|
||||
EXPECT_NE(design_str.find("module \\my_module"), std::string::npos);
|
||||
EXPECT_NE(design_str.find("end"), std::string::npos);
|
||||
}
|
||||
|
||||
TEST(RtlilStrTest, ModuleToString) {
|
||||
Design design;
|
||||
Module *mod = design.addModule(ID(test_mod));
|
||||
Wire *wire = mod->addWire(ID(clk), 1);
|
||||
wire->port_input = true;
|
||||
|
||||
std::string mod_str = mod->to_rtlil_str();
|
||||
|
||||
EXPECT_NE(mod_str.find("module \\test_mod"), std::string::npos);
|
||||
EXPECT_NE(mod_str.find("wire"), std::string::npos);
|
||||
EXPECT_NE(mod_str.find("\\clk"), std::string::npos);
|
||||
EXPECT_NE(mod_str.find("input"), std::string::npos);
|
||||
}
|
||||
|
||||
TEST(RtlilStrTest, WireToString) {
|
||||
Design design;
|
||||
Module *mod = design.addModule(ID(m));
|
||||
Wire *wire = mod->addWire(ID(data), 8);
|
||||
|
||||
std::string wire_str = wire->to_rtlil_str();
|
||||
|
||||
EXPECT_NE(wire_str.find("wire"), std::string::npos);
|
||||
EXPECT_NE(wire_str.find("width 8"), std::string::npos);
|
||||
EXPECT_NE(wire_str.find("\\data"), std::string::npos);
|
||||
}
|
||||
|
||||
TEST(RtlilStrTest, CellToString) {
|
||||
Design design;
|
||||
Module *mod = design.addModule(ID(m));
|
||||
Cell *cell = mod->addCell(ID(u1), ID(my_cell_type));
|
||||
|
||||
std::string cell_str = cell->to_rtlil_str();
|
||||
|
||||
EXPECT_NE(cell_str.find("cell"), std::string::npos);
|
||||
EXPECT_NE(cell_str.find("\\my_cell_type"), std::string::npos);
|
||||
EXPECT_NE(cell_str.find("\\u1"), std::string::npos);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# Test debug -on/-off modes
|
||||
|
||||
design -reset
|
||||
|
||||
read_verilog <<EOT
|
||||
module top(input a, input b, output y);
|
||||
assign y = a & b;
|
||||
endmodule
|
||||
EOT
|
||||
|
||||
debug -on
|
||||
hierarchy
|
||||
select -assert-count 1 t:$and
|
||||
debug -off
|
||||
Loading…
Reference in New Issue