3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-03-02 03:36:56 +00:00

Merge branch 'main' into emil/turbo-celltypes

This commit is contained in:
nella 2026-02-25 12:29:06 +01:00 committed by GitHub
commit 2a2c91e78a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
265 changed files with 11258 additions and 3014 deletions

View file

@ -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/).

View file

@ -42,7 +42,7 @@ runs:
if: runner.os == 'Linux' && inputs.get-build-deps == 'true'
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
with:
packages: bison clang flex libffi-dev libfl-dev libreadline-dev pkg-config tcl-dev zlib1g-dev
packages: bison clang flex libffi-dev libfl-dev libreadline-dev pkg-config tcl-dev zlib1g-dev libgtest-dev
version: ${{ inputs.runs-on }}-buildys
- name: Linux docs dependencies
@ -52,15 +52,6 @@ runs:
packages: graphviz xdot
version: ${{ inputs.runs-on }}-docsys
# if updating test dependencies, make sure to update
# docs/source/yosys_internals/extending_yosys/test_suites.rst to match.
- name: Linux test dependencies
if: runner.os == 'Linux' && inputs.get-test-deps == 'true'
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
with:
packages: libgtest-dev
version: ${{ inputs.runs-on }}-testys
- name: Install macOS Dependencies
if: runner.os == 'macOS'
shell: bash

View file

@ -5,6 +5,7 @@ on:
push:
branches:
- main
merge_group:
# test PRs
pull_request:
# allow triggering tests, ignores skip check
@ -37,7 +38,7 @@ jobs:
persist-credentials: false
- run: sudo apt-get install libfl-dev
- name: Build
run: make vcxsrc YOSYS_VER=latest
run: make vcxsrc YOSYS_COMPILER="Visual Studio" VCX_DIR_NAME=yosys-win32-vcxsrc-latest
- uses: actions/upload-artifact@v4
with:
name: vcxsrc
@ -102,6 +103,7 @@ jobs:
ENABLE_ZLIB := 0
CXXFLAGS += -I$(pwd)/flex-prefix/include
LINKFLAGS += -Wl,-z,stack-size=8388608 -Wl,--stack-first -Wl,--strip-all
END
make -C build -f ../Makefile CXX=clang -j$(nproc)

View file

@ -1,6 +1,6 @@
name: Build docs artifact with Verific
on: [push, pull_request]
on: [push, pull_request, merge_group]
jobs:
check_docs_rebuild:

View file

@ -5,6 +5,7 @@ on:
push:
branches:
- main
merge_group:
# test PRs
pull_request:
# allow triggering tests, ignores skip check
@ -71,6 +72,7 @@ jobs:
cd build
make -f ../Makefile config-$CC
make -f ../Makefile -j$procs
make -f ../Makefile unit-test -j$procs
- name: Log yosys-config output
run: |
@ -80,7 +82,7 @@ jobs:
shell: bash
run: |
cd build
tar -cvf ../build.tar share/ yosys yosys-*
tar -cvf ../build.tar share/ yosys yosys-* libyosys.so
- name: Store build artifact
uses: actions/upload-artifact@v4
@ -130,7 +132,7 @@ jobs:
- name: Run tests
shell: bash
run: |
make -j$procs test TARGETS= EXTRA_TARGETS= CONFIG=$CC
make -j$procs vanilla-test TARGETS= EXTRA_TARGETS= CONFIG=$CC
- name: Report errors
if: ${{ failure() }}

View file

@ -5,6 +5,7 @@ on:
push:
branches:
- main
merge_group:
# test PRs
pull_request:
# allow triggering tests, ignores skip check

View file

@ -5,6 +5,7 @@ on:
push:
branches:
- main
merge_group:
# ignore PRs due to time needed
# allow triggering tests, ignores skip check
workflow_dispatch:
@ -64,7 +65,7 @@ jobs:
- name: Run tests
shell: bash
run: |
make -j$procs test TARGETS= EXTRA_TARGETS=
make -j$procs vanilla-test TARGETS= EXTRA_TARGETS=
- name: Report errors
if: ${{ failure() }}
@ -72,7 +73,3 @@ jobs:
run: |
find tests/**/*.err -print -exec cat {} \;
- name: Run unit tests
shell: bash
run: |
make -j$procs unit-test ENABLE_LIBYOSYS=1

109
.github/workflows/test-verific-cfg.yml vendored Normal file
View file

@ -0,0 +1,109 @@
name: Build various Verific configurations
on:
workflow_dispatch:
jobs:
test-verific-cfg:
if: github.repository_owner == 'YosysHQ'
runs-on: [self-hosted, linux, x64, fast]
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: true
- name: Runtime environment
run: |
echo "procs=$(nproc)" >> $GITHUB_ENV
- name: verific [SV]
run: |
make config-clang
echo "ENABLE_VERIFIC := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_SYSTEMVERILOG := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_VHDL := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_HIER_TREE := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_EDIF := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_LIBERTY := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 0" >> Makefile.conf
echo "ENABLE_CCACHE := 1" >> Makefile.conf
make -j$procs
- name: verific [VHDL]
run: |
make config-clang
echo "ENABLE_VERIFIC := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_SYSTEMVERILOG := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_VHDL := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_HIER_TREE := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_EDIF := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_LIBERTY := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 0" >> Makefile.conf
echo "ENABLE_CCACHE := 1" >> Makefile.conf
make -j$procs
- name: verific [SV + VHDL]
run: |
make config-clang
echo "ENABLE_VERIFIC := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_SYSTEMVERILOG := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_VHDL := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_HIER_TREE := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_EDIF := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_LIBERTY := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 0" >> Makefile.conf
echo "ENABLE_CCACHE := 1" >> Makefile.conf
make -j$procs
- name: verific [SV + HIER]
run: |
make config-clang
echo "ENABLE_VERIFIC := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_SYSTEMVERILOG := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_VHDL := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_HIER_TREE := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_EDIF := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_LIBERTY := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 0" >> Makefile.conf
echo "ENABLE_CCACHE := 1" >> Makefile.conf
make -j$procs
- name: verific [VHDL + HIER]
run: |
make config-clang
echo "ENABLE_VERIFIC := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_SYSTEMVERILOG := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_VHDL := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_HIER_TREE := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_EDIF := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_LIBERTY := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 0" >> Makefile.conf
echo "ENABLE_CCACHE := 1" >> Makefile.conf
make -j$procs
- name: verific [SV + VHDL + HIER]
run: |
make config-clang
echo "ENABLE_VERIFIC := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_SYSTEMVERILOG := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_VHDL := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_HIER_TREE := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_EDIF := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_LIBERTY := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 0" >> Makefile.conf
echo "ENABLE_CCACHE := 1" >> Makefile.conf
make -j$procs
- name: verific [SV + VHDL + HIER + EDIF + LIBERTY]
run: |
make config-clang
echo "ENABLE_VERIFIC := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_SYSTEMVERILOG := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_VHDL := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_HIER_TREE := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_EDIF := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_LIBERTY := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 0" >> Makefile.conf
echo "ENABLE_CCACHE := 1" >> Makefile.conf
make -j$procs

View file

@ -5,6 +5,7 @@ on:
push:
branches:
- main
merge_group:
# test PRs
pull_request:
# allow triggering tests, ignores skip check
@ -67,7 +68,7 @@ jobs:
- name: Run Yosys tests
run: |
make -j$procs test
make -j$procs vanilla-test
- name: Run Verific specific Yosys tests
run: |
@ -79,11 +80,6 @@ jobs:
run: |
make -C sby run_ci
- name: Run unit tests
shell: bash
run: |
make -j$procs unit-test ENABLE_LTO=1 ENABLE_LIBYOSYS=1
test-pyosys:
needs: pre-job
if: ${{ needs.pre-job.outputs.should_skip != 'true' && github.repository == 'YosysHQ/Yosys' }}

View file

@ -1,25 +0,0 @@
name: update-flake-lock
on:
workflow_dispatch: # allows manual triggering
schedule:
- cron: '0 0 * * 0' # runs weekly on Sunday at 00:00
jobs:
lockfile:
if: github.repository == 'YosysHQ/Yosys'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
- name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@main
with:
token: ${{CI_CREATE_PR_TOKEN}}
pr-title: "Update flake.lock" # Title of PR to be created
pr-labels: | # Labels to be set on the PR
dependencies
automated

View file

@ -1,34 +0,0 @@
name: Bump version
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * *'
jobs:
bump-version:
if: github.repository == 'YosysHQ/Yosys'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
persist-credentials: false
- name: Take last commit
id: log
run: echo "message=$(git log --no-merges -1 --oneline)" >> $GITHUB_OUTPUT
- name: Bump version
if: ${{ !contains(steps.log.outputs.message, 'Bump version') }}
run: |
make bumpversion
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add Makefile
git commit -m "Bump version"
- name: Push changes # push the output folder to your repo
if: ${{ !contains(steps.log.outputs.message, 'Bump version') }}
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}

View file

@ -2,9 +2,54 @@
List of major changes and improvements between releases
=======================================================
Yosys 0.59 .. Yosys 0.60-dev
Yosys 0.62 .. Yosys 0.63-dev
--------------------------
Yosys 0.61 .. Yosys 0.62
--------------------------
* Various
- verific: Added "-sv2017" flag option to support System
Verilog 2017.
- verific: Added VHDL related flags to "-f" and "-F" and
support reading VHDL file from file lists.
- Updated cell libs with proper module declaration where
non standard (...) style was used.
* New commands and options
- Added "-word" option to "lut2mux" pass to enable emitting
word level cells.
- Added experimental "opt_balance_tree" pass to convert
cascaded cells into tree of cells to improve timing.
- Added "-gatesi" option to "write_blif" pass to init gates
under gates_mode in BLIF format.
- Added "-on" and "-off" options to "debug" pass for
persistent debug logging.
- Added "linux_perf" pass to control performance recording.
Yosys 0.60 .. Yosys 0.61
--------------------------
* Various
- Removed "cover" pass for coverage tracking.
- Avoid merging formal properties with "opt_merge" pass.
- Parallelize "opt_merge" pass.
* New commands and options
- Added "design_equal" pass to support fuzz-test comparison.
- Added "lut2bmux" pass to convert $lut to $bmux.
- Added "-legalize" option to "read_rtlil" pass to prevent
semantic errors.
Yosys 0.59 .. Yosys 0.60
--------------------------
* Various
- read_verilog: suport unsized parameters.
- Added static library compile option.
* New commands and options
- Added "sdc" pass for reading SDC files.
- Added experimental "sdc_expand" and "opensta" for OpenSTA integration.
- Added "icell_liberty" pass for used internal cells.
Yosys 0.58 .. Yosys 0.59
--------------------------
* Various

View file

@ -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).

View file

@ -1,6 +1,6 @@
ISC License
Copyright (C) 2012 - 2025 Claire Xenia Wolf <claire@yosyshq.com>
Copyright (C) 2012 - 2026 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,58 +0,0 @@
ARG IMAGE="python:3-slim-buster"
#---
FROM $IMAGE AS base
RUN apt-get update -qq \
&& DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends \
ca-certificates \
clang \
lld \
curl \
libffi-dev \
libreadline-dev \
tcl-dev \
graphviz \
xdot \
&& apt-get autoclean && apt-get clean && apt-get -y autoremove \
&& update-ca-certificates \
&& rm -rf /var/lib/apt/lists
#---
FROM base AS build
RUN apt-get update -qq \
&& DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends \
bison \
flex \
gawk \
gcc \
git \
iverilog \
pkg-config \
&& apt-get autoclean && apt-get clean && apt-get -y autoremove \
&& rm -rf /var/lib/apt/lists
COPY . /yosys
ENV PREFIX /opt/yosys
RUN cd /yosys \
&& make \
&& make install \
&& make test
#---
FROM base
COPY --from=build /opt/yosys /opt/yosys
ENV PATH /opt/yosys/bin:$PATH
RUN useradd -m yosys
USER yosys
CMD ["yosys"]

108
Makefile
View file

@ -21,8 +21,8 @@ ENABLE_VERIFIC_HIER_TREE := 1
ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 0
ENABLE_VERIFIC_EDIF := 0
ENABLE_VERIFIC_LIBERTY := 0
ENABLE_COVER := 1
ENABLE_LIBYOSYS := 0
ENABLE_LIBYOSYS_STATIC := 0
ENABLE_ZLIB := 1
ENABLE_HELP_SOURCE := 0
@ -161,7 +161,19 @@ ifeq ($(OS), Haiku)
CXXFLAGS += -D_DEFAULT_SOURCE
endif
YOSYS_VER := 0.59+110
YOSYS_VER := 0.62
ifneq (, $(shell command -v git 2>/dev/null))
ifneq (, $(shell git rev-parse --git-dir 2>/dev/null))
GIT_COMMIT_COUNT := $(or $(shell git rev-list --count v$(YOSYS_VER)..HEAD 2>/dev/null),0)
ifneq ($(GIT_COMMIT_COUNT),0)
YOSYS_VER := $(YOSYS_VER)+$(GIT_COMMIT_COUNT)
endif
else
YOSYS_VER := $(YOSYS_VER)+post
endif
endif
YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1)
YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1)
YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2)
@ -177,15 +189,14 @@ CXXFLAGS += -DYOSYS_VER=\\"$(YOSYS_VER)\\" \
TARBALL_GIT_REV := $(shell cat $(YOSYS_SRC)/.gitcommit)
ifneq ($(findstring Format:,$(TARBALL_GIT_REV)),)
GIT_REV := $(shell GIT_DIR=$(YOSYS_SRC)/.git git rev-parse --short=9 HEAD || echo UNKNOWN)
GIT_DIRTY := $(shell GIT_DIR=$(YOSYS_SRC)/.git git diff --exit-code --quiet 2>/dev/null; if [ $$? -ne 0 ]; then echo "-dirty"; fi)
else
GIT_REV := $(TARBALL_GIT_REV)
GIT_DIRTY := ""
endif
OBJS = kernel/version_$(GIT_REV).o
bumpversion:
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 03eb220.. | wc -l`/;" Makefile
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q)
# set ABCEXTERNAL = <abc-command> to use an external ABC instance
@ -248,9 +259,6 @@ ifneq ($(SANITIZER),)
$(info [Clang Sanitizer] $(SANITIZER))
CXXFLAGS += -g -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=$(SANITIZER)
LINKFLAGS += -g -fsanitize=$(SANITIZER)
ifneq ($(findstring address,$(SANITIZER)),)
ENABLE_COVER := 0
endif
ifneq ($(findstring memory,$(SANITIZER)),)
CXXFLAGS += -fPIE -fsanitize-memory-track-origins
LINKFLAGS += -fPIE -fsanitize-memory-track-origins
@ -342,6 +350,9 @@ endif
ifeq ($(ENABLE_LIBYOSYS),1)
TARGETS += libyosys.so
ifeq ($(ENABLE_LIBYOSYS_STATIC),1)
TARGETS += libyosys.a
endif
endif
PY_WRAPPER_FILE = pyosys/wrappers
@ -474,6 +485,9 @@ else
ifeq ($(ABCEXTERNAL),)
TARGETS := $(PROGRAM_PREFIX)yosys-abc$(EXE) $(TARGETS)
endif
ifeq ($(DISABLE_SPAWN),1)
$(error ENABLE_ABC=1 requires either LINK_ABC=1 or DISABLE_SPAWN=0)
endif
endif
endif
@ -541,10 +555,6 @@ LIBS_VERIFIC += -Wl,--whole-archive $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VE
endif
endif
ifeq ($(ENABLE_COVER),1)
CXXFLAGS += -DYOSYS_ENABLE_COVER
endif
ifeq ($(ENABLE_CCACHE),1)
CXX := ccache $(CXX)
else
@ -638,6 +648,7 @@ $(eval $(call add_include_file,kernel/yosys_common.h))
$(eval $(call add_include_file,kernel/yw.h))
$(eval $(call add_include_file,libs/ezsat/ezsat.h))
$(eval $(call add_include_file,libs/ezsat/ezminisat.h))
$(eval $(call add_include_file,libs/ezsat/ezcmdline.h))
ifeq ($(ENABLE_ZLIB),1)
$(eval $(call add_include_file,libs/fst/fstapi.h))
endif
@ -645,8 +656,6 @@ $(eval $(call add_include_file,libs/sha1/sha1.h))
$(eval $(call add_include_file,libs/json11/json11.hpp))
$(eval $(call add_include_file,passes/fsm/fsmdata.h))
$(eval $(call add_include_file,passes/techmap/libparse.h))
$(eval $(call add_include_file,frontends/ast/ast.h))
$(eval $(call add_include_file,frontends/ast/ast_binding.h))
$(eval $(call add_include_file,frontends/blif/blifparse.h))
$(eval $(call add_include_file,backends/rtlil/rtlil_backend.h))
@ -685,6 +694,7 @@ OBJS += libs/json11/json11.o
OBJS += libs/ezsat/ezsat.o
OBJS += libs/ezsat/ezminisat.o
OBJS += libs/ezsat/ezcmdline.o
OBJS += libs/minisat/Options.o
OBJS += libs/minisat/SimpSolver.o
@ -723,7 +733,6 @@ OBJS += passes/hierarchy/hierarchy.o
OBJS += passes/cmds/select.o
OBJS += passes/cmds/show.o
OBJS += passes/cmds/stat.o
OBJS += passes/cmds/cover.o
OBJS += passes/cmds/design.o
OBJS += passes/cmds/plugin.o
@ -773,6 +782,9 @@ else
$(P) $(CXX) -o libyosys.so -shared -Wl,-soname,libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC)
endif
libyosys.a: $(filter-out kernel/driver.o,$(OBJS))
$(P) $(AR) rcs $@ $^
%.o: %.cc
$(Q) mkdir -p $(dir $@)
$(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $<
@ -791,12 +803,34 @@ endif
$(Q) mkdir -p $(dir $@)
$(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $<
YOSYS_VER_STR := Yosys $(YOSYS_VER) (git sha1 $(GIT_REV), $(notdir $(CXX)) $(shell \
$(CXX) --version | tr ' ()' '\n' | grep '^[0-9]' | head -n1) $(filter -f% -m% -O% -DNDEBUG,$(CXXFLAGS)))
YOSYS_REPO :=
ifneq (, $(shell command -v git 2>/dev/null))
ifneq (, $(shell git rev-parse --git-dir 2>/dev/null))
GIT_REMOTE := $(strip $(shell git config --get remote.origin.url 2>/dev/null | $(AWK) '{print tolower($$0)}'))
ifneq ($(strip $(GIT_REMOTE)),)
YOSYS_REPO := $(strip $(shell echo $(GIT_REMOTE) | $(AWK) -F '[:/]' '{gsub(/\.git$$/, "", $$NF); printf "%s/%s", $$(NF-1), $$NF}'))
endif
ifeq ($(strip $(YOSYS_REPO)),yosyshq/yosys)
YOSYS_REPO :=
endif
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)
ifeq ($(filter main HEAD release/v%,$(GIT_BRANCH)),)
YOSYS_REPO := $(YOSYS_REPO) at $(GIT_BRANCH)
endif
YOSYS_REPO := $(strip $(YOSYS_REPO))
endif
endif
YOSYS_GIT_STR := $(GIT_REV)$(GIT_DIRTY)
YOSYS_COMPILER := $(notdir $(CXX)) $(shell $(CXX) --version | tr ' ()' '\n' | grep '^[0-9]' | head -n1) $(filter -f% -m% -O% -DNDEBUG,$(CXXFLAGS))
YOSYS_VER_STR := Yosys $(YOSYS_VER) (git sha1 $(YOSYS_GIT_STR), $(YOSYS_COMPILER))
ifneq ($(strip $(YOSYS_REPO)),)
YOSYS_VER_STR := $(YOSYS_VER_STR) [$(YOSYS_REPO)]
endif
kernel/version_$(GIT_REV).cc: $(YOSYS_SRC)/Makefile
$(P) rm -f kernel/version_*.o kernel/version_*.d kernel/version_*.cc
$(Q) mkdir -p kernel && echo "namespace Yosys { extern const char *yosys_version_str; const char *yosys_version_str=\"$(YOSYS_VER_STR)\"; }" > kernel/version_$(GIT_REV).cc
$(Q) mkdir -p kernel && echo "namespace Yosys { extern const char *yosys_version_str; const char *yosys_version_str=\"$(YOSYS_VER_STR)\"; const char *yosys_git_hash_str=\"$(YOSYS_GIT_STR)\"; }" > kernel/version_$(GIT_REV).cc
ifeq ($(ENABLE_VERIFIC),1)
CXXFLAGS_NOVERIFIC = $(foreach v,$(CXXFLAGS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v)))
@ -976,9 +1010,11 @@ makefile-tests/%: %/run-test.mk $(TARGETS) $(EXTRA_TARGETS)
$(MAKE) -C $* -f run-test.mk
+@echo "...passed tests in $*"
test: makefile-tests abcopt-tests seed-tests
test: vanilla-test unit-test
vanilla-test: makefile-tests abcopt-tests seed-tests
@echo ""
@echo " Passed \"make test\"."
@echo " Passed \"make vanilla-test\"."
ifeq ($(ENABLE_VERIFIC),1)
ifeq ($(YOSYS_NOVERIFIC),1)
@echo " Ran tests without verific support due to YOSYS_NOVERIFIC=1."
@ -1010,11 +1046,11 @@ ystests: $(TARGETS) $(EXTRA_TARGETS)
# Unit test
unit-test: libyosys.so
@$(MAKE) -C $(UNITESTPATH) CXX="$(CXX)" CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" \
@$(MAKE) -f $(UNITESTPATH)/Makefile CXX="$(CXX)" CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" \
CXXFLAGS="$(CXXFLAGS)" LINKFLAGS="$(LINKFLAGS)" LIBS="$(LIBS)" ROOTPATH="$(CURDIR)"
clean-unit-test:
@$(MAKE) -C $(UNITESTPATH) clean
@$(MAKE) -f $(UNITESTPATH)/Makefile clean
install-dev: $(PROGRAM_PREFIX)yosys-config share
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR)
@ -1024,7 +1060,7 @@ install-dev: $(PROGRAM_PREFIX)yosys-config share
install: $(TARGETS) $(EXTRA_TARGETS)
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR)
$(INSTALL_SUDO) cp $(filter-out libyosys.so,$(TARGETS)) $(DESTDIR)$(BINDIR)
$(INSTALL_SUDO) cp $(filter-out libyosys.so libyosys.a,$(TARGETS)) $(DESTDIR)$(BINDIR)
ifneq ($(filter $(PROGRAM_PREFIX)yosys,$(TARGETS)),)
if [ -n "$(STRIP)" ]; then $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys; fi
endif
@ -1040,13 +1076,18 @@ ifeq ($(ENABLE_LIBYOSYS),1)
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(LIBDIR)
$(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR)/
if [ -n "$(STRIP)" ]; then $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(LIBDIR)/libyosys.so; fi
ifeq ($(ENABLE_LIBYOSYS_STATIC),1)
$(INSTALL_SUDO) cp libyosys.a $(DESTDIR)$(LIBDIR)/
endif
ifeq ($(ENABLE_PYOSYS),1)
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys
$(INSTALL_SUDO) cp $(YOSYS_SRC)/pyosys/__init__.py $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/__init__.py
$(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/libyosys.so
$(INSTALL_SUDO) cp -r share $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys
ifeq ($(ENABLE_ABC),1)
$(INSTALL_SUDO) cp yosys-abc $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/yosys-abc
ifeq ($(ABCEXTERNAL),)
$(INSTALL_SUDO) cp $(PROGRAM_PREFIX)yosys-abc$(EXE) $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/yosys-abc$(EXE)
endif
endif
endif
endif
@ -1062,6 +1103,9 @@ uninstall:
$(INSTALL_SUDO) rm -rvf $(DESTDIR)$(DATDIR)
ifeq ($(ENABLE_LIBYOSYS),1)
$(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.so
ifeq ($(ENABLE_LIBYOSYS_STATIC),1)
$(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.a
endif
ifeq ($(ENABLE_PYOSYS),1)
$(INSTALL_SUDO) rm -vf $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/libyosys.so
$(INSTALL_SUDO) rm -vf $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/__init__.py
@ -1160,7 +1204,7 @@ clean-py:
rm -f $(PY_WRAPPER_FILE).inc.cc $(PY_WRAPPER_FILE).cc
rm -f $(PYTHON_OBJECTS)
rm -f *.whl
rm -f libyosys.so
rm -f libyosys.so libyosys.a
rm -rf kernel/*.pyh
clean-abc:
@ -1194,15 +1238,17 @@ qtcreator:
{ echo .; find backends frontends kernel libs passes -type f \( -name '*.h' -o -name '*.hh' \) -printf '%h\n' | sort -u; } > qtcreator.includes
touch qtcreator.creator
vcxsrc: $(GENFILES) $(EXTRA_TARGETS)
rm -rf yosys-win32-vcxsrc-$(YOSYS_VER){,.zip}
VCX_DIR_NAME := yosys-win32-vcxsrc-$(YOSYS_VER)
vcxsrc: $(GENFILES) $(EXTRA_TARGETS) kernel/version_$(GIT_REV).cc
rm -rf $(VCX_DIR_NAME){,.zip}
cp -f kernel/version_$(GIT_REV).cc kernel/version.cc
set -e; for f in `ls $(filter %.cc %.cpp,$(GENFILES)) $(addsuffix .cc,$(basename $(OBJS))) $(addsuffix .cpp,$(basename $(OBJS))) 2> /dev/null`; do \
echo "Analyse: $$f" >&2; cpp -std=c++17 -MM -I. -D_YOSYS_ $$f; done | sed 's,.*:,,; s,//*,/,g; s,/[^/]*/\.\./,/,g; y, \\,\n\n,;' | grep '^[^/]' | sort -u | grep -v kernel/version_ > srcfiles.txt
echo "libs/fst/fst_win_unistd.h" >> srcfiles.txt
bash misc/create_vcxsrc.sh yosys-win32-vcxsrc $(YOSYS_VER) $(GIT_REV)
echo "namespace Yosys { extern const char *yosys_version_str; const char *yosys_version_str=\"Yosys (Version Information Unavailable)\"; }" > kernel/version.cc
zip yosys-win32-vcxsrc-$(YOSYS_VER)/genfiles.zip $(GENFILES) kernel/version.cc
zip -r yosys-win32-vcxsrc-$(YOSYS_VER).zip yosys-win32-vcxsrc-$(YOSYS_VER)/
echo "kernel/version.cc" >> srcfiles.txt
bash misc/create_vcxsrc.sh $(VCX_DIR_NAME) $(YOSYS_VER)
zip $(VCX_DIR_NAME)/genfiles.zip $(GENFILES) kernel/version.cc
zip -r $(VCX_DIR_NAME).zip $(VCX_DIR_NAME)/
rm -f srcfiles.txt kernel/version.cc
config-clean: clean

View file

@ -114,8 +114,8 @@ To build Yosys simply type 'make' in this directory.
$ sudo make install
Tests are located in the tests subdirectory and can be executed using the test
target. Note that you need gawk as well as a recent version of iverilog (i.e.
build from git). Then, execute tests via:
target. Note that you need gawk, a recent version of iverilog, and gtest.
Execute tests via:
$ make test
@ -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

@ -1 +1 @@
Subproject commit 1c5ed1ce378cc04beac30bb31abc4c37c8467042
Subproject commit c18b835ef140217c84a26ba510f98f69d54dd48e

View file

@ -24,6 +24,7 @@
#include "kernel/register.h"
#include "kernel/celltypes.h"
#include "kernel/rtlil.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@ -847,11 +848,14 @@ struct XAigerAnalysis : Index<XAigerAnalysis, int, 0, 0> {
return false;
int max = 1;
for (auto wire : mod->wires())
if (wire->port_input && !wire->port_output)
for (int i = 0; i < wire->width; i++) {
int ilevel = visit(cursor, driver->getPort(wire->name)[i]);
max = std::max(max, ilevel + 1);
for (auto wire : mod->wires()) {
if (wire->port_input && !wire->port_output) {
SigSpec port = driver->getPort(wire->name);
for (int i = 0; i < std::min(wire->width, port.size()); i++) {
int ilevel = visit(cursor, port[i]);
max = std::max(max, ilevel + 1);
}
}
}
lits[idx] = max;

View file

@ -44,6 +44,7 @@ struct BlifDumperConfig
bool iattr_mode;
bool blackbox_mode;
bool noalias_mode;
bool gatesi_mode;
std::string buf_type, buf_in, buf_out;
std::map<RTLIL::IdString, std::pair<RTLIL::IdString, RTLIL::IdString>> unbuf_types;
@ -51,7 +52,7 @@ struct BlifDumperConfig
BlifDumperConfig() : icells_mode(false), conn_mode(false), impltf_mode(false), gates_mode(false),
cname_mode(false), iname_mode(false), param_mode(false), attr_mode(false), iattr_mode(false),
blackbox_mode(false), noalias_mode(false) { }
blackbox_mode(false), noalias_mode(false), gatesi_mode(false) { }
};
struct BlifDumper
@ -118,16 +119,21 @@ struct BlifDumper
return str;
}
const std::string str_init(RTLIL::SigBit sig)
template <bool Space = true> const std::string str_init(RTLIL::SigBit sig)
{
sigmap.apply(sig);
if (init_bits.count(sig) == 0)
return " 2";
if (init_bits.count(sig) == 0) {
if constexpr (Space)
return " 2";
else
return "2";
}
string str = stringf(" %d", init_bits.at(sig));
return str;
if constexpr (Space)
return stringf(" %d", init_bits.at(sig));
else
return stringf("%d", init_bits.at(sig));
}
const char *subckt_or_gate(std::string cell_type)
@ -469,6 +475,11 @@ struct BlifDumper
f << stringf(".names %s %s\n1 1\n", str(rhs_bit), str(lhs_bit));
}
if (config->gatesi_mode) {
for (auto &&init_bit : init_bits)
f << stringf(".gateinit %s=%s\n", str(init_bit.first), str_init<false>(init_bit.first));
}
f << stringf(".end\n");
}
@ -550,6 +561,9 @@ struct BlifBackend : public Backend {
log(" -impltf\n");
log(" do not write definitions for the $true, $false and $undef wires.\n");
log("\n");
log(" -gatesi\n");
log(" write initial bit(s) with .gateinit for gates that needs to be initialized.\n");
log("\n");
}
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
@ -640,6 +654,10 @@ struct BlifBackend : public Backend {
config.noalias_mode = true;
continue;
}
if (args[argidx] == "-gatesi") {
config.gatesi_mode = true;
continue;
}
break;
}
extra_args(f, filename, args, argidx);

View file

@ -756,7 +756,7 @@ struct CxxrtlWorker {
// 1b. Generated identifiers for internal names (beginning with `$`) start with `i_`.
// 2. An underscore is escaped with another underscore, i.e. `__`.
// 3. Any other non-alnum character is escaped with underscores around its lowercase hex code, e.g. `@` as `_40_`.
std::string mangle_name(const RTLIL::IdString &name)
std::string mangle_name(RTLIL::IdString name)
{
std::string mangled;
bool first = true;
@ -786,7 +786,7 @@ struct CxxrtlWorker {
return mangled;
}
std::string mangle_module_name(const RTLIL::IdString &name, bool is_blackbox = false)
std::string mangle_module_name(RTLIL::IdString name, bool is_blackbox = false)
{
// Class namespace.
if (is_blackbox)
@ -794,19 +794,19 @@ struct CxxrtlWorker {
return mangle_name(name);
}
std::string mangle_memory_name(const RTLIL::IdString &name)
std::string mangle_memory_name(RTLIL::IdString name)
{
// Class member namespace.
return "memory_" + mangle_name(name);
}
std::string mangle_cell_name(const RTLIL::IdString &name)
std::string mangle_cell_name(RTLIL::IdString name)
{
// Class member namespace.
return "cell_" + mangle_name(name);
}
std::string mangle_wire_name(const RTLIL::IdString &name)
std::string mangle_wire_name(RTLIL::IdString name)
{
// Class member namespace.
return mangle_name(name);

View file

@ -188,20 +188,27 @@ struct SmtrModule {
Functional::IR ir;
SmtrScope scope;
std::string name;
bool use_assoc_list_helpers;
std::optional<std::string> input_helper_name;
std::optional<std::string> output_helper_name;
SmtrStruct input_struct;
SmtrStruct output_struct;
SmtrStruct state_struct;
SmtrModule(Module *module)
: ir(Functional::IR::from_module(module))
, scope()
, name(scope.unique_name(module->name))
, input_struct(scope.unique_name(module->name.str() + "_Inputs"), scope)
, output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope)
, state_struct(scope.unique_name(module->name.str() + "_State"), scope)
SmtrModule(Module *module, bool assoc_list_helpers)
: ir(Functional::IR::from_module(module)), scope(), name(scope.unique_name(module->name)), use_assoc_list_helpers(assoc_list_helpers),
input_struct(scope.unique_name(module->name.str() + "_Inputs"), scope),
output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope),
state_struct(scope.unique_name(module->name.str() + "_State"), scope)
{
scope.reserve(name + "_initial");
if (assoc_list_helpers) {
input_helper_name = scope.unique_name(module->name.str() + "_inputs_helper");
scope.reserve(*input_helper_name);
output_helper_name = scope.unique_name(module->name.str() + "_outputs_helper");
scope.reserve(*output_helper_name);
}
for (auto input : ir.inputs())
input_struct.insert(input->name, input->sort);
for (auto output : ir.outputs())
@ -257,6 +264,45 @@ struct SmtrModule {
w.pop();
}
void write_assoc_list_helpers(SExprWriter &w)
{
log_assert(output_helper_name && input_helper_name);
// Input struct keyword-based constructor.
w.push();
w.open(list("define"));
const auto inputs_name = "inputs";
w.open(list(*input_helper_name, inputs_name));
w.close();
w.open(list(input_struct.name));
for (auto input : ir.inputs()) {
w.push();
w.open(list("let"));
w.push();
w.open(list());
w.open(list("assoc-result"));
w << list("assoc", "\"" + RTLIL::unescape_id(input->name) + "\"", inputs_name);
w.pop();
w.open(list("if", "assoc-result"));
w << list("cdr", "assoc-result");
w.open(list("begin"));
w << list("fprintf", list("current-error-port"), "\"%s not found in inputs\"");
w << "'not-found";
w.pop();
}
w.pop();
// Output struct keyword-based destructuring
w.push();
w.open(list("define"));
const auto outputs_name = "outputs";
w << list(*output_helper_name, outputs_name);
w.open(list("list"));
for (auto output : ir.outputs()) {
w << list("cons", "\"" + RTLIL::unescape_id(output->name) + "\"", output_struct.access("outputs", output->name));
}
w.pop();
}
void write(std::ostream &out)
{
SExprWriter w(out);
@ -265,6 +311,10 @@ struct SmtrModule {
output_struct.write_definition(w);
state_struct.write_definition(w);
if (use_assoc_list_helpers) {
write_assoc_list_helpers(w);
}
write_eval(w);
write_initial(w);
}
@ -282,12 +332,16 @@ struct FunctionalSmtrBackend : public Backend {
log("\n");
log(" -provides\n");
log(" include 'provide' statement(s) for loading output as a module\n");
log(" -assoc-list-helpers\n");
log(" provide helper functions which convert inputs/outputs from/to association lists\n");
log(" \n");
log("\n");
}
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
auto provides = false;
auto assoc_list_helpers = false;
log_header(design, "Executing Functional Rosette Backend.\n");
@ -296,6 +350,8 @@ struct FunctionalSmtrBackend : public Backend {
{
if (args[argidx] == "-provides")
provides = true;
else if (args[argidx] == "-assoc-list-helpers")
assoc_list_helpers = true;
else
break;
}
@ -307,8 +363,8 @@ struct FunctionalSmtrBackend : public Backend {
}
for (auto module : design->selected_modules()) {
log("Processing module `%s`.\n", module->name);
SmtrModule smtr(module);
log("Processing module `%s`.\n", module->name.c_str());
SmtrModule smtr(module, assoc_list_helpers);
smtr.write(*f);
}
}

View file

@ -735,6 +735,12 @@ def ywfile_signal(sig, step, mask=None):
output = []
def ywfile_signal_error(reason, detail=None):
msg = f"Yosys witness signal mismatch for {sig.pretty()}: {reason}"
if detail:
msg += f" ({detail})"
raise ValueError(msg)
if sig.path in smt_wires:
for wire in smt_wires[sig.path]:
width, offset = wire["width"], wire["offset"]
@ -765,6 +771,12 @@ def ywfile_signal(sig, step, mask=None):
for mem in smt_mems[sig.memory_path]:
width, size, bv = mem["width"], mem["size"], mem["statebv"]
if sig.memory_addr is not None and sig.memory_addr >= size:
ywfile_signal_error(
"memory address out of bounds",
f"address={sig.memory_addr} size={size}",
)
smt_expr = smt.net_expr(topmod, f"s{step}", mem["smtpath"])
if bv:
@ -781,18 +793,34 @@ def ywfile_signal(sig, step, mask=None):
smt_expr = "((_ extract %d %d) %s)" % (slice_high, sig.offset, smt_expr)
output.append((0, sig.width, smt_expr))
else:
ywfile_signal_error("memory not found in design")
output.sort()
output = [chunk for chunk in output if chunk[0] != chunk[1]]
if not output:
if sig.memory_path:
ywfile_signal_error("memory signal has no matching bits in design")
else:
ywfile_signal_error("signal not found in design")
pos = 0
for start, end, smt_expr in output:
assert start == pos
if start != pos:
ywfile_signal_error(
"signal width/offset mismatch",
f"expected coverage at bit {pos}",
)
pos = end
assert pos == sig.width
if pos != sig.width:
ywfile_signal_error(
"signal width/offset mismatch",
f"covered {pos} of {sig.width} bits",
)
if len(output) == 1:
return output[0][-1]

View file

@ -95,7 +95,8 @@ bool VERILOG_BACKEND::id_is_verilog_escaped(const std::string &str) {
PRIVATE_NAMESPACE_BEGIN
bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit, systemverilog, simple_lhs, noparallelcase;
bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit, systemverilog, simple_lhs,
noparallelcase, default_params;
int auto_name_counter, auto_name_offset, auto_name_digits, extmem_counter;
dict<RTLIL::IdString, int> auto_name_map;
std::set<RTLIL::IdString> reg_wires;
@ -421,6 +422,13 @@ void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString,
}
}
void dump_parameter(std::ostream &f, std::string indent, RTLIL::IdString id_string, RTLIL::Const parameter)
{
f << stringf("%sparameter %s = ", indent.c_str(), id(id_string).c_str());
dump_const(f, parameter);
f << ";\n";
}
void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire)
{
dump_attributes(f, indent, wire->attributes, "\n", /*modattr=*/false, /*regattr=*/reg_wires.count(wire->name));
@ -2143,6 +2151,9 @@ void dump_case_actions(std::ostream &f, std::string indent, RTLIL::CaseRule *cs)
bool dump_proc_switch_ifelse(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw)
{
if (sw->cases.empty())
return true;
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) {
if ((*it)->compare.size() == 0) {
break;
@ -2435,6 +2446,10 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
f << indent + " " << "reg " << id(initial_id) << " = 0;\n";
}
if (default_params)
for (auto p : module->parameter_default_values)
dump_parameter(f, indent + " ", p.first, p.second);
// first dump input / output according to their order in module->ports
for (auto port : module->ports)
dump_wire(f, indent + " ", module->wire(port));
@ -2542,6 +2557,10 @@ struct VerilogBackend : public Backend {
log(" use 'defparam' statements instead of the Verilog-2001 syntax for\n");
log(" cell parameters.\n");
log("\n");
log(" -default_params\n");
log(" emit module parameter declarations from\n");
log(" parameter_default_values.\n");
log("\n");
log(" -blackboxes\n");
log(" usually modules with the 'blackbox' attribute are ignored. with\n");
log(" this option set only the modules with the 'blackbox' attribute\n");
@ -2579,6 +2598,7 @@ struct VerilogBackend : public Backend {
siminit = false;
simple_lhs = false;
noparallelcase = false;
default_params = false;
auto_prefix = "";
bool blackboxes = false;
@ -2639,6 +2659,10 @@ struct VerilogBackend : public Backend {
defparam = true;
continue;
}
if (arg == "-defaultparams") {
default_params = true;
continue;
}
if (arg == "-decimal") {
decimal = true;
continue;

View file

@ -17,6 +17,7 @@ coarse:
opt_clean
memory_collect
opt -noff -keepdc -fast
sort
check:
stat

View file

@ -5,8 +5,8 @@ import os
project = 'YosysHQ Yosys'
author = 'YosysHQ GmbH'
copyright ='2025 YosysHQ GmbH'
yosys_ver = "0.59"
copyright ='2026 YosysHQ GmbH'
yosys_ver = "0.62"
# select HTML theme
html_theme = 'furo-ys'

View file

@ -355,6 +355,9 @@ from SystemVerilog:
design with `read_verilog`, all its packages are available to SystemVerilog
files being read into the same design afterwards.
- nested packages are currently not supported (i.e. calling ``import`` inside
a ``package`` .. ``endpackage`` block)
- typedefs are supported (including inside packages)
- type casts are currently not supported

View file

@ -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.

View file

@ -8,7 +8,44 @@ Running the included test suite
The Yosys source comes with a test suite to avoid regressions and keep
everything working as expected. Tests can be run by calling ``make test`` from
the root Yosys directory.
the root Yosys directory. By default, this runs vanilla and unit tests.
Vanilla tests
~~~~~~~~~~~~~
These make up the majority of our testing coverage.
They can be run with ``make vanilla-test`` and are based on calls to
make subcommands (``make makefile-tests``) and shell scripts
(``make seed-tests`` and ``make abcopt-tests``). Both use ``run-test.sh``
files, but make-based tests only call ``tests/gen-tests-makefile.sh``
to generate a makefile appropriate for the given directory, so only
afterwards when make is invoked do the tests actually run.
Usually their structure looks something like this:
you write a .ys file that gets automatically run,
which runs a frontend like ``read_verilog`` or ``read_rtlil`` with
a relative path or a heredoc, then runs some commands including the command
under test, and then uses :doc:`/using_yosys/more_scripting/selections`
with ``-assert-count``. Usually it's unnecessary to "register" the test anywhere
as if it's being added to an existing directory, depending
on how the ``run-test.sh`` in that directory works.
Unit tests
~~~~~~~~~~
Running the unit tests requires the following additional packages:
.. tab:: Ubuntu
.. code:: console
sudo apt-get install libgtest-dev
.. tab:: macOS
No additional requirements.
Unit tests can be run with ``make unit-test``.
Functional tests
~~~~~~~~~~~~~~~~
@ -41,23 +78,6 @@ instructions <https://github.com/Z3Prover/z3>`_.
Then, set the :makevar:`ENABLE_FUNCTIONAL_TESTS` make variable when calling
``make test`` and the functional tests will be run as well.
Unit tests
~~~~~~~~~~
Running the unit tests requires the following additional packages:
.. tab:: Ubuntu
.. code:: console
sudo apt-get install libgtest-dev
.. tab:: macOS
No additional requirements.
Unit tests can be run with ``make unit-test``.
Docs tests
~~~~~~~~~~

View file

@ -286,10 +286,15 @@ end_of_header:
RTLIL::IdString escaped_s = stringf("\\%s", s);
RTLIL::Wire* wire;
if (c == 'i') wire = inputs[l1];
else if (c == 'l') wire = latches[l1];
else if (c == 'o') {
if (c == 'i') {
log_assert(l1 < inputs.size());
wire = inputs[l1];
} else if (c == 'l') {
log_assert(l1 < latches.size());
wire = latches[l1];
} else if (c == 'o') {
wire = module->wire(escaped_s);
log_assert(l1 < outputs.size());
if (wire) {
// Could have been renamed by a latch
module->swap_names(wire, outputs[l1]);
@ -297,9 +302,9 @@ end_of_header:
goto next;
}
wire = outputs[l1];
}
else if (c == 'b') wire = bad_properties[l1];
else log_abort();
} else if (c == 'b') {
wire = bad_properties[l1];
} else log_abort();
module->rename(wire, escaped_s);
}
@ -652,6 +657,9 @@ void AigerReader::parse_aiger_binary()
unsigned l1, l2, l3;
std::string line;
if (M != I + L + A)
log_error("Binary AIGER input is malformed: maximum variable index M is %u, but number of inputs, latches and AND gates adds up to %u.\n", M, I + L + A);
// Parse inputs
int digits = decimal_digits(I);
for (unsigned i = 1; i <= I; ++i) {

View file

@ -110,7 +110,8 @@ struct Xaiger2Frontend : public Frontend {
for (int i = 0; i < (int) O; i++) {
int po;
*f >> po;
log_assert(f->get() == '\n');
int c = f->get();
log_assert(c == '\n');
outputs.push_back(po);
}

View file

@ -844,6 +844,7 @@ struct AST_INTERNAL::ProcessGenerator
RTLIL::Cell *cell = current_module->addCell(cellname, ID($check));
set_src_attr(cell, ast);
cell->set_bool_attribute(ID(keep));
for (auto &attr : ast->attributes) {
if (attr.second->type != AST_CONSTANT)
log_file_error(*ast->location.begin.filename, ast->location.begin.line, "Attribute `%s' with non-constant value!\n", attr.first);
@ -2084,8 +2085,6 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
check_unique_id(current_module, id, this, "cell");
RTLIL::Cell *cell = current_module->addCell(id, "");
set_src_attr(cell, this);
// Set attribute 'module_not_derived' which will be cleared again after the hierarchy pass
cell->set_bool_attribute(ID::module_not_derived);
for (auto it = children.begin(); it != children.end(); it++) {
auto* child = it->get();
@ -2148,6 +2147,11 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
}
log_abort();
}
// Set attribute 'module_not_derived' which will be cleared again after the hierarchy pass
if (cell->type.isPublic())
cell->set_bool_attribute(ID::module_not_derived);
for (auto &attr : attributes) {
if (attr.second->type != AST_CONSTANT)
input_error("Attribute `%s' with non-constant value.\n", attr.first);

View file

@ -879,7 +879,7 @@ static void check_auto_nosync(AstNode *node)
}
// remove the attributes we've "consumed"
for (const RTLIL::IdString &str : attrs_to_drop) {
for (RTLIL::IdString str : attrs_to_drop) {
auto it = node->attributes.find(str);
node->attributes.erase(it);
}
@ -4690,6 +4690,7 @@ void AstNode::expand_genblock(const std::string &prefix)
switch (child->type) {
case AST_WIRE:
case AST_AUTOWIRE:
case AST_MEMORY:
case AST_STRUCT:
case AST_UNION:
@ -4718,6 +4719,93 @@ void AstNode::expand_genblock(const std::string &prefix)
}
break;
case AST_IDENTIFIER:
if (!child->str.empty() && prefix.size() > 0) {
bool is_resolved = false;
std::string identifier_str = child->str;
if (current_ast_mod != nullptr && identifier_str.compare(0, current_ast_mod->str.size(), current_ast_mod->str) == 0) {
if (identifier_str.at(current_ast_mod->str.size()) == '.') {
identifier_str = '\\' + identifier_str.substr(current_ast_mod->str.size()+1, identifier_str.size());
}
}
// search starting in the innermost scope and then stepping outward
for (size_t ppos = prefix.size() - 1; ppos; --ppos) {
if (prefix.at(ppos) != '.') continue;
std::string new_prefix = prefix.substr(0, ppos + 1);
auto attempt_resolve = [&new_prefix](const std::string &ident) -> std::string {
std::string new_name = prefix_id(new_prefix, ident);
if (current_scope.count(new_name))
return new_name;
return {};
};
// attempt to resolve the full identifier
std::string resolved = attempt_resolve(identifier_str);
if (!resolved.empty()) {
is_resolved = true;
break;
}
// attempt to resolve hierarchical prefixes within the identifier,
// as the prefix could refer to a local scope which exists but
// hasn't yet been elaborated
for (size_t spos = identifier_str.size() - 1; spos; --spos) {
if (identifier_str.at(spos) != '.') continue;
resolved = attempt_resolve(identifier_str.substr(0, spos));
if (!resolved.empty()) {
is_resolved = true;
identifier_str = resolved + identifier_str.substr(spos);
ppos = 1; // break outer loop
break;
}
}
if (current_scope.count(identifier_str) == 0) {
AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod;
for (auto& node : current_scope_ast->children) {
switch (node->type) {
case AST_PARAMETER:
case AST_LOCALPARAM:
case AST_WIRE:
case AST_AUTOWIRE:
case AST_GENVAR:
case AST_MEMORY:
case AST_FUNCTION:
case AST_TASK:
case AST_DPI_FUNCTION:
if (prefix_id(new_prefix, identifier_str) == node->str) {
is_resolved = true;
current_scope[node->str] = node.get();
}
break;
case AST_ENUM:
current_scope[node->str] = node.get();
for (auto& enum_node : node->children) {
log_assert(enum_node->type==AST_ENUM_ITEM);
if (prefix_id(new_prefix, identifier_str) == enum_node->str) {
is_resolved = true;
current_scope[enum_node->str] = enum_node.get();
}
}
break;
default:
break;
}
}
}
}
if ((current_scope.count(identifier_str) == 0) && is_resolved == false) {
if (current_ast_mod == nullptr) {
input_error("Identifier `%s' is implicitly declared outside of a module.\n", child->str.c_str());
} else if (flag_autowire || identifier_str == "\\$global_clock") {
auto auto_wire = std::make_unique<AstNode>(child->location, AST_AUTOWIRE);
auto_wire->str = identifier_str;
children.push_back(std::move(auto_wire));
} else {
input_error("Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", identifier_str.c_str());
}
}
}
break;
default:
break;
}

View file

@ -245,7 +245,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
if (undef_wire != nullptr)
module->rename(undef_wire, stringf("$undef$%d", ++blif_maxnum));
autoidx = std::max(autoidx, blif_maxnum+1);
autoidx.ensure_at_least(blif_maxnum+1);
blif_maxnum = 0;
}
@ -470,6 +470,27 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
continue;
}
if (!strcmp(cmd, ".gateinit"))
{
char *p = strtok(NULL, " \t\r\n");
if (p == NULL)
goto error;
char *n = strtok(p, "=");
char *init = strtok(NULL, "=");
if (n == NULL || init == NULL)
goto error;
if (init[0] != '0' && init[0] != '1')
goto error;
if (blif_wire(n)->attributes.find(ID::init) == blif_wire(n)->attributes.end())
blif_wire(n)->attributes.emplace(ID::init, Const(init[0] == '1' ? 1 : 0, 1));
else
blif_wire(n)->attributes[ID::init] = Const(init[0] == '1' ? 1 : 0, 1);
continue;
}
if (!strcmp(cmd, ".names"))
{
char *p;
@ -608,6 +629,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
goto try_next_value;
}
}
log_assert(i < lutptr->size());
lutptr->set(i, !strcmp(output, "0") ? RTLIL::State::S0 : RTLIL::State::S1);
try_next_value:;
}

View file

@ -302,6 +302,9 @@ void json_import(Design *design, string &modname, JsonNode *node)
if (node->data_dict.count("attributes"))
json_parse_attr_param(module->attributes, node->data_dict.at("attributes"));
if (node->data_dict.count("parameter_default_values"))
json_parse_attr_param(module->parameter_default_values, node->data_dict.at("parameter_default_values"));
dict<int, SigBit> signal_bits;
if (node->data_dict.count("ports"))

View file

@ -40,14 +40,14 @@ static RTLIL::SigSpec parse_func_identifier(RTLIL::Module *module, const char *&
expr[id_len] == '_' || expr[id_len] == '[' || expr[id_len] == ']') id_len++;
if (id_len == 0)
log_error("Expected identifier at `%s'.\n", expr);
log_error("Expected identifier at `%s' in %s.\n", expr, RTLIL::unescape_id(module->name));
if (id_len == 1 && (*expr == '0' || *expr == '1'))
return *(expr++) == '0' ? RTLIL::State::S0 : RTLIL::State::S1;
std::string id = RTLIL::escape_id(std::string(expr, id_len));
if (!module->wires_.count(id))
log_error("Can't resolve wire name %s.\n", RTLIL::unescape_id(id));
log_error("Can't resolve wire name %s in %s.\n", RTLIL::unescape_id(id), RTLIL::unescape_id(module->name));
expr += id_len;
return module->wires_.at(id);
@ -174,7 +174,7 @@ static RTLIL::SigSpec parse_func_expr(RTLIL::Module *module, const char *expr)
#endif
if (stack.size() != 1 || stack.back().type != 3)
log_error("Parser error in function expr `%s'.\n", orig_expr);
log_error("Parser error in function expr `%s'in %s.\n", orig_expr, RTLIL::unescape_id(module->name));
return stack.back().sig;
}
@ -191,11 +191,23 @@ static RTLIL::SigSpec create_tristate(RTLIL::Module *module, RTLIL::SigSpec func
return cell->getPort(ID::Y);
}
static void create_latch_ff_wires(RTLIL::Module *module, const LibertyAst *node)
{
module->addWire(RTLIL::escape_id(node->args.at(0)));
module->addWire(RTLIL::escape_id(node->args.at(1)));
}
static std::pair<RTLIL::SigSpec, RTLIL::SigSpec> find_latch_ff_wires(RTLIL::Module *module, const LibertyAst *node)
{
auto* iq_wire = module->wire(RTLIL::escape_id(node->args.at(0)));
auto* iqn_wire = module->wire(RTLIL::escape_id(node->args.at(1)));
log_assert(iq_wire && iqn_wire);
return std::make_pair(iq_wire, iqn_wire);
}
static void create_ff(RTLIL::Module *module, const LibertyAst *node)
{
RTLIL::SigSpec iq_sig(module->addWire(RTLIL::escape_id(node->args.at(0))));
RTLIL::SigSpec iqn_sig(module->addWire(RTLIL::escape_id(node->args.at(1))));
auto [iq_sig, iqn_sig] = find_latch_ff_wires(module, node);
RTLIL::SigSpec clk_sig, data_sig, clear_sig, preset_sig;
bool clk_polarity = true, clear_polarity = true, preset_polarity = true;
@ -211,7 +223,7 @@ static void create_ff(RTLIL::Module *module, const LibertyAst *node)
}
if (clk_sig.size() == 0 || data_sig.size() == 0)
log_error("FF cell %s has no next_state and/or clocked_on attribute.\n", log_id(module->name));
log_error("FF cell %s has no next_state and/or clocked_on attribute.\n", RTLIL::unescape_id(module->name));
for (bool rerun_invert_rollback = true; rerun_invert_rollback;)
{
@ -270,9 +282,7 @@ static void create_ff(RTLIL::Module *module, const LibertyAst *node)
static bool create_latch(RTLIL::Module *module, const LibertyAst *node, bool flag_ignore_miss_data_latch)
{
RTLIL::SigSpec iq_sig(module->addWire(RTLIL::escape_id(node->args.at(0))));
RTLIL::SigSpec iqn_sig(module->addWire(RTLIL::escape_id(node->args.at(1))));
auto [iq_sig, iqn_sig] = find_latch_ff_wires(module, node);
RTLIL::SigSpec enable_sig, data_sig, clear_sig, preset_sig;
bool enable_polarity = true, clear_polarity = true, preset_polarity = true;
@ -289,9 +299,9 @@ static bool create_latch(RTLIL::Module *module, const LibertyAst *node, bool fla
if (enable_sig.size() == 0 || data_sig.size() == 0) {
if (!flag_ignore_miss_data_latch)
log_error("Latch cell %s has no data_in and/or enable attribute.\n", log_id(module->name));
log_error("Latch cell %s has no data_in and/or enable attribute.\n", RTLIL::unescape_id(module->name));
else
log("Ignored latch cell %s with no data_in and/or enable attribute.\n", log_id(module->name));
log("Ignored latch cell %s with no data_in and/or enable attribute.\n", RTLIL::unescape_id(module->name));
return false;
}
@ -582,9 +592,9 @@ struct LibertyFrontend : public Frontend {
{
if (!flag_ignore_miss_dir)
{
log_error("Missing or invalid direction for pin %s on cell %s.\n", node->args.at(0), log_id(module->name));
log_error("Missing or invalid direction for pin %s on cell %s.\n", node->args.at(0), RTLIL::unescape_id(module->name));
} else {
log("Ignoring cell %s with missing or invalid direction for pin %s.\n", log_id(module->name), node->args.at(0));
log("Ignoring cell %s with missing or invalid direction for pin %s.\n", RTLIL::unescape_id(module->name), node->args.at(0));
delete module;
goto skip_cell;
}
@ -596,13 +606,13 @@ struct LibertyFrontend : public Frontend {
if (node->id == "bus" && node->args.size() == 1)
{
if (flag_ignore_buses) {
log("Ignoring cell %s with a bus interface %s.\n", log_id(module->name), node->args.at(0));
log("Ignoring cell %s with a bus interface %s.\n", RTLIL::unescape_id(module->name), node->args.at(0));
delete module;
goto skip_cell;
}
if (!flag_lib)
log_error("Error in cell %s: bus interfaces are only supported in -lib mode.\n", log_id(cell_name));
log_error("Error in cell %s: bus interfaces are only supported in -lib mode.\n", RTLIL::unescape_id(cell_name));
const LibertyAst *dir = node->find("direction");
@ -613,7 +623,7 @@ struct LibertyFrontend : public Frontend {
}
if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal"))
log_error("Missing or invalid direction for bus %s on cell %s.\n", node->args.at(0), log_id(module->name));
log_error("Missing or invalid direction for bus %s on cell %s.\n", node->args.at(0), RTLIL::unescape_id(module->name));
simple_comb_cell = false;
@ -624,7 +634,7 @@ struct LibertyFrontend : public Frontend {
if (!bus_type_node || !type_map.count(bus_type_node->value))
log_error("Unknown or unsupported type for bus interface %s on cell %s.\n",
node->args.at(0).c_str(), log_id(cell_name));
node->args.at(0).c_str(), RTLIL::unescape_id(cell_name));
int bus_type_width = std::get<0>(type_map.at(bus_type_node->value));
int bus_type_offset = std::get<1>(type_map.at(bus_type_node->value));
@ -646,6 +656,13 @@ struct LibertyFrontend : public Frontend {
{
// some liberty files do not put ff/latch at the beginning of a cell
// try to find "ff" or "latch" and create FF/latch _before_ processing all other nodes
// but first, in case of balloon retention cells, we need all ff/latch output wires
// defined before we add ff/latch cells
for (auto node : cell->children)
{
if ((node->id == "ff" && node->args.size() == 2) || (node->id == "latch" && node->args.size() == 2))
create_latch_ff_wires(module, node);
}
for (auto node : cell->children)
{
if (node->id == "ff" && node->args.size() == 2)
@ -701,9 +718,9 @@ struct LibertyFrontend : public Frontend {
if (dir->value != "inout") { // allow inout with missing function, can be used for power pins
if (!flag_ignore_miss_func)
{
log_error("Missing function on output %s of cell %s.\n", log_id(wire->name), log_id(module->name));
log_error("Missing function on output %s of cell %s.\n", RTLIL::unescape_id(wire->name), RTLIL::unescape_id(module->name));
} else {
log("Ignoring cell %s with missing function on output %s.\n", log_id(module->name), log_id(wire->name));
log("Ignoring cell %s with missing function on output %s.\n", RTLIL::unescape_id(module->name), RTLIL::unescape_id(wire->name));
delete module;
goto skip_cell;
}
@ -757,13 +774,13 @@ struct LibertyFrontend : public Frontend {
if (design->has(cell_name)) {
Module *existing_mod = design->module(cell_name);
if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute(ID::blackbox)) {
log_error("Re-definition of cell/module %s!\n", log_id(cell_name));
log_error("Re-definition of cell/module %s!\n", RTLIL::unescape_id(cell_name));
} else if (flag_nooverwrite) {
log("Ignoring re-definition of module %s.\n", log_id(cell_name));
log("Ignoring re-definition of module %s.\n", RTLIL::unescape_id(cell_name));
delete module;
goto skip_cell;
} else {
log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", log_id(cell_name));
log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", RTLIL::unescape_id(cell_name));
design->remove(existing_mod);
}
}

View file

@ -40,6 +40,7 @@ struct RTLILFrontendWorker {
bool flag_nooverwrite = false;
bool flag_overwrite = false;
bool flag_lib = false;
bool flag_legalize = false;
int line_num;
std::string line_buf;
@ -322,6 +323,17 @@ struct RTLILFrontendWorker {
return val;
}
RTLIL::Wire *legalize_wire(RTLIL::IdString id)
{
int wires_size = current_module->wires_size();
if (wires_size == 0)
error("No wires found for legalization");
int hash = hash_ops<RTLIL::IdString>::hash(id).yield();
RTLIL::Wire *wire = current_module->wire_at(abs(hash % wires_size));
log("Legalizing wire `%s' to `%s'.\n", log_id(id), log_id(wire->name));
return wire;
}
RTLIL::SigSpec parse_sigspec()
{
RTLIL::SigSpec sig;
@ -339,8 +351,12 @@ struct RTLILFrontendWorker {
std::optional<RTLIL::IdString> id = try_parse_id();
if (id.has_value()) {
RTLIL::Wire *wire = current_module->wire(*id);
if (wire == nullptr)
error("Wire `%s' not found.", *id);
if (wire == nullptr) {
if (flag_legalize)
wire = legalize_wire(*id);
else
error("Wire `%s' not found.", *id);
}
sig = RTLIL::SigSpec(wire);
} else {
sig = RTLIL::SigSpec(parse_const());
@ -349,17 +365,44 @@ struct RTLILFrontendWorker {
while (try_parse_char('[')) {
int left = parse_integer();
if (left >= sig.size() || left < 0)
error("bit index %d out of range", left);
if (left >= sig.size() || left < 0) {
if (flag_legalize) {
int legalized;
if (sig.size() == 0)
legalized = 0;
else
legalized = std::max(0, std::min(left, sig.size() - 1));
log("Legalizing bit index %d to %d.\n", left, legalized);
left = legalized;
} else {
error("bit index %d out of range", left);
}
}
if (try_parse_char(':')) {
int right = parse_integer();
if (right < 0)
error("bit index %d out of range", right);
if (left < right)
error("invalid slice [%d:%d]", left, right);
sig = sig.extract(right, left-right+1);
if (right < 0) {
if (flag_legalize) {
log("Legalizing bit index %d to %d.\n", right, 0);
right = 0;
} else
error("bit index %d out of range", right);
}
if (left < right) {
if (flag_legalize) {
log("Legalizing bit index %d to %d.\n", left, right);
left = right;
} else
error("invalid slice [%d:%d]", left, right);
}
if (flag_legalize && left >= sig.size())
log("Legalizing slice %d:%d by igoring it\n", left, right);
else
sig = sig.extract(right, left - right + 1);
} else {
sig = sig.extract(left);
if (flag_legalize && left >= sig.size())
log("Legalizing slice %d by igoring it\n", left);
else
sig = sig.extract(left);
}
expect_char(']');
}
@ -476,8 +519,14 @@ struct RTLILFrontendWorker {
{
std::optional<RTLIL::IdString> id = try_parse_id();
if (id.has_value()) {
if (current_module->wire(*id) != nullptr)
error("RTLIL error: redefinition of wire %s.", *id);
if (current_module->wire(*id) != nullptr) {
if (flag_legalize) {
log("Legalizing redefinition of wire %s.\n", *id);
pool<RTLIL::Wire*> wires = {current_module->wire(*id)};
current_module->remove(wires);
} else
error("RTLIL error: redefinition of wire %s.", *id);
}
wire = current_module->addWire(std::move(*id));
break;
}
@ -528,8 +577,13 @@ struct RTLILFrontendWorker {
{
std::optional<RTLIL::IdString> id = try_parse_id();
if (id.has_value()) {
if (current_module->memories.count(*id) != 0)
error("RTLIL error: redefinition of memory %s.", *id);
if (current_module->memories.count(*id) != 0) {
if (flag_legalize) {
log("Legalizing redefinition of memory %s.\n", *id);
current_module->remove(current_module->memories.at(*id));
} else
error("RTLIL error: redefinition of memory %s.", *id);
}
memory->name = std::move(*id);
break;
}
@ -551,14 +605,36 @@ struct RTLILFrontendWorker {
expect_eol();
}
void legalize_width_parameter(RTLIL::Cell *cell, RTLIL::IdString port_name)
{
std::string width_param_name = port_name.str() + "_WIDTH";
if (cell->parameters.count(width_param_name) == 0)
return;
RTLIL::Const &param = cell->parameters.at(width_param_name);
if (param.as_int() != 0)
return;
cell->parameters[width_param_name] = RTLIL::Const(cell->getPort(port_name).size());
}
void parse_cell()
{
RTLIL::IdString cell_type = parse_id();
RTLIL::IdString cell_name = parse_id();
expect_eol();
if (current_module->cell(cell_name) != nullptr)
error("RTLIL error: redefinition of cell %s.", cell_name);
if (current_module->cell(cell_name) != nullptr) {
if (flag_legalize) {
RTLIL::IdString new_name;
int suffix = 1;
do {
new_name = RTLIL::IdString(cell_name.str() + "_" + std::to_string(suffix));
++suffix;
} while (current_module->cell(new_name) != nullptr);
log("Legalizing redefinition of cell %s by renaming to %s.\n", cell_name, new_name);
cell_name = new_name;
} else
error("RTLIL error: redefinition of cell %s.", cell_name);
}
RTLIL::Cell *cell = current_module->addCell(cell_name, cell_type);
cell->attributes = std::move(attrbuf);
@ -587,9 +663,15 @@ struct RTLILFrontendWorker {
expect_eol();
} else if (try_parse_keyword("connect")) {
RTLIL::IdString port_name = parse_id();
if (cell->hasPort(port_name))
error("RTLIL error: redefinition of cell port %s.", port_name);
if (cell->hasPort(port_name)) {
if (flag_legalize)
log("Legalizing redefinition of cell port %s.", port_name);
else
error("RTLIL error: redefinition of cell port %s.", port_name);
}
cell->setPort(std::move(port_name), parse_sigspec());
if (flag_legalize)
legalize_width_parameter(cell, port_name);
expect_eol();
} else if (try_parse_keyword("end")) {
expect_eol();
@ -606,6 +688,11 @@ struct RTLILFrontendWorker {
error("dangling attribute");
RTLIL::SigSpec s1 = parse_sigspec();
RTLIL::SigSpec s2 = parse_sigspec();
if (flag_legalize) {
int min_size = std::min(s1.size(), s2.size());
s1 = s1.extract(0, min_size);
s2 = s2.extract(0, min_size);
}
current_module->connect(std::move(s1), std::move(s2));
expect_eol();
}
@ -682,8 +769,13 @@ struct RTLILFrontendWorker {
RTLIL::IdString proc_name = parse_id();
expect_eol();
if (current_module->processes.count(proc_name) != 0)
error("RTLIL error: redefinition of process %s.", proc_name);
if (current_module->processes.count(proc_name) != 0) {
if (flag_legalize) {
log("Legalizing redefinition of process %s.\n", proc_name);
current_module->remove(current_module->processes.at(proc_name));
} else
error("RTLIL error: redefinition of process %s.", proc_name);
}
RTLIL::Process *proc = current_module->addProcess(std::move(proc_name));
proc->attributes = std::move(attrbuf);
@ -804,6 +896,11 @@ struct RTLILFrontend : public Frontend {
log(" -lib\n");
log(" only create empty blackbox modules\n");
log("\n");
log(" -legalize\n");
log(" prevent semantic errors (e.g. reference to unknown wire, redefinition of wire/cell)\n");
log(" by deterministically rewriting the input into something valid. Useful when using\n");
log(" fuzzing to generate random but valid RTLIL.\n");
log("\n");
}
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
@ -828,6 +925,10 @@ struct RTLILFrontend : public Frontend {
worker.flag_lib = true;
continue;
}
if (arg == "-legalize") {
worker.flag_legalize = true;
continue;
}
break;
}
extra_args(f, filename, args, argidx);

View file

@ -3114,9 +3114,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");
@ -3161,7 +3163,10 @@ 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");
#ifdef VERIFIC_VHDL_SUPPORT
log(" -vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl2019|-vhdl|\n");
#endif
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");
@ -3696,6 +3701,9 @@ struct VerificPass : public Pass {
if (GetSize(args) > argidx && (args[argidx] == "-f" || args[argidx] == "-F"))
{
unsigned verilog_mode = veri_file::UNDEFINED;
#ifdef VERIFIC_VHDL_SUPPORT
unsigned vhdl_mode = vhdl_file::UNDEFINED;
#endif
bool is_formal = false;
const char* filename = nullptr;
@ -3714,10 +3722,38 @@ struct VerificPass : public Pass {
} else if (args[argidx] == "-sv2009") {
verilog_mode = veri_file::SYSTEM_VERILOG_2009;
continue;
} else if (args[argidx] == "-sv2012" || args[argidx] == "-sv" || args[argidx] == "-formal") {
} else if (args[argidx] == "-sv2012") {
verilog_mode = veri_file::SYSTEM_VERILOG_2012;
continue;
} else if (args[argidx] == "-sv2017") {
verilog_mode = veri_file::SYSTEM_VERILOG_2017;
continue;
} else if (args[argidx] == "-sv" || args[argidx] == "-formal") {
verilog_mode = veri_file::SYSTEM_VERILOG;
if (args[argidx] == "-formal") is_formal = true;
continue;
#ifdef VERIFIC_VHDL_SUPPORT
} else if (args[argidx] == "-vhdl87") {
vhdl_mode = vhdl_file::VHDL_87;
vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1987").c_str());
continue;
} else if (args[argidx] == "-vhdl93") {
vhdl_mode = vhdl_file::VHDL_93;
vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1993").c_str());
continue;
} else if (args[argidx] == "-vhdl2k") {
vhdl_mode = vhdl_file::VHDL_2K;
vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1993").c_str());
continue;
} else if (args[argidx] == "-vhdl2019") {
vhdl_mode = vhdl_file::VHDL_2019;
vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_2019").c_str());
continue;
} else if (args[argidx] == "-vhdl2008" || args[argidx] == "-vhdl") {
vhdl_mode = vhdl_file::VHDL_2008;
vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_2008").c_str());
continue;
#endif
} else if (args[argidx].compare(0, 1, "-") == 0) {
cmd_error(args, argidx, "unknown option");
goto check_error;
@ -3742,10 +3778,36 @@ struct VerificPass : public Pass {
veri_file::DefineMacro("VERIFIC");
veri_file::DefineMacro(is_formal ? "FORMAL" : "SYNTHESIS");
#ifdef VERIFIC_VHDL_SUPPORT
if (vhdl_mode == vhdl_file::UNDEFINED) {
vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_2008").c_str());
vhdl_mode = vhdl_file::VHDL_2008;
}
int i;
Array *file_names_sv = new Array(POINTER_HASH);
FOREACH_ARRAY_ITEM(file_names, i, filename) {
std::string filename_str = filename;
if ((filename_str.substr(filename_str.find_last_of(".") + 1) == "vhd") ||
(filename_str.substr(filename_str.find_last_of(".") + 1) == "vhdl")) {
if (!vhdl_file::Analyze(filename, work.c_str(), vhdl_mode)) {
verific_error_msg.clear();
log_cmd_error("Reading VHDL sources failed.\n");
}
} else {
file_names_sv->Insert(strdup(filename));
}
}
if (!veri_file::AnalyzeMultipleFiles(file_names_sv, analysis_mode, work.c_str(), veri_file::MFCU)) {
verific_error_msg.clear();
log_cmd_error("Reading Verilog/SystemVerilog sources failed.\n");
}
delete file_names_sv;
#else
if (!veri_file::AnalyzeMultipleFiles(file_names, analysis_mode, work.c_str(), veri_file::MFCU)) {
verific_error_msg.clear();
log_cmd_error("Reading Verilog/SystemVerilog sources failed.\n");
}
#endif
delete file_names;
verific_import_pending = true;
@ -3753,7 +3815,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;
@ -3766,7 +3829,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();

View file

@ -1846,7 +1846,10 @@ struct VerificSvaImporter
if (mode_assume) c = module->addAssume(root_name, sig_a_q, sig_en_q);
if (mode_cover) c = module->addCover(root_name, sig_a_q, sig_en_q);
if (c) importer->import_attributes(c->attributes, root);
if (c) {
c->set_bool_attribute(ID(keep));
importer->import_attributes(c->attributes, root);
}
}
}
catch (ParserErrorException)

View file

@ -500,7 +500,6 @@ struct VerilogFrontend : public Frontend {
log("Parsing %s%s input from `%s' to AST representation.\n",
parse_mode.formal ? "formal " : "", parse_mode.sv ? "SystemVerilog" : "Verilog", filename.c_str());
log("verilog frontend filename %s\n", filename.c_str());
if (flag_relative_share) {
auto share_path = proc_share_dirname();
if (filename.substr(0, share_path.length()) == share_path)

View file

@ -291,7 +291,7 @@ static RTLIL::Const const_shift_worker(const RTLIL::Const &arg1, const RTLIL::Co
if (pos < 0)
result.set(i, vacant_bits);
else if (pos >= BigInteger(GetSize(arg1)))
result.set(i, sign_ext ? arg1.back() : vacant_bits);
result.set(i, sign_ext && !arg1.empty() ? arg1.back() : vacant_bits);
else
result.set(i, arg1[pos.toInt()]);
}

View file

@ -185,50 +185,68 @@ struct AigMaker
int or_gate(int A, int B)
{
return nand_gate(not_gate(A), not_gate(B));
int not_a = not_gate(A);
int not_b = not_gate(B);
return nand_gate(not_a, not_b);
}
int nor_gate(int A, int B)
{
return and_gate(not_gate(A), not_gate(B));
int not_a = not_gate(A);
int not_b = not_gate(B);
return and_gate(not_a, not_b);
}
int xor_gate(int A, int B)
{
return nor_gate(and_gate(A, B), nor_gate(A, B));
int a_and_b = and_gate(A, B);
int a_nor_b = nor_gate(A, B);
return nor_gate(a_and_b, a_nor_b);
}
int xnor_gate(int A, int B)
{
return or_gate(and_gate(A, B), nor_gate(A, B));
int a_and_b = and_gate(A, B);
int a_nor_b = nor_gate(A, B);
return or_gate(a_and_b, a_nor_b);
}
int andnot_gate(int A, int B)
{
return and_gate(A, not_gate(B));
int not_b = not_gate(B);
return and_gate(A, not_b);
}
int ornot_gate(int A, int B)
{
return or_gate(A, not_gate(B));
int not_b = not_gate(B);
return or_gate(A, not_b);
}
int mux_gate(int A, int B, int S)
{
return or_gate(and_gate(A, not_gate(S)), and_gate(B, S));
int not_s = not_gate(S);
int a_active = and_gate(A, not_s);
int b_active = and_gate(B, S);
return or_gate(a_active, b_active);
}
vector<int> adder(const vector<int> &A, const vector<int> &B, int carry, vector<int> *X = nullptr, vector<int> *CO = nullptr)
vector<int> adder(const vector<int> &A, const vector<int> &B, int carry_in, vector<int> *X = nullptr, vector<int> *CO = nullptr)
{
vector<int> Y(GetSize(A));
log_assert(GetSize(A) == GetSize(B));
for (int i = 0; i < GetSize(A); i++) {
Y[i] = xor_gate(xor_gate(A[i], B[i]), carry);
carry = or_gate(and_gate(A[i], B[i]), and_gate(or_gate(A[i], B[i]), carry));
int a_xor_b = xor_gate(A[i], B[i]);
int a_or_b = or_gate(A[i], B[i]);
int a_and_b = and_gate(A[i], B[i]);
Y[i] = xor_gate(a_xor_b, carry_in);
int tmp = and_gate(a_or_b, carry_in);
int carry_out = or_gate(a_and_b, tmp);
if (X != nullptr)
X->at(i) = xor_gate(A[i], B[i]);
X->at(i) = a_xor_b;
if (CO != nullptr)
CO->at(i) = carry;
CO->at(i) = carry_out;
carry_in = carry_out;
}
return Y;
}
@ -307,13 +325,13 @@ Aig::Aig(Cell *cell)
int A = mk.inport(ID::A, i);
int B = mk.inport(ID::B, i);
int Y = cell->type.in(ID($and), ID($_AND_)) ? mk.and_gate(A, B) :
cell->type.in(ID($_NAND_)) ? mk.nand_gate(A, B) :
cell->type.in(ID($_NAND_)) ? mk.nand_gate(A, B) :
cell->type.in(ID($or), ID($_OR_)) ? mk.or_gate(A, B) :
cell->type.in(ID($_NOR_)) ? mk.nor_gate(A, B) :
cell->type.in(ID($_NOR_)) ? mk.nor_gate(A, B) :
cell->type.in(ID($xor), ID($_XOR_)) ? mk.xor_gate(A, B) :
cell->type.in(ID($xnor), ID($_XNOR_)) ? mk.xnor_gate(A, B) :
cell->type.in(ID($_ANDNOT_)) ? mk.andnot_gate(A, B) :
cell->type.in(ID($_ORNOT_)) ? mk.ornot_gate(A, B) : -1;
cell->type.in(ID($_ANDNOT_)) ? mk.andnot_gate(A, B) :
cell->type.in(ID($_ORNOT_)) ? mk.ornot_gate(A, B) : -1;
mk.outport(Y, ID::Y, i);
}
goto optimize;
@ -465,7 +483,8 @@ Aig::Aig(Cell *cell)
int B = mk.inport(ID::B);
int C = mk.inport(ID::C);
int D = mk.inport(ID::D);
int Y = mk.nor_gate(mk.and_gate(A, B), mk.and_gate(C, D));
int a_and_b = mk.and_gate(A, B);
int Y = mk.nor_gate(a_and_b, mk.and_gate(C, D));
mk.outport(Y, ID::Y);
goto optimize;
}
@ -476,7 +495,8 @@ Aig::Aig(Cell *cell)
int B = mk.inport(ID::B);
int C = mk.inport(ID::C);
int D = mk.inport(ID::D);
int Y = mk.nand_gate(mk.or_gate(A, B), mk.or_gate(C, D));
int a_or_b = mk.or_gate(A, B);
int Y = mk.nand_gate(a_or_b, mk.or_gate(C, D));
mk.outport(Y, ID::Y);
goto optimize;
}

View file

@ -112,6 +112,41 @@ void reduce_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
db->add_edge(cell, ID::A, i, ID::Y, 0, -1);
}
void logic_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
{
int a_width = GetSize(cell->getPort(ID::A));
int b_width = GetSize(cell->getPort(ID::B));
for (int i = 0; i < a_width; i++)
db->add_edge(cell, ID::A, i, ID::Y, 0, -1);
for (int i = 0; i < b_width; i++)
db->add_edge(cell, ID::B, i, ID::Y, 0, -1);
}
void concat_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
{
int a_width = GetSize(cell->getPort(ID::A));
int b_width = GetSize(cell->getPort(ID::B));
for (int i = 0; i < a_width; i++)
db->add_edge(cell, ID::A, i, ID::Y, i, -1);
for (int i = 0; i < b_width; i++)
db->add_edge(cell, ID::B, i, ID::Y, a_width + i, -1);
}
void slice_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
{
int offset = cell->getParam(ID::OFFSET).as_int();
int a_width = GetSize(cell->getPort(ID::A));
int y_width = GetSize(cell->getPort(ID::Y));
for (int i = 0; i < y_width; i++) {
int a_bit = offset + i;
if (a_bit >= 0 && a_bit < a_width)
db->add_edge(cell, ID::A, a_bit, ID::Y, i, -1);
}
}
void compare_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
{
int a_width = GetSize(cell->getPort(ID::A));
@ -254,7 +289,7 @@ void shift_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
int skip = 1 << (k + 1);
int base = skip -1;
if (i % skip != base && i - a_width + 2 < 1 << b_width_capped)
db->add_edge(cell, ID::B, k, ID::Y, i, -1);
db->add_edge(cell, ID::B, k, ID::Y, i, -1);
} else if (is_signed) {
if (i - a_width + 2 < 1 << b_width_capped)
db->add_edge(cell, ID::B, k, ID::Y, i, -1);
@ -388,6 +423,64 @@ void ff_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
db->add_edge(cell, ID::ARST, 0, ID::Q, k, -1);
}
void full_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
{
std::vector<RTLIL::IdString> input_ports;
std::vector<RTLIL::IdString> output_ports;
for (auto &conn : cell->connections())
{
RTLIL::IdString port = conn.first;
RTLIL::PortDir dir = cell->port_dir(port);
if (cell->input(port) || dir == RTLIL::PortDir::PD_INOUT)
input_ports.push_back(port);
if (cell->output(port) || dir == RTLIL::PortDir::PD_INOUT)
output_ports.push_back(port);
}
for (auto out_port : output_ports)
{
int out_width = GetSize(cell->getPort(out_port));
for (int out_bit = 0; out_bit < out_width; out_bit++)
{
for (auto in_port : input_ports)
{
int in_width = GetSize(cell->getPort(in_port));
for (int in_bit = 0; in_bit < in_width; in_bit++)
db->add_edge(cell, in_port, in_bit, out_port, out_bit, -1);
}
}
}
}
void bweqx_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
{
int width = GetSize(cell->getPort(ID::Y));
int a_width = GetSize(cell->getPort(ID::A));
int b_width = GetSize(cell->getPort(ID::B));
int max_width = std::min(width, std::min(a_width, b_width));
for (int i = 0; i < max_width; i++) {
db->add_edge(cell, ID::A, i, ID::Y, i, -1);
db->add_edge(cell, ID::B, i, ID::Y, i, -1);
}
}
void bwmux_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
{
int width = GetSize(cell->getPort(ID::Y));
int a_width = GetSize(cell->getPort(ID::A));
int b_width = GetSize(cell->getPort(ID::B));
int s_width = GetSize(cell->getPort(ID::S));
int max_width = std::min(width, std::min(a_width, std::min(b_width, s_width)));
for (int i = 0; i < max_width; i++) {
db->add_edge(cell, ID::A, i, ID::Y, i, -1);
db->add_edge(cell, ID::B, i, ID::Y, i, -1);
db->add_edge(cell, ID::S, i, ID::Y, i, -1);
}
}
PRIVATE_NAMESPACE_END
bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL::Cell *cell)
@ -417,6 +510,21 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL
return true;
}
if (cell->type.in(ID($logic_and), ID($logic_or))) {
logic_op(this, cell);
return true;
}
if (cell->type == ID($slice)) {
slice_op(this, cell);
return true;
}
if (cell->type == ID($concat)) {
concat_op(this, cell);
return true;
}
if (cell->type.in(ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx))) {
shift_op(this, cell);
return true;
@ -442,6 +550,16 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL
return true;
}
if (cell->type == ID($bweqx)) {
bweqx_op(this, cell);
return true;
}
if (cell->type == ID($bwmux)) {
bwmux_op(this, cell);
return true;
}
if (cell->type.in(ID($mem_v2), ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2), ID($meminit))) {
mem_op(this, cell);
return true;
@ -452,13 +570,24 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL
return true;
}
// FIXME: $mul $div $mod $divfloor $modfloor $slice $concat
// FIXME: $lut $sop $alu $lcu $macc $macc_v2 $fa
// FIXME: $mul $div $mod $divfloor $modfloor $pow $slice $concat $bweqx
// FIXME: $lut $sop $alu $lcu $macc $fa $logic_and $logic_or $bwmux
if (cell->type.in(ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow))) {
full_op(this, cell);
return true;
}
// FIXME: $_BUF_ $_NOT_ $_AND_ $_NAND_ $_OR_ $_NOR_ $_XOR_ $_XNOR_ $_ANDNOT_ $_ORNOT_
// FIXME: $_MUX_ $_NMUX_ $_MUX4_ $_MUX8_ $_MUX16_ $_AOI3_ $_OAI3_ $_AOI4_ $_OAI4_
if (cell->type.in(ID($lut), ID($sop), ID($alu), ID($lcu), ID($macc), ID($macc_v2))) {
full_op(this, cell);
return true;
}
if (cell->type.in(
ID($_BUF_), ID($_NOT_), ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_),
ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_), ID($_MUX_), ID($_NMUX_),
ID($_MUX4_), ID($_MUX8_), ID($_MUX16_), ID($_AOI3_), ID($_OAI3_), ID($_AOI4_),
ID($_OAI4_), ID($_TBUF_))) {
full_op(this, cell);
return true;
}
// FIXME: $specify2 $specify3 $specrule ???
// FIXME: $equiv $set_tag $get_tag $overwrite_tag $original_tag
@ -468,4 +597,3 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL
return false;
}

View file

@ -305,18 +305,18 @@ struct CellTypes
cell_types.clear();
}
bool cell_known(const RTLIL::IdString &type) const
bool cell_known(RTLIL::IdString type) const
{
return cell_types.count(type) != 0;
}
bool cell_output(const RTLIL::IdString &type, const RTLIL::IdString &port) const
bool cell_output(RTLIL::IdString type, RTLIL::IdString port) const
{
auto it = cell_types.find(type);
return it != cell_types.end() && it->second.outputs.count(port) != 0;
}
bool cell_input(const RTLIL::IdString &type, const RTLIL::IdString &port) const
bool cell_input(RTLIL::IdString type, RTLIL::IdString port) const
{
auto it = cell_types.find(type);
return it != cell_types.end() && it->second.inputs.count(port) != 0;
@ -332,7 +332,7 @@ struct CellTypes
return RTLIL::PortDir(is_input + is_output * 2);
}
bool cell_evaluable(const RTLIL::IdString &type) const
bool cell_evaluable(RTLIL::IdString type) const
{
auto it = cell_types.find(type);
return it != cell_types.end() && it->second.is_evaluable;

View file

@ -28,6 +28,10 @@
YOSYS_NAMESPACE_BEGIN
/**
* ConstEval provides on-demand constant propagation by traversing input cones
* with caching
*/
struct ConstEval
{
RTLIL::Module *module;

View file

@ -255,6 +255,7 @@ int main(int argc, char **argv)
("h,help", "print this help message. If given, print help for <command>.",
cxxopts::value<std::string>(), "[<command>]")
("V,version", "print version information and exit")
("git-hash", "print git commit hash and exit")
("infile", "input files", cxxopts::value<std::vector<std::string>>())
;
options.add_options("logging")
@ -332,6 +333,10 @@ int main(int argc, char **argv)
std::cout << yosys_version_str << std::endl;
exit(0);
}
if (result.count("git-hash")) {
std::cout << yosys_git_hash_str << std::endl;
exit(0);
}
if (result.count("S")) {
passes_commands.push_back("synth");
run_shell = false;
@ -764,33 +769,6 @@ int main(int argc, char **argv)
}
}
#if defined(YOSYS_ENABLE_COVER) && (defined(__linux__) || defined(__FreeBSD__))
if (getenv("YOSYS_COVER_DIR") || getenv("YOSYS_COVER_FILE"))
{
string filename;
FILE *f;
if (getenv("YOSYS_COVER_DIR")) {
filename = stringf("%s/yosys_cover_%d_XXXXXX.txt", getenv("YOSYS_COVER_DIR"), getpid());
filename = make_temp_file(filename);
} else {
filename = getenv("YOSYS_COVER_FILE");
}
f = fopen(filename.c_str(), "a+");
if (f == NULL)
log_error("Can't create coverage file `%s'.\n", filename);
log("<writing coverage file \"%s\">\n", filename);
for (auto &it : get_coverage_data())
fprintf(f, "%-60s %10d %s\n", it.second.first.c_str(), it.second.second, it.first.c_str());
fclose(f);
}
#endif
log_check_expected();
yosys_atexit();

View file

@ -1321,6 +1321,12 @@ public:
return i < 0 ? 0 : 1;
}
int lookup(const K &key) const
{
Hasher::hash_t hash = database.do_hash(key);
return database.do_lookup_no_rehash(key, hash);
}
void expect(const K &key, int i)
{
int j = (*this)(key);

View file

@ -602,7 +602,7 @@ void format_emit_string_view(std::string &result, std::string_view spec, int *dy
}
void format_emit_idstring(std::string &result, std::string_view spec, int *dynamic_ints,
DynamicIntCount num_dynamic_ints, const IdString &arg)
DynamicIntCount num_dynamic_ints, const RTLIL::IdString &arg)
{
if (spec == "%s") {
// Format checking will have guaranteed num_dynamic_ints == 0.

View file

@ -90,11 +90,11 @@ int gettimeofday(struct timeval *tv, struct timezone *tz)
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&counter);
counter.QuadPart *= 1000000;
counter.QuadPart *= 1'000'000;
counter.QuadPart /= freq.QuadPart;
tv->tv_sec = long(counter.QuadPart / 1000000);
tv->tv_usec = counter.QuadPart % 1000000;
tv->tv_usec = counter.QuadPart % 1'000'000;
return 0;
}
@ -135,7 +135,7 @@ static void logv_string(std::string_view format, std::string str) {
initial_tv = tv;
if (tv.tv_usec < initial_tv.tv_usec) {
tv.tv_sec--;
tv.tv_usec += 1000000;
tv.tv_usec += 1'000'000;
}
tv.tv_sec -= initial_tv.tv_sec;
tv.tv_usec -= initial_tv.tv_usec;
@ -203,6 +203,8 @@ static void logv_string(std::string_view format, std::string str) {
void log_formatted_string(std::string_view format, std::string str)
{
log_assert(!Multithreading::active());
if (log_make_debug && !ys_debug(1))
return;
logv_string(format, std::move(str));
@ -210,6 +212,8 @@ void log_formatted_string(std::string_view format, std::string str)
void log_formatted_header(RTLIL::Design *design, std::string_view format, std::string str)
{
log_assert(!Multithreading::active());
bool pop_errfile = false;
log_spacer();
@ -249,6 +253,8 @@ void log_formatted_header(RTLIL::Design *design, std::string_view format, std::s
void log_formatted_warning(std::string_view prefix, std::string message)
{
log_assert(!Multithreading::active());
bool suppressed = false;
for (auto &re : log_nowarn_regexes)
@ -681,55 +687,4 @@ void log_check_expected()
check_err("prefixed error", pattern, item);
}
// ---------------------------------------------------
// This is the magic behind the code coverage counters
// ---------------------------------------------------
#if defined(YOSYS_ENABLE_COVER) && (defined(__linux__) || defined(__FreeBSD__))
dict<std::string, std::pair<std::string, int>> extra_coverage_data;
void cover_extra(std::string parent, std::string id, bool increment) {
if (extra_coverage_data.count(id) == 0) {
for (CoverData *p = __start_yosys_cover_list; p != __stop_yosys_cover_list; p++)
if (p->id == parent)
extra_coverage_data[id].first = stringf("%s:%d:%s", p->file, p->line, p->func);
log_assert(extra_coverage_data.count(id));
}
if (increment)
extra_coverage_data[id].second++;
}
dict<std::string, std::pair<std::string, int>> get_coverage_data()
{
dict<std::string, std::pair<std::string, int>> coverage_data;
for (auto &it : pass_register) {
std::string key = stringf("passes.%s", it.first);
coverage_data[key].first = stringf("%s:%d:%s", __FILE__, __LINE__, __FUNCTION__);
coverage_data[key].second += it.second->call_counter;
}
for (auto &it : extra_coverage_data) {
if (coverage_data.count(it.first))
log_warning("found duplicate coverage id \"%s\".\n", it.first);
coverage_data[it.first].first = it.second.first;
coverage_data[it.first].second += it.second.second;
}
for (CoverData *p = __start_yosys_cover_list; p != __stop_yosys_cover_list; p++) {
if (coverage_data.count(p->id))
log_warning("found duplicate coverage id \"%s\".\n", p->id);
coverage_data[p->id].first = stringf("%s:%d:%s", p->file, p->line, p->func);
coverage_data[p->id].second += p->counter;
}
for (auto &it : coverage_data)
if (!it.second.first.compare(0, strlen(YOSYS_SRC "/"), YOSYS_SRC "/"))
it.second.first = it.second.first.substr(strlen(YOSYS_SRC "/"));
return coverage_data;
}
#endif
YOSYS_NAMESPACE_END

View file

@ -24,6 +24,7 @@
#include <time.h>
#include <atomic>
#include <regex>
#define YS_REGEX_COMPILE(param) std::regex(param, \
std::regex_constants::nosubs | \
@ -290,53 +291,6 @@ void log_abort_internal(const char *file, int line);
#define log_ping() YOSYS_NAMESPACE_PREFIX log("-- %s:%d %s --\n", __FILE__, __LINE__, __PRETTY_FUNCTION__)
// ---------------------------------------------------
// This is the magic behind the code coverage counters
// ---------------------------------------------------
#if defined(YOSYS_ENABLE_COVER) && (defined(__linux__) || defined(__FreeBSD__))
#define cover(_id) do { \
static CoverData __d __attribute__((section("yosys_cover_list"), aligned(1), used)) = { __FILE__, __FUNCTION__, _id, __LINE__, 0 }; \
__d.counter++; \
} while (0)
struct CoverData {
const char *file, *func, *id;
int line, counter;
} YS_ATTRIBUTE(packed);
// this two symbols are created by the linker for the "yosys_cover_list" ELF section
extern "C" struct CoverData __start_yosys_cover_list[];
extern "C" struct CoverData __stop_yosys_cover_list[];
extern dict<std::string, std::pair<std::string, int>> extra_coverage_data;
void cover_extra(std::string parent, std::string id, bool increment = true);
dict<std::string, std::pair<std::string, int>> get_coverage_data();
#define cover_list(_id, ...) do { cover(_id); \
std::string r = cover_list_worker(_id, __VA_ARGS__); \
log_assert(r.empty()); \
} while (0)
static inline std::string cover_list_worker(std::string, std::string last) {
return last;
}
template<typename... T>
std::string cover_list_worker(std::string prefix, std::string first, T... rest) {
std::string selected = cover_list_worker(prefix, rest...);
cover_extra(prefix, prefix + "." + first, first == selected);
return first == selected ? "" : selected;
}
#else
# define cover(...) do { } while (0)
# define cover_list(...) do { } while (0)
#endif
// ------------------------------------------------------------
// everything below this line are utilities for troubleshooting
// ------------------------------------------------------------

View file

@ -78,7 +78,7 @@ ContentListing* ContentListing::open_option(const string &text,
}
#define MAX_LINE_LEN 80
void log_pass_str(const std::string &pass_str, std::string indent_str, bool leading_newline=false) {
void log_body_str(const std::string &pass_str, std::string indent_str, bool leading_newline=false, bool is_formatted=false) {
if (pass_str.empty())
return;
std::istringstream iss(pass_str);
@ -86,26 +86,30 @@ void log_pass_str(const std::string &pass_str, std::string indent_str, bool lead
log("\n");
for (std::string line; std::getline(iss, line);) {
log("%s", indent_str);
auto curr_len = indent_str.length();
std::istringstream lss(line);
for (std::string word; std::getline(lss, word, ' ');) {
while (word[0] == '`' && word.back() == '`')
word = word.substr(1, word.length()-2);
if (curr_len + word.length() >= MAX_LINE_LEN-1) {
curr_len = 0;
log("\n%s", indent_str);
}
if (word.length()) {
log("%s ", word);
curr_len += word.length() + 1;
if (is_formatted) {
log("%s", line);
} else {
auto curr_len = indent_str.length();
std::istringstream lss(line);
for (std::string word; std::getline(lss, word, ' ');) {
while (word[0] == '`' && word.back() == '`')
word = word.substr(1, word.length()-2);
if (curr_len + word.length() >= MAX_LINE_LEN-1) {
curr_len = 0;
log("\n%s", indent_str);
}
if (word.length()) {
log("%s ", word);
curr_len += word.length() + 1;
}
}
}
log("\n");
}
}
void log_pass_str(const std::string &pass_str, int indent=0, bool leading_newline=false) {
void log_body(const ContentListing &content, int indent=0, bool leading_newline=false) {
std::string indent_str(indent*4, ' ');
log_pass_str(pass_str, indent_str, leading_newline);
log_body_str(content.body, indent_str, leading_newline, content.type.compare("code") == 0);
}
PrettyHelp *current_help = nullptr;
@ -134,16 +138,16 @@ void PrettyHelp::log_help() const
{
for (auto &content : _root_listing) {
if (content.type.compare("usage") == 0) {
log_pass_str(content.body, 1, true);
log_body(content, 1, true);
log("\n");
} else if (content.type.compare("option") == 0) {
log_pass_str(content.body, 1);
log_body(content, 1);
for (auto text : content) {
log_pass_str(text.body, 2);
log_body(text, 2);
log("\n");
}
} else {
log_pass_str(content.body, 0);
log_body(content, 0);
log("\n");
}
}

View file

@ -29,6 +29,22 @@ YOSYS_NAMESPACE_BEGIN
struct ModIndex : public RTLIL::Monitor
{
struct PointerOrderedSigBit : public RTLIL::SigBit {
PointerOrderedSigBit(SigBit s) {
wire = s.wire;
if (wire)
offset = s.offset;
else
data = s.data;
}
inline bool operator<(const RTLIL::SigBit &other) const {
if (wire == other.wire)
return wire ? (offset < other.offset) : (data < other.data);
if (wire != nullptr && other.wire != nullptr)
return wire < other.wire; // look here
return (wire != nullptr) < (other.wire != nullptr);
}
};
struct PortInfo {
RTLIL::Cell* cell;
RTLIL::IdString port;
@ -78,7 +94,7 @@ struct ModIndex : public RTLIL::Monitor
SigMap sigmap;
RTLIL::Module *module;
std::map<RTLIL::SigBit, SigBitInfo> database;
std::map<PointerOrderedSigBit, SigBitInfo> database;
int auto_reload_counter;
bool auto_reload_module;
@ -95,8 +111,11 @@ struct ModIndex : public RTLIL::Monitor
{
for (int i = 0; i < GetSize(sig); i++) {
RTLIL::SigBit bit = sigmap(sig[i]);
if (bit.wire)
if (bit.wire) {
database[bit].ports.erase(PortInfo(cell, port, i));
if (!database[bit].is_input && !database[bit].is_output && database[bit].ports.empty())
database.erase(bit);
}
}
}
@ -133,11 +152,11 @@ struct ModIndex : public RTLIL::Monitor
}
}
void check()
bool ok()
{
#ifndef NDEBUG
if (auto_reload_module)
return;
return true;
for (auto it : database)
log_assert(it.first == sigmap(it.first));
@ -157,12 +176,18 @@ struct ModIndex : public RTLIL::Monitor
else if (!(it.second == database_bak.at(it.first)))
log("ModuleIndex::check(): Different content for database[%s].\n", log_signal(it.first));
log_assert(database == database_bak);
return false;
}
return true;
#endif
}
void notify_connect(RTLIL::Cell *cell, const RTLIL::IdString &port, const RTLIL::SigSpec &old_sig, const RTLIL::SigSpec &sig) override
void check()
{
log_assert(ok());
}
void notify_connect(RTLIL::Cell *cell, RTLIL::IdString port, const RTLIL::SigSpec &old_sig, const RTLIL::SigSpec &sig) override
{
log_assert(module == cell->module);

102
kernel/pattern.h Normal file
View file

@ -0,0 +1,102 @@
#ifndef OPT_DFF_COMP_H
#define OPT_DFF_COMP_H
#include "kernel/rtlil.h"
#include <map>
#include <optional>
YOSYS_NAMESPACE_BEGIN
/**
* Pattern matching utilities for control signal analysis.
*
* A pattern_t maps control signals to required values, representing a
* product term (conjunction): {A=1, B=0} means "A AND !B".
*
* A patterns_t is a set of patterns representing a sum-of-products:
* {{A=1, B=0}, {A=0, C=1}} means "(A AND !B) OR (!A AND C)".
*
* Used for analyzing MUX tree control paths in DFF optimization.
*/
// Pattern matching for clock enable
// A pattern maps control signals to their required values for a MUX path
typedef std::map<RTLIL::SigBit, bool> pattern_t; // Set of control signals that must ALL match required vals
typedef std::set<pattern_t> patterns_t; // Alternative patterns (OR)
typedef std::pair<RTLIL::SigBit, bool> ctrl_t; // Control signal
typedef std::set<ctrl_t> ctrls_t; // Set of control signals that must ALL be active
/**
* Find if two patterns differ in exactly one variable.
* Example: {A=1,B=1} vs {A=1,B=0} returns B, allows simplification: (A&B) | (A&!B) => A
*/
inline std::optional<RTLIL::SigBit> find_complementary_pattern_var(
const pattern_t& left,
const pattern_t& right
) {
std::optional<RTLIL::SigBit> ret;
for (const auto &pt : left) {
// Left requires signal that right doesn't constrain - incompatible domains
if (right.count(pt.first) == 0)
return std::nullopt;
// Signal has same required value in both - not the complement variable
if (right.at(pt.first) == pt.second)
continue;
// Already found one differing signal, now found another - not simplifiable
if (ret)
return std::nullopt;
// First differing signal - candidate complement variable
ret = pt.first;
}
return ret;
}
/**
* Simplify a sum-of-products by merging complementary patterns: (A&B) | (A&!B) => A,
* and removing redundant patterns: A | (A&B) => A
*/
inline void simplify_patterns(patterns_t& patterns) {
auto new_patterns = patterns;
// Merge complementary patterns
bool optimized;
do {
optimized = false;
for (auto i = patterns.begin(); i != patterns.end(); i++) {
for (auto j = std::next(i, 1); j != patterns.end(); j++) {
const auto& left = (GetSize(*j) <= GetSize(*i)) ? *j : *i;
auto right = (GetSize(*i) < GetSize(*j)) ? *j : *i;
const auto complementary_var = find_complementary_pattern_var(left, right);
if (complementary_var && new_patterns.count(right)) {
new_patterns.erase(right);
right.erase(complementary_var.value());
new_patterns.insert(right);
optimized = true;
}
}
}
patterns = new_patterns;
} while(optimized);
// Remove redundant patterns
for (auto i = patterns.begin(); i != patterns.end(); ++i) {
for (auto j = std::next(i, 1); j != patterns.end(); ++j) {
const auto& left = (GetSize(*j) <= GetSize(*i)) ? *j : *i;
const auto& right = (GetSize(*i) < GetSize(*j)) ? *j : *i;
bool redundant = true;
for (const auto& pt : left)
if (right.count(pt.first) == 0 || right.at(pt.first) != pt.second)
redundant = false;
if (redundant)
new_patterns.erase(right);
}
}
patterns = std::move(new_patterns);
}
YOSYS_NAMESPACE_END
#endif

View file

@ -1207,7 +1207,7 @@ struct LicensePass : public Pass {
log(" | |\n");
log(" | yosys -- Yosys Open SYnthesis Suite |\n");
log(" | |\n");
log(" | Copyright (C) 2012 - 2025 Claire Xenia Wolf <claire@yosyshq.com> |\n");
log(" | Copyright (C) 2012 - 2026 Claire Xenia Wolf <claire@yosyshq.com> |\n");
log(" | |\n");
log(" | Permission to use, copy, modify, and/or distribute this software for any |\n");
log(" | purpose with or without fee is hereby granted, provided that the above |\n");

File diff suppressed because it is too large Load diff

View file

@ -134,6 +134,17 @@ struct RTLIL::IdString
std::string_view str_view() const { return {buf, static_cast<size_t>(size)}; }
};
struct AutoidxStorage {
// Append the negated (i.e. positive) ID to this string to get
// the real string. The prefix strings must live forever.
const std::string *prefix;
// Cache of the full string, or nullptr if not cached yet.
std::atomic<char *> full_str;
AutoidxStorage(const std::string *prefix) : prefix(prefix), full_str(nullptr) {}
AutoidxStorage(AutoidxStorage&& other) : prefix(other.prefix), full_str(other.full_str.exchange(nullptr, std::memory_order_relaxed)) {}
~AutoidxStorage() { delete[] full_str.load(std::memory_order_acquire); }
};
// the global id string cache
@ -147,17 +158,12 @@ struct RTLIL::IdString
static std::vector<Storage> global_id_storage_;
// Lookup table for non-autoidx IDs
static std::unordered_map<std::string_view, int> global_id_index_;
// Shared prefix string storage for autoidx IDs, which have negative
// indices. Append the negated (i.e. positive) ID to this string to get
// the real string. The prefix strings must live forever.
static std::unordered_map<int, const std::string*> global_autoidx_id_prefix_storage_;
// Explicit string storage for autoidx IDs
static std::unordered_map<int, char*> global_autoidx_id_storage_;
#ifndef YOSYS_NO_IDS_REFCNT
// Storage for autoidx IDs, which have negative indices, i.e. all entries in this
// map have negative keys.
static std::unordered_map<int, AutoidxStorage> global_autoidx_id_storage_;
// All (index, refcount) pairs in this map have refcount > 0.
static std::unordered_map<int, int> global_refcount_storage_;
static std::vector<int> global_free_idx_list_;
#endif
static int refcount(int idx) {
auto it = global_refcount_storage_.find(idx);
@ -189,6 +195,7 @@ struct RTLIL::IdString
static int insert(std::string_view p)
{
log_assert(destruct_guard_ok);
log_assert(!Multithreading::active());
auto it = global_id_index_.find(p);
if (it != global_id_index_.end()) {
@ -204,8 +211,9 @@ struct RTLIL::IdString
// Inserts an ID with string `prefix + autoidx', incrementing autoidx.
// `prefix` must start with '$auto$', end with '$', and live forever.
static IdString new_autoidx_with_prefix(const std::string *prefix) {
log_assert(!Multithreading::active());
int index = -(autoidx++);
global_autoidx_id_prefix_storage_.insert({index, prefix});
global_autoidx_id_storage_.insert({index, prefix});
return from_index(index);
}
@ -215,8 +223,8 @@ struct RTLIL::IdString
constexpr inline IdString() : index_(0) { }
inline IdString(const char *str) : index_(insert(std::string_view(str))) { }
constexpr inline IdString(const IdString &str) : index_(str.index_) { }
inline IdString(IdString &&str) : index_(str.index_) { str.index_ = 0; }
constexpr IdString(const IdString &str) = default;
IdString(IdString &&str) = default;
inline IdString(const std::string &str) : index_(insert(std::string_view(str))) { }
inline IdString(std::string_view str) : index_(insert(str)) { }
constexpr inline IdString(StaticId id) : index_(static_cast<short>(id)) {}
@ -233,22 +241,23 @@ struct RTLIL::IdString
*this = id;
}
constexpr inline const IdString &id_string() const { return *this; }
inline const char *c_str() const {
if (index_ >= 0)
return global_id_storage_.at(index_).buf;
auto it = global_autoidx_id_storage_.find(index_);
if (it != global_autoidx_id_storage_.end())
return it->second;
const std::string &prefix = *global_autoidx_id_prefix_storage_.at(index_);
AutoidxStorage &s = global_autoidx_id_storage_.at(index_);
char *full_str = s.full_str.load(std::memory_order_acquire);
if (full_str != nullptr)
return full_str;
const std::string &prefix = *s.prefix;
std::string suffix = std::to_string(-index_);
char *c = new char[prefix.size() + suffix.size() + 1];
memcpy(c, prefix.data(), prefix.size());
memcpy(c + prefix.size(), suffix.c_str(), suffix.size() + 1);
global_autoidx_id_storage_.insert(it, {index_, c});
return c;
if (s.full_str.compare_exchange_strong(full_str, c, std::memory_order_acq_rel))
return c;
delete[] c;
return full_str;
}
inline std::string str() const {
@ -262,7 +271,7 @@ struct RTLIL::IdString
*out += global_id_storage_.at(index_).str_view();
return;
}
*out += *global_autoidx_id_prefix_storage_.at(index_);
*out += *global_autoidx_id_storage_.at(index_).prefix;
*out += std::to_string(-index_);
}
@ -348,7 +357,7 @@ struct RTLIL::IdString
if (index_ >= 0) {
return const_iterator(global_id_storage_.at(index_));
}
return const_iterator(global_autoidx_id_prefix_storage_.at(index_), -index_);
return const_iterator(global_autoidx_id_storage_.at(index_).prefix, -index_);
}
const_iterator end() const {
return const_iterator();
@ -358,10 +367,10 @@ struct RTLIL::IdString
if (index_ >= 0) {
return Substrings(global_id_storage_.at(index_));
}
return Substrings(global_autoidx_id_prefix_storage_.at(index_), -index_);
return Substrings(global_autoidx_id_storage_.at(index_).prefix, -index_);
}
inline bool lt_by_name(const IdString &rhs) const {
inline bool lt_by_name(IdString rhs) const {
Substrings lhs_it = substrings();
Substrings rhs_it = rhs.substrings();
std::string_view lhs_substr = lhs_it.first();
@ -388,12 +397,12 @@ struct RTLIL::IdString
}
}
inline bool operator<(const IdString &rhs) const {
inline bool operator<(IdString rhs) const {
return index_ < rhs.index_;
}
inline bool operator==(const IdString &rhs) const { return index_ == rhs.index_; }
inline bool operator!=(const IdString &rhs) const { return index_ != rhs.index_; }
inline bool operator==(IdString rhs) const { return index_ == rhs.index_; }
inline bool operator!=(IdString rhs) const { return index_ != rhs.index_; }
// The methods below are just convenience functions for better compatibility with std::string.
@ -411,7 +420,7 @@ struct RTLIL::IdString
#endif
return *(storage.buf + i);
}
const std::string &id_start = *global_autoidx_id_prefix_storage_.at(index_);
const std::string &id_start = *global_autoidx_id_storage_.at(index_).prefix;
if (i < id_start.size())
return id_start[i];
i -= id_start.size();
@ -517,7 +526,7 @@ struct RTLIL::IdString
return (... || in(args));
}
bool in(const IdString &rhs) const { return *this == rhs; }
bool in(IdString rhs) const { return *this == rhs; }
bool in(const char *rhs) const { return *this == rhs; }
bool in(const std::string &rhs) const { return *this == rhs; }
inline bool in(const pool<IdString> &rhs) const;
@ -597,7 +606,8 @@ private:
}
static void get_reference(int idx)
{
#ifndef YOSYS_NO_IDS_REFCNT
log_assert(!Multithreading::active());
if (idx < static_cast<short>(StaticId::STATIC_ID_END))
return;
auto it = global_refcount_storage_.find(idx);
@ -605,7 +615,6 @@ private:
global_refcount_storage_.insert(it, {idx, 1});
else
++it->second;
#endif
#ifdef YOSYS_XTRACE_GET_PUT
if (yosys_xtrace && idx >= static_cast<short>(StaticId::STATIC_ID_END))
log("#X# GET-BY-INDEX '%s' (index %d, refcount %u)\n", from_index(idx), idx, refcount(idx));
@ -614,7 +623,8 @@ private:
void put_reference()
{
#ifndef YOSYS_NO_IDS_REFCNT
log_assert(!Multithreading::active());
// put_reference() may be called from destructors after the destructor of
// global_refcount_storage_ has been run. in this case we simply do nothing.
if (index_ < static_cast<short>(StaticId::STATIC_ID_END) || !destruct_guard_ok)
@ -628,20 +638,19 @@ private:
if (--it->second == 0) {
global_refcount_storage_.erase(it);
}
#endif
}
};
namespace hashlib {
template <>
struct hash_ops<RTLIL::IdString> {
static inline bool cmp(const RTLIL::IdString &a, const RTLIL::IdString &b) {
static inline bool cmp(RTLIL::IdString a, RTLIL::IdString b) {
return a == b;
}
[[nodiscard]] static inline Hasher hash(const RTLIL::IdString &id) {
[[nodiscard]] static inline Hasher hash(RTLIL::IdString id) {
return id.hash_top();
}
[[nodiscard]] static inline Hasher hash_into(const RTLIL::IdString &id, Hasher h) {
[[nodiscard]] static inline Hasher hash_into(RTLIL::IdString id, Hasher h) {
return id.hash_into(h);
}
};
@ -748,11 +757,11 @@ namespace RTLIL {
return str.substr(1);
}
static inline std::string unescape_id(const RTLIL::IdString &str) {
static inline std::string unescape_id(RTLIL::IdString str) {
return unescape_id(str.str());
}
static inline const char *id2cstr(const RTLIL::IdString &str) {
static inline const char *id2cstr(RTLIL::IdString str) {
return log_id(str);
}
@ -769,7 +778,7 @@ namespace RTLIL {
};
struct sort_by_id_str {
bool operator()(const RTLIL::IdString &a, const RTLIL::IdString &b) const {
bool operator()(RTLIL::IdString a, RTLIL::IdString b) const {
return a.lt_by_name(b);
}
};
@ -1235,22 +1244,22 @@ struct RTLIL::AttrObject
{
dict<RTLIL::IdString, RTLIL::Const> attributes;
bool has_attribute(const RTLIL::IdString &id) const;
bool has_attribute(RTLIL::IdString id) const;
void set_bool_attribute(const RTLIL::IdString &id, bool value=true);
bool get_bool_attribute(const RTLIL::IdString &id) const;
void set_bool_attribute(RTLIL::IdString id, bool value=true);
bool get_bool_attribute(RTLIL::IdString id) const;
[[deprecated("Use Module::get_blackbox_attribute() instead.")]]
bool get_blackbox_attribute(bool ignore_wb=false) const {
return get_bool_attribute(ID::blackbox) || (!ignore_wb && get_bool_attribute(ID::whitebox));
}
void set_string_attribute(const RTLIL::IdString& id, string value);
string get_string_attribute(const RTLIL::IdString &id) const;
void set_string_attribute(RTLIL::IdString id, string value);
string get_string_attribute(RTLIL::IdString id) const;
void set_strpool_attribute(const RTLIL::IdString& id, const pool<string> &data);
void add_strpool_attribute(const RTLIL::IdString& id, const pool<string> &data);
pool<string> get_strpool_attribute(const RTLIL::IdString &id) const;
void set_strpool_attribute(RTLIL::IdString id, const pool<string> &data);
void add_strpool_attribute(RTLIL::IdString id, const pool<string> &data);
pool<string> get_strpool_attribute(RTLIL::IdString id) const;
void set_src_attribute(const std::string &src) {
set_string_attribute(ID::src, src);
@ -1262,8 +1271,8 @@ struct RTLIL::AttrObject
void set_hdlname_attribute(const vector<string> &hierarchy);
vector<string> get_hdlname_attribute() const;
void set_intvec_attribute(const RTLIL::IdString& id, const vector<int> &data);
vector<int> get_intvec_attribute(const RTLIL::IdString &id) const;
void set_intvec_attribute(RTLIL::IdString id, const vector<int> &data);
vector<int> get_intvec_attribute(RTLIL::IdString id) const;
};
struct RTLIL::NamedObject : public RTLIL::AttrObject
@ -1770,18 +1779,18 @@ struct RTLIL::Selection
// checks if the given module exists in the current design and is a
// boxed module, warning the user if the current design is not set
bool boxed_module(const RTLIL::IdString &mod_name) const;
bool boxed_module(RTLIL::IdString mod_name) const;
// checks if the given module is included in this selection
bool selected_module(const RTLIL::IdString &mod_name) const;
bool selected_module(RTLIL::IdString mod_name) const;
// checks if the given module is wholly included in this selection,
// i.e. not partially selected
bool selected_whole_module(const RTLIL::IdString &mod_name) const;
bool selected_whole_module(RTLIL::IdString mod_name) const;
// checks if the given member from the given module is included in this
// selection
bool selected_member(const RTLIL::IdString &mod_name, const RTLIL::IdString &memb_name) const;
bool selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) const;
// optimizes this selection for the given design by:
// - removing non-existent modules and members, any boxed modules and
@ -1851,7 +1860,7 @@ struct RTLIL::Monitor
virtual ~Monitor() { }
virtual void notify_module_add(RTLIL::Module*) { }
virtual void notify_module_del(RTLIL::Module*) { }
virtual void notify_connect(RTLIL::Cell*, const RTLIL::IdString&, const RTLIL::SigSpec&, const RTLIL::SigSpec&) { }
virtual void notify_connect(RTLIL::Cell*, RTLIL::IdString, const RTLIL::SigSpec&, const RTLIL::SigSpec&) { }
virtual void notify_connect(RTLIL::Module*, const RTLIL::SigSig&) { }
virtual void notify_connect(RTLIL::Module*, const std::vector<RTLIL::SigSig>&) { }
virtual void notify_blackout(RTLIL::Module*) { }
@ -1886,11 +1895,11 @@ struct RTLIL::Design
~Design();
RTLIL::ObjRange<RTLIL::Module*> modules();
RTLIL::Module *module(const RTLIL::IdString &name);
const RTLIL::Module *module(const RTLIL::IdString &name) const;
RTLIL::Module *module(RTLIL::IdString name);
const RTLIL::Module *module(RTLIL::IdString name) const;
RTLIL::Module *top_module() const;
bool has(const RTLIL::IdString &id) const {
bool has(RTLIL::IdString id) const {
return modules_.count(id) != 0;
}
@ -1917,15 +1926,15 @@ struct RTLIL::Design
void optimize();
// checks if the given module is included in the current selection
bool selected_module(const RTLIL::IdString &mod_name) const;
bool selected_module(RTLIL::IdString mod_name) const;
// checks if the given module is wholly included in the current
// selection, i.e. not partially selected
bool selected_whole_module(const RTLIL::IdString &mod_name) const;
bool selected_whole_module(RTLIL::IdString mod_name) const;
// checks if the given member from the given module is included in the
// current selection
bool selected_member(const RTLIL::IdString &mod_name, const RTLIL::IdString &memb_name) const;
bool selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) const;
// checks if the given module is included in the current selection
bool selected_module(RTLIL::Module *mod) const;
@ -2022,7 +2031,10 @@ struct RTLIL::Design
// returns all selected unboxed whole modules, warning the user if any
// partially selected or boxed modules have been ignored
std::vector<RTLIL::Module*> selected_unboxed_whole_modules_warn() const { return selected_modules(SELECT_WHOLE_WARN, SB_UNBOXED_WARN); }
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
@ -2057,7 +2069,7 @@ public:
virtual ~Module();
virtual RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, bool mayfail = false);
virtual RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, const dict<RTLIL::IdString, RTLIL::Module*> &interfaces, const dict<RTLIL::IdString, RTLIL::IdString> &modports, bool mayfail = false);
virtual size_t count_id(const RTLIL::IdString& id);
virtual size_t count_id(RTLIL::IdString id);
virtual void expand_interfaces(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces);
virtual bool reprocess_if_necessary(RTLIL::Design *design);
@ -2109,32 +2121,37 @@ public:
return design->selected_member(name, member->name);
}
RTLIL::Wire* wire(const RTLIL::IdString &id) {
RTLIL::Wire* wire(RTLIL::IdString id) {
auto it = wires_.find(id);
return it == wires_.end() ? nullptr : it->second;
}
RTLIL::Cell* cell(const RTLIL::IdString &id) {
RTLIL::Cell* cell(RTLIL::IdString id) {
auto it = cells_.find(id);
return it == cells_.end() ? nullptr : it->second;
}
const RTLIL::Wire* wire(const RTLIL::IdString &id) const{
const RTLIL::Wire* wire(RTLIL::IdString id) const{
auto it = wires_.find(id);
return it == wires_.end() ? nullptr : it->second;
}
const RTLIL::Cell* cell(const RTLIL::IdString &id) const {
const RTLIL::Cell* cell(RTLIL::IdString id) const {
auto it = cells_.find(id);
return it == cells_.end() ? nullptr : it->second;
}
RTLIL::ObjRange<RTLIL::Wire*> wires() { return RTLIL::ObjRange<RTLIL::Wire*>(&wires_, &refcount_wires_); }
int wires_size() const { return wires_.size(); }
RTLIL::Wire* wire_at(int index) const { return wires_.element(index)->second; }
RTLIL::ObjRange<RTLIL::Cell*> cells() { return RTLIL::ObjRange<RTLIL::Cell*>(&cells_, &refcount_cells_); }
int cells_size() const { return cells_.size(); }
RTLIL::Cell* cell_at(int index) const { return cells_.element(index)->second; }
void add(RTLIL::Binding *binding);
// Removing wires is expensive. If you have to remove wires, remove them all at once.
void remove(const pool<RTLIL::Wire*> &wires);
void remove(RTLIL::Cell *cell);
void remove(RTLIL::Memory *memory);
void remove(RTLIL::Process *process);
void rename(RTLIL::Wire *wire, RTLIL::IdString new_name);
@ -2381,6 +2398,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 to_rtlil_str() const;
#ifdef YOSYS_ENABLE_PYTHON
static std::map<unsigned int, RTLIL::Module*> *get_all_modules(void);
#endif
@ -2434,6 +2452,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
@ -2451,6 +2470,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);
@ -2479,23 +2500,23 @@ public:
dict<RTLIL::IdString, RTLIL::Const> parameters;
// access cell ports
bool hasPort(const RTLIL::IdString &portname) const;
void unsetPort(const RTLIL::IdString &portname);
void setPort(const RTLIL::IdString &portname, RTLIL::SigSpec signal);
const RTLIL::SigSpec &getPort(const RTLIL::IdString &portname) const;
bool hasPort(RTLIL::IdString portname) const;
void unsetPort(RTLIL::IdString portname);
void setPort(RTLIL::IdString portname, RTLIL::SigSpec signal);
const RTLIL::SigSpec &getPort(RTLIL::IdString portname) const;
const dict<RTLIL::IdString, RTLIL::SigSpec> &connections() const;
// information about cell ports
bool known() const;
bool input(const RTLIL::IdString &portname) const;
bool output(const RTLIL::IdString &portname) const;
PortDir port_dir(const RTLIL::IdString &portname) const;
bool input(RTLIL::IdString portname) const;
bool output(RTLIL::IdString portname) const;
PortDir port_dir(RTLIL::IdString portname) const;
// access cell parameters
bool hasParam(const RTLIL::IdString &paramname) const;
void unsetParam(const RTLIL::IdString &paramname);
void setParam(const RTLIL::IdString &paramname, RTLIL::Const value);
const RTLIL::Const &getParam(const RTLIL::IdString &paramname) const;
bool hasParam(RTLIL::IdString paramname) const;
void unsetParam(RTLIL::IdString paramname);
void setParam(RTLIL::IdString paramname, RTLIL::Const value);
const RTLIL::Const &getParam(RTLIL::IdString paramname) const;
void sort();
void check();
@ -2509,6 +2530,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
@ -2587,6 +2610,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;
};

View file

@ -526,7 +526,7 @@ void RTLIL::Module::bufNormalize()
pending_deleted_cells.clear();
}
void RTLIL::Cell::unsetPort(const RTLIL::IdString& portname)
void RTLIL::Cell::unsetPort(RTLIL::IdString portname)
{
RTLIL::SigSpec signal;
auto conn_it = connections_.find(portname);
@ -586,7 +586,7 @@ void RTLIL::Cell::unsetPort(const RTLIL::IdString& portname)
}
}
void RTLIL::Cell::setPort(const RTLIL::IdString& portname, RTLIL::SigSpec signal)
void RTLIL::Cell::setPort(RTLIL::IdString portname, RTLIL::SigSpec signal)
{
auto r = connections_.insert(portname);
auto conn_it = r.first;

View file

@ -59,6 +59,7 @@ struct SatSolver
struct ezSatPtr : public std::unique_ptr<ezSAT> {
ezSatPtr() : unique_ptr<ezSAT>(yosys_satsolver->create()) { }
explicit ezSatPtr(SatSolver *solver) : unique_ptr<ezSAT>((solver ? solver : yosys_satsolver)->create()) { }
};
struct SatGen

View file

@ -97,13 +97,13 @@ static const char *attr_prefix(ScopeinfoAttrs attrs)
}
}
bool scopeinfo_has_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, const RTLIL::IdString &id)
bool scopeinfo_has_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, RTLIL::IdString id)
{
log_assert(scopeinfo->type == ID($scopeinfo));
return scopeinfo->has_attribute(attr_prefix(attrs) + RTLIL::unescape_id(id));
}
RTLIL::Const scopeinfo_get_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, const RTLIL::IdString &id)
RTLIL::Const scopeinfo_get_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, RTLIL::IdString id)
{
log_assert(scopeinfo->type == ID($scopeinfo));
auto found = scopeinfo->attributes.find(attr_prefix(attrs) + RTLIL::unescape_id(id));

View file

@ -433,10 +433,10 @@ enum class ScopeinfoAttrs {
};
// Check whether the flattened module or flattened cell corresponding to a $scopeinfo cell had a specific attribute.
bool scopeinfo_has_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, const RTLIL::IdString &id);
bool scopeinfo_has_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, RTLIL::IdString id);
// Get a specific attribute from the flattened module or flattened cell corresponding to a $scopeinfo cell.
RTLIL::Const scopeinfo_get_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, const RTLIL::IdString &id);
RTLIL::Const scopeinfo_get_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, RTLIL::IdString id);
// Get all attribute from the flattened module or flattened cell corresponding to a $scopeinfo cell.
dict<RTLIL::IdString, RTLIL::Const> scopeinfo_attributes(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs);

View file

@ -3,6 +3,20 @@
YOSYS_NAMESPACE_BEGIN
static int init_max_threads()
{
const char *v = getenv("YOSYS_MAX_THREADS");
if (v == nullptr)
return INT32_MAX;
return atoi(v);
}
static int get_max_threads()
{
static int max_threads = init_max_threads();
return max_threads;
}
void DeferredLogs::flush()
{
for (auto &m : logs)
@ -12,10 +26,11 @@ void DeferredLogs::flush()
YOSYS_NAMESPACE_PREFIX log("%s", m.text.c_str());
}
int ThreadPool::pool_size(int reserved_cores, int max_threads)
int ThreadPool::pool_size(int reserved_cores, int max_worker_threads)
{
#ifdef YOSYS_ENABLE_THREADS
int num_threads = std::min<int>(std::thread::hardware_concurrency() - reserved_cores, max_threads);
int available_threads = std::min<int>(std::thread::hardware_concurrency(), get_max_threads());
int num_threads = std::min(available_threads - reserved_cores, max_worker_threads);
return std::max(0, num_threads);
#else
return 0;

View file

@ -127,9 +127,9 @@ class ThreadPool
public:
// Computes the number of worker threads to use.
// `reserved_cores` cores are set aside for other threads (e.g. work on the main thread).
// `max_threads` --- don't return more workers than this.
// `max_worker_threads` --- don't return more workers than this.
// The result may be 0.
static int pool_size(int reserved_cores, int max_threads);
static int pool_size(int reserved_cores, int max_worker_threads);
// Create a pool of threads running the given closure (parameterized by thread number).
// `pool_size` must be the result of a `pool_size()` call.

View file

@ -19,6 +19,7 @@
#include "kernel/yosys.h"
#include "kernel/newcelltypes.h"
#include "kernel/log.h"
#ifdef YOSYS_ENABLE_READLINE
# include <readline/readline.h>
@ -80,7 +81,7 @@ extern "C" PyObject* PyInit_pyosys();
YOSYS_NAMESPACE_BEGIN
int autoidx = 1;
Autoidx autoidx(1);
int yosys_xtrace = 0;
bool yosys_write_versions = true;
const char* yosys_maybe_version() {
@ -108,9 +109,30 @@ uint32_t Hasher::fudge = 0;
std::string yosys_share_dirname;
std::string yosys_abc_executable;
bool Multithreading::active_ = false;
void init_share_dirname();
void init_abc_executable_name();
Multithreading::Multithreading() {
log_assert(!active_);
active_ = true;
}
Multithreading::~Multithreading() {
log_assert(active_);
active_ = false;
}
void Autoidx::ensure_at_least(int v) {
value = std::max(value, v);
}
int Autoidx::operator++(int) {
log_assert(!Multithreading::active());
return value++;
}
void memhasher_on()
{
#if defined(__linux__) || defined(__FreeBSD__)
@ -151,7 +173,7 @@ void yosys_banner()
log("\n");
log(" /----------------------------------------------------------------------------\\\n");
log(" | yosys -- Yosys Open SYnthesis Suite |\n");
log(" | Copyright (C) 2012 - 2025 Claire Xenia Wolf <claire@yosyshq.com> |\n");
log(" | Copyright (C) 2012 - 2026 Claire Xenia Wolf <claire@yosyshq.com> |\n");
log(" | Distributed under an ISC-like license, type \"license\" to see terms |\n");
log(" \\----------------------------------------------------------------------------/\n");
log(" %s\n", yosys_maybe_version());

View file

@ -81,6 +81,7 @@ extern std::set<std::string> yosys_input_files, yosys_output_files;
// from kernel/version_*.o (cc source generated from Makefile)
extern const char *yosys_version_str;
extern const char *yosys_git_hash_str;
const char* yosys_maybe_version();
// from passes/cmds/design.cc

View file

@ -267,7 +267,30 @@ int ceil_log2(int x) YS_ATTRIBUTE(const);
template<typename T> int GetSize(const T &obj) { return obj.size(); }
inline int GetSize(RTLIL::Wire *wire);
extern int autoidx;
// When multiple threads are accessing RTLIL, one of these guard objects
// must exist.
struct Multithreading
{
Multithreading();
~Multithreading();
// Returns true when multiple threads are accessing RTLIL.
// autoidx cannot be used during such times.
// IdStrings cannot be created during such times.
static bool active() { return active_; }
private:
static bool active_;
};
struct Autoidx {
Autoidx(int value) : value(value) {}
operator int() const { return value; }
void ensure_at_least(int v);
int operator++(int);
private:
int value;
};
extern Autoidx autoidx;
extern int yosys_xtrace;
extern bool yosys_write_versions;
@ -276,8 +299,8 @@ RTLIL::IdString new_id_suffix(std::string_view file, int line, std::string_view
#define NEW_ID \
YOSYS_NAMESPACE_PREFIX RTLIL::IdString::new_autoidx_with_prefix([](std::string_view func) -> const std::string * { \
static const std::string *prefix = YOSYS_NAMESPACE_PREFIX create_id_prefix(__FILE__, __LINE__, func); \
return prefix; \
static std::unique_ptr<const std::string> prefix(YOSYS_NAMESPACE_PREFIX create_id_prefix(__FILE__, __LINE__, func)); \
return prefix.get(); \
}(__FUNCTION__))
#define NEW_ID_SUFFIX(suffix) \
YOSYS_NAMESPACE_PREFIX new_id_suffix(__FILE__, __LINE__, __FUNCTION__, suffix)

92
libs/ezsat/ezcmdline.cc Normal file
View file

@ -0,0 +1,92 @@
#include "ezcmdline.h"
#include "../../kernel/yosys.h"
ezCmdlineSAT::ezCmdlineSAT(const std::string &cmd) : command(cmd) {}
ezCmdlineSAT::~ezCmdlineSAT() {}
bool ezCmdlineSAT::solver(const std::vector<int> &modelExpressions, std::vector<bool> &modelValues, const std::vector<int> &assumptions)
{
#if !defined(YOSYS_DISABLE_SPAWN)
const std::string tempdir_name = Yosys::make_temp_dir(Yosys::get_base_tmpdir() + "/yosys-sat-XXXXXX");
const std::string cnf_filename = Yosys::stringf("%s/problem.cnf", tempdir_name.c_str());
const std::string sat_command = Yosys::stringf("%s %s", command.c_str(), cnf_filename.c_str());
FILE *dimacs = fopen(cnf_filename.c_str(), "w");
if (dimacs == nullptr) {
Yosys::log_cmd_error("Failed to create CNF file `%s`.\n", cnf_filename.c_str());
}
std::vector<int> modelIdx;
for (auto id : modelExpressions)
modelIdx.push_back(bind(id));
std::vector<std::vector<int>> extraClauses;
for (auto id : assumptions)
extraClauses.push_back({bind(id)});
printDIMACS(dimacs, false, extraClauses);
fclose(dimacs);
bool status_sat = false;
bool status_unsat = false;
std::vector<bool> values;
auto line_callback = [&](const std::string &line) {
if (line.empty()) {
return;
}
if (line[0] == 's') {
if (line.substr(0, 5) == "s SAT") {
status_sat = true;
}
if (line.substr(0, 7) == "s UNSAT") {
status_unsat = true;
}
return;
}
if (line[0] == 'v') {
std::stringstream ss(line.substr(1));
int lit;
while (ss >> lit) {
if (lit == 0) {
return;
}
bool val = lit >= 0;
int ind = lit >= 0 ? lit - 1 : -lit - 1;
if (Yosys::GetSize(values) <= ind) {
values.resize(ind + 1);
}
values[ind] = val;
}
}
};
int return_code = Yosys::run_command(sat_command, line_callback);
if (return_code != 0 && return_code != 10 && return_code != 20) {
Yosys::log_cmd_error("Shell command failed!\n");
}
modelValues.clear();
modelValues.resize(modelIdx.size());
if (!status_sat && !status_unsat) {
solverTimeoutStatus = true;
}
if (!status_sat) {
return false;
}
for (size_t i = 0; i < modelIdx.size(); i++) {
int idx = modelIdx[i];
bool refvalue = true;
if (idx < 0)
idx = -idx, refvalue = false;
modelValues[i] = (values.at(idx - 1) == refvalue);
}
return true;
#else
Yosys::log_error("SAT solver command not available in this build!\n");
#endif
}

36
libs/ezsat/ezcmdline.h Normal file
View file

@ -0,0 +1,36 @@
/*
* ezSAT -- A simple and easy to use CNF generator for SAT solvers
*
* Copyright (C) 2013 Claire Xenia Wolf <claire@yosyshq.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.
*
*/
#ifndef EZSATCOMMAND_H
#define EZSATCOMMAND_H
#include "ezsat.h"
class ezCmdlineSAT : public ezSAT
{
private:
std::string command;
public:
ezCmdlineSAT(const std::string &cmd);
virtual ~ezCmdlineSAT();
bool solver(const std::vector<int> &modelExpressions, std::vector<bool> &modelValues, const std::vector<int> &assumptions) override;
};
#endif

View file

@ -103,7 +103,7 @@ bool ezMiniSAT::solver(const std::vector<int> &modelExpressions, std::vector<boo
{
preSolverCallback();
solverTimoutStatus = false;
solverTimeoutStatus = false;
if (0) {
contradiction:
@ -206,7 +206,7 @@ contradiction:
#if defined(HAS_ALARM)
if (solverTimeout > 0) {
if (alarmHandlerTimeout == 0)
solverTimoutStatus = true;
solverTimeoutStatus = true;
alarm(0);
sigaction(SIGALRM, &old_sig_action, NULL);
alarm(old_alarm_timeout);

View file

@ -54,7 +54,7 @@ ezSAT::ezSAT()
cnfClausesCount = 0;
solverTimeout = 0;
solverTimoutStatus = false;
solverTimeoutStatus = false;
literal("CONST_TRUE");
literal("CONST_FALSE");
@ -1222,10 +1222,15 @@ ezSATvec ezSAT::vec(const std::vector<int> &vec)
return ezSATvec(*this, vec);
}
void ezSAT::printDIMACS(FILE *f, bool verbose) const
void ezSAT::printDIMACS(FILE *f, bool verbose, const std::vector<std::vector<int>> &extraClauses) const
{
if (f == nullptr) {
fprintf(stderr, "Usage error: printDIMACS() must not be called with a null FILE pointer\n");
abort();
}
if (cnfConsumed) {
fprintf(stderr, "Usage error: printDIMACS() must not be called after cnfConsumed()!");
fprintf(stderr, "Usage error: printDIMACS() must not be called after cnfConsumed()!\n");
abort();
}
@ -1259,8 +1264,10 @@ void ezSAT::printDIMACS(FILE *f, bool verbose) const
std::vector<std::vector<int>> all_clauses;
getFullCnf(all_clauses);
assert(cnfClausesCount == int(all_clauses.size()));
for (auto c : extraClauses)
all_clauses.push_back(c);
fprintf(f, "p cnf %d %d\n", cnfVariableCount, cnfClausesCount);
fprintf(f, "p cnf %d %d\n", cnfVariableCount, (int) all_clauses.size());
int maxClauseLen = 0;
for (auto &clause : all_clauses)
maxClauseLen = std::max(int(clause.size()), maxClauseLen);

View file

@ -78,7 +78,7 @@ protected:
public:
int solverTimeout;
bool solverTimoutStatus;
bool solverTimeoutStatus;
ezSAT();
virtual ~ezSAT();
@ -153,8 +153,8 @@ public:
solverTimeout = newTimeoutSeconds;
}
bool getSolverTimoutStatus() {
return solverTimoutStatus;
bool getSolverTimeoutStatus() {
return solverTimeoutStatus;
}
// manage CNF (usually only accessed by SAT solvers)
@ -295,7 +295,7 @@ public:
// printing CNF and internal state
void printDIMACS(FILE *f, bool verbose = false) const;
void printDIMACS(FILE *f, bool verbose = false, const std::vector<std::vector<int>> &extraClauses = std::vector<std::vector<int>>()) const;
void printInternalState(FILE *f) const;
// more sophisticated constraints (designed to be used directly with assume(..))

View file

@ -912,6 +912,10 @@ class SubCircuit::SolverWorker
bool pruneEnumerationMatrix(std::vector<std::set<int>> &enumerationMatrix, const GraphData &needle, const GraphData &haystack, int &nextRow, bool allowOverlap)
{
bool didSomething = true;
// Map of j:[i where j is used]
std::map<int, std::set<int>> usedNodes;
while (didSomething)
{
nextRow = -1;
@ -923,13 +927,23 @@ class SubCircuit::SolverWorker
didSomething = true;
else if (!allowOverlap && haystack.usedNodes[j])
didSomething = true;
else
else {
newRow.insert(j);
usedNodes[j].insert(i); // Store the needle index by haystack node index
}
}
// This indicates there are no available haystack nodes to assign to the needle
if (newRow.size() == 0)
return false;
// If there are multiple needles assigned to the haystack node, the solution is invalid
if (newRow.size() == 1 && usedNodes[*newRow.begin()].size() > 1)
return false;
if (newRow.size() >= 2 && (nextRow < 0 || needle.adjMatrix.at(nextRow).size() < needle.adjMatrix.at(i).size()))
nextRow = i;
enumerationMatrix[i].swap(newRow);
}
}

View file

@ -1,9 +1,8 @@
#!/bin/bash
set -ex
vcxsrc="$1-$2"
vcxsrc="$1"
yosysver="$2"
gitsha="$3"
rm -rf YosysVS-Tpl-v2.zip YosysVS
wget https://github.com/YosysHQ/yosys/releases/download/resources/YosysVS-Tpl-v2.zip
@ -33,7 +32,6 @@ popd
head -n$n "$vcxsrc"/YosysVS/YosysVS.vcxproj
egrep '\.(h|hh|hpp|inc)$' srcfiles.txt | sed 's,.*,<ClInclude Include="../yosys/&" />,'
egrep -v '\.(h|hh|hpp|inc)$' srcfiles.txt | sed 's,.*,<ClCompile Include="../yosys/&" />,'
echo '<ClCompile Include="../yosys/kernel/version.cc" />'
tail -n +$((n+1)) "$vcxsrc"/YosysVS/YosysVS.vcxproj
} > "$vcxsrc"/YosysVS/YosysVS.vcxproj.new
@ -48,9 +46,6 @@ mkdir -p "$vcxsrc"/yosys
tar -cf - -T srcfiles.txt | tar -xf - -C "$vcxsrc"/yosys
cp -r share "$vcxsrc"/
echo "namespace Yosys { extern const char *yosys_version_str; const char *yosys_version_str=\"Yosys" \
"$yosysver (git sha1 $gitsha, Visual Studio)\"; }" > "$vcxsrc"/yosys/kernel/version.cc
cat > "$vcxsrc"/readme-git.txt << EOT
Want to use a git working copy for the yosys source code?
Open "Git Bash" in this directory and run:

View file

@ -5,6 +5,7 @@ endif
OBJS += passes/cmds/add.o
OBJS += passes/cmds/delete.o
OBJS += passes/cmds/design.o
OBJS += passes/cmds/design_equal.o
OBJS += passes/cmds/select.o
OBJS += passes/cmds/show.o
OBJS += passes/cmds/viz.o
@ -27,7 +28,6 @@ OBJS += passes/cmds/logcmd.o
OBJS += passes/cmds/tee.o
OBJS += passes/cmds/write_file.o
OBJS += passes/cmds/connwrappers.o
OBJS += passes/cmds/cover.o
OBJS += passes/cmds/trace.o
OBJS += passes/cmds/plugin.o
OBJS += passes/cmds/check.o
@ -37,6 +37,7 @@ OBJS += passes/cmds/chformal.o
OBJS += passes/cmds/chtype.o
OBJS += passes/cmds/blackbox.o
OBJS += passes/cmds/ltp.o
OBJS += passes/cmds/linux_perf.o
ifeq ($(DISABLE_SPAWN),0)
OBJS += passes/cmds/bugpoint.o
endif

View file

@ -1,163 +0,0 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2014 Claire Xenia Wolf <claire@yosyshq.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/log_help.h"
#include <sys/types.h>
#ifndef _WIN32
# include <unistd.h>
#else
# include <io.h>
#endif
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct CoverPass : public Pass {
CoverPass() : Pass("cover", "print code coverage counters") {
internal();
}
bool formatted_help() override {
auto *help = PrettyHelp::get_current();
help->set_group("passes/status");
return false;
}
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" cover [options] [pattern]\n");
log("\n");
log("Print the code coverage counters collected using the cover() macro in the Yosys\n");
log("C++ code. This is useful to figure out what parts of Yosys are utilized by a\n");
log("test bench.\n");
log("\n");
log(" -q\n");
log(" Do not print output to the normal destination (console and/or log file)\n");
log("\n");
log(" -o file\n");
log(" Write output to this file, truncate if exists.\n");
log("\n");
log(" -a file\n");
log(" Write output to this file, append if exists.\n");
log("\n");
log(" -d dir\n");
log(" Write output to a newly created file in the specified directory.\n");
log("\n");
log("When one or more pattern (shell wildcards) are specified, then only counters\n");
log("matching at least one pattern are printed.\n");
log("\n");
log("\n");
log("It is also possible to instruct Yosys to print the coverage counters on program\n");
log("exit to a file using environment variables:\n");
log("\n");
log(" YOSYS_COVER_DIR=\"{dir-name}\" yosys {args}\n");
log("\n");
log(" This will create a file (with an auto-generated name) in this\n");
log(" directory and write the coverage counters to it.\n");
log("\n");
log(" YOSYS_COVER_FILE=\"{file-name}\" yosys {args}\n");
log("\n");
log(" This will append the coverage counters to the specified file.\n");
log("\n");
log("\n");
log("Hint: Use the following AWK command to consolidate Yosys coverage files:\n");
log("\n");
log(" gawk '{ p[$3] = $1; c[$3] += $2; } END { for (i in p)\n");
log(" printf \"%%-60s %%10d %%s\\n\", p[i], c[i], i; }' {files} | sort -k3\n");
log("\n");
log("\n");
log("Coverage counters are only available in Yosys for Linux.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
std::vector<FILE*> out_files;
std::vector<std::string> patterns;
bool do_log = true;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
if (args[argidx] == "-q") {
do_log = false;
continue;
}
if ((args[argidx] == "-o" || args[argidx] == "-a" || args[argidx] == "-d") && argidx+1 < args.size()) {
const char *open_mode = args[argidx] == "-a" ? "a+" : "w";
const std::string &filename = args[++argidx];
FILE *f = nullptr;
if (args[argidx-1] == "-d") {
#if defined(_WIN32) || defined(__wasm)
log_cmd_error("The 'cover -d' option is not supported on this platform.\n");
#else
char filename_buffer[4096];
snprintf(filename_buffer, 4096, "%s/yosys_cover_%d_XXXXXX.txt", filename.c_str(), getpid());
f = fdopen(mkstemps(filename_buffer, 4), "w");
#endif
} else {
f = fopen(filename.c_str(), open_mode);
}
if (f == NULL) {
for (auto f : out_files)
fclose(f);
log_cmd_error("Can't create file %s%s.\n", args[argidx-1] == "-d" ? "in directory " : "", args[argidx]);
}
out_files.push_back(f);
continue;
}
break;
}
while (argidx < args.size() && args[argidx].compare(0, 1, "-") != 0)
patterns.push_back(args[argidx++]);
extra_args(args, argidx, design);
if (do_log) {
log_header(design, "Printing code coverage counters.\n");
log("\n");
}
#if defined(YOSYS_ENABLE_COVER) && (defined(__linux__) || defined(__FreeBSD__))
for (auto &it : get_coverage_data()) {
if (!patterns.empty()) {
for (auto &p : patterns)
if (patmatch(p.c_str(), it.first.c_str()))
goto pattern_match;
continue;
}
pattern_match:
for (auto f : out_files)
fprintf(f, "%-60s %10d %s\n", it.second.first.c_str(), it.second.second, it.first.c_str());
if (do_log)
log("%-60s %10d %s\n", it.second.first, it.second.second, it.first);
}
#else
for (auto f : out_files)
fclose(f);
log_cmd_error("This version of Yosys was not built with support for code coverage counters.\n");
#endif
for (auto f : out_files)
fclose(f);
}
} CoverPass;
PRIVATE_NAMESPACE_END

367
passes/cmds/design_equal.cc Normal file
View file

@ -0,0 +1,367 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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/rtlil.h"
YOSYS_NAMESPACE_BEGIN
class ModuleComparator
{
RTLIL::Module *mod_a;
RTLIL::Module *mod_b;
public:
ModuleComparator(RTLIL::Module *mod_a, RTLIL::Module *mod_b) : mod_a(mod_a), mod_b(mod_b) {}
template <typename... Args>
[[noreturn]] void error(FmtString<TypeIdentity<Args>...> fmt, const Args &... args)
{
formatted_error(fmt.format(args...));
}
[[noreturn]]
void formatted_error(std::string err)
{
log("Module A: %s\n", log_id(mod_a->name));
log_module(mod_a, " ");
log("Module B: %s\n", log_id(mod_b->name));
log_module(mod_b, " ");
log_cmd_error("Designs are different: %s\n", err);
}
bool compare_sigbit(const RTLIL::SigBit &a, const RTLIL::SigBit &b)
{
if (a.wire == nullptr && b.wire == nullptr)
return a.data == b.data;
if (a.wire != nullptr && b.wire != nullptr)
return a.wire->name == b.wire->name && a.offset == b.offset;
return false;
}
bool compare_sigspec(const RTLIL::SigSpec &a, const RTLIL::SigSpec &b)
{
if (a.size() != b.size()) return false;
auto it_a = a.begin(), it_b = b.begin();
for (; it_a != a.end(); ++it_a, ++it_b) {
if (!compare_sigbit(*it_a, *it_b)) return false;
}
return true;
}
std::string compare_attributes(const RTLIL::AttrObject *a, const RTLIL::AttrObject *b)
{
for (const auto &it : a->attributes) {
if (b->attributes.count(it.first) == 0)
return "missing attribute " + std::string(log_id(it.first)) + " in second design";
if (it.second != b->attributes.at(it.first))
return "attribute " + std::string(log_id(it.first)) + " mismatch: " + log_const(it.second) + " != " + log_const(b->attributes.at(it.first));
}
for (const auto &it : b->attributes)
if (a->attributes.count(it.first) == 0)
return "missing attribute " + std::string(log_id(it.first)) + " in first design";
return "";
}
std::string compare_wires(const RTLIL::Wire *a, const RTLIL::Wire *b)
{
if (a->name != b->name)
return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
if (a->width != b->width)
return "width mismatch: " + std::to_string(a->width) + " != " + std::to_string(b->width);
if (a->start_offset != b->start_offset)
return "start_offset mismatch: " + std::to_string(a->start_offset) + " != " + std::to_string(b->start_offset);
if (a->port_id != b->port_id)
return "port_id mismatch: " + std::to_string(a->port_id) + " != " + std::to_string(b->port_id);
if (a->port_input != b->port_input)
return "port_input mismatch: " + std::to_string(a->port_input) + " != " + std::to_string(b->port_input);
if (a->port_output != b->port_output)
return "port_output mismatch: " + std::to_string(a->port_output) + " != " + std::to_string(b->port_output);
if (a->upto != b->upto)
return "upto mismatch: " + std::to_string(a->upto) + " != " + std::to_string(b->upto);
if (a->is_signed != b->is_signed)
return "is_signed mismatch: " + std::to_string(a->is_signed) + " != " + std::to_string(b->is_signed);
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
return mismatch;
return "";
}
void check_wires()
{
for (const auto &it : mod_a->wires_) {
if (mod_b->wires_.count(it.first) == 0)
error("Module %s missing wire %s in second design.\n", log_id(mod_a->name), log_id(it.first));
if (std::string mismatch = compare_wires(it.second, mod_b->wires_.at(it.first)); !mismatch.empty())
error("Module %s wire %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch);
}
for (const auto &it : mod_b->wires_)
if (mod_a->wires_.count(it.first) == 0)
error("Module %s missing wire %s in first design.\n", log_id(mod_b->name), log_id(it.first));
}
std::string compare_memories(const RTLIL::Memory *a, const RTLIL::Memory *b)
{
if (a->name != b->name)
return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
if (a->width != b->width)
return "width mismatch: " + std::to_string(a->width) + " != " + std::to_string(b->width);
if (a->start_offset != b->start_offset)
return "start_offset mismatch: " + std::to_string(a->start_offset) + " != " + std::to_string(b->start_offset);
if (a->size != b->size)
return "size mismatch: " + std::to_string(a->size) + " != " + std::to_string(b->size);
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
return mismatch;
return "";
}
std::string compare_cells(const RTLIL::Cell *a, const RTLIL::Cell *b)
{
if (a->name != b->name)
return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
if (a->type != b->type)
return "type mismatch: " + std::string(log_id(a->type)) + " != " + log_id(b->type);
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
return mismatch;
for (const auto &it : a->parameters) {
if (b->parameters.count(it.first) == 0)
return "parameter mismatch: missing parameter " + std::string(log_id(it.first)) + " in second design";
if (it.second != b->parameters.at(it.first))
return "parameter mismatch: " + std::string(log_id(it.first)) + " mismatch: " + log_const(it.second) + " != " + log_const(b->parameters.at(it.first));
}
for (const auto &it : b->parameters)
if (a->parameters.count(it.first) == 0)
return "parameter mismatch: missing parameter " + std::string(log_id(it.first)) + " in first design";
for (const auto &it : a->connections()) {
if (b->connections().count(it.first) == 0)
return "connection mismatch: missing connection " + std::string(log_id(it.first)) + " in second design";
if (!compare_sigspec(it.second, b->connections().at(it.first)))
return "connection " + std::string(log_id(it.first)) + " mismatch: " + log_signal(it.second) + " != " + log_signal(b->connections().at(it.first));
}
for (const auto &it : b->connections())
if (a->connections().count(it.first) == 0)
return "connection mismatch: missing connection " + std::string(log_id(it.first)) + " in first design";
return "";
}
void check_cells()
{
for (const auto &it : mod_a->cells_) {
if (mod_b->cells_.count(it.first) == 0)
error("Module %s missing cell %s in second design.\n", log_id(mod_a->name), log_id(it.first));
if (std::string mismatch = compare_cells(it.second, mod_b->cells_.at(it.first)); !mismatch.empty())
error("Module %s cell %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch);
}
for (const auto &it : mod_b->cells_)
if (mod_a->cells_.count(it.first) == 0)
error("Module %s missing cell %s in first design.\n", log_id(mod_b->name), log_id(it.first));
}
void check_memories()
{
for (const auto &it : mod_a->memories) {
if (mod_b->memories.count(it.first) == 0)
error("Module %s missing memory %s in second design.\n", log_id(mod_a->name), log_id(it.first));
if (std::string mismatch = compare_memories(it.second, mod_b->memories.at(it.first)); !mismatch.empty())
error("Module %s memory %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch);
}
for (const auto &it : mod_b->memories)
if (mod_a->memories.count(it.first) == 0)
error("Module %s missing memory %s in first design.\n", log_id(mod_b->name), log_id(it.first));
}
std::string compare_case_rules(const RTLIL::CaseRule *a, const RTLIL::CaseRule *b)
{
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty()) return mismatch;
if (a->compare.size() != b->compare.size())
return "compare size mismatch: " + std::to_string(a->compare.size()) + " != " + std::to_string(b->compare.size());
for (size_t i = 0; i < a->compare.size(); i++)
if (!compare_sigspec(a->compare[i], b->compare[i]))
return "compare " + std::to_string(i) + " mismatch: " + log_signal(a->compare[i]) + " != " + log_signal(b->compare[i]);
if (a->actions.size() != b->actions.size())
return "actions size mismatch: " + std::to_string(a->actions.size()) + " != " + std::to_string(b->actions.size());
for (size_t i = 0; i < a->actions.size(); i++) {
if (!compare_sigspec(a->actions[i].first, b->actions[i].first))
return "action " + std::to_string(i) + " first mismatch: " + log_signal(a->actions[i].first) + " != " + log_signal(b->actions[i].first);
if (!compare_sigspec(a->actions[i].second, b->actions[i].second))
return "action " + std::to_string(i) + " second mismatch: " + log_signal(a->actions[i].second) + " != " + log_signal(b->actions[i].second);
}
if (a->switches.size() != b->switches.size())
return "switches size mismatch: " + std::to_string(a->switches.size()) + " != " + std::to_string(b->switches.size());
for (size_t i = 0; i < a->switches.size(); i++)
if (std::string mismatch = compare_switch_rules(a->switches[i], b->switches[i]); !mismatch.empty())
return "switch " + std::to_string(i) + " " + mismatch;
return "";
}
std::string compare_switch_rules(const RTLIL::SwitchRule *a, const RTLIL::SwitchRule *b)
{
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
return mismatch;
if (!compare_sigspec(a->signal, b->signal))
return "signal mismatch: " + log_signal(a->signal) + " != " + log_signal(b->signal);
if (a->cases.size() != b->cases.size())
return "cases size mismatch: " + std::to_string(a->cases.size()) + " != " + std::to_string(b->cases.size());
for (size_t i = 0; i < a->cases.size(); i++)
if (std::string mismatch = compare_case_rules(a->cases[i], b->cases[i]); !mismatch.empty())
return "case " + std::to_string(i) + " " + mismatch;
return "";
}
std::string compare_sync_rules(const RTLIL::SyncRule *a, const RTLIL::SyncRule *b)
{
if (a->type != b->type)
return "type mismatch: " + std::to_string(a->type) + " != " + std::to_string(b->type);
if (!compare_sigspec(a->signal, b->signal))
return "signal mismatch: " + log_signal(a->signal) + " != " + log_signal(b->signal);
if (a->actions.size() != b->actions.size())
return "actions size mismatch: " + std::to_string(a->actions.size()) + " != " + std::to_string(b->actions.size());
for (size_t i = 0; i < a->actions.size(); i++) {
if (!compare_sigspec(a->actions[i].first, b->actions[i].first))
return "action " + std::to_string(i) + " first mismatch: " + log_signal(a->actions[i].first) + " != " + log_signal(b->actions[i].first);
if (!compare_sigspec(a->actions[i].second, b->actions[i].second))
return "action " + std::to_string(i) + " second mismatch: " + log_signal(a->actions[i].second) + " != " + log_signal(b->actions[i].second);
}
if (a->mem_write_actions.size() != b->mem_write_actions.size())
return "mem_write_actions size mismatch: " + std::to_string(a->mem_write_actions.size()) + " != " + std::to_string(b->mem_write_actions.size());
for (size_t i = 0; i < a->mem_write_actions.size(); i++) {
const auto &ma = a->mem_write_actions[i];
const auto &mb = b->mem_write_actions[i];
if (ma.memid != mb.memid)
return "mem_write_actions " + std::to_string(i) + " memid mismatch: " + log_id(ma.memid) + " != " + log_id(mb.memid);
if (!compare_sigspec(ma.address, mb.address))
return "mem_write_actions " + std::to_string(i) + " address mismatch: " + log_signal(ma.address) + " != " + log_signal(mb.address);
if (!compare_sigspec(ma.data, mb.data))
return "mem_write_actions " + std::to_string(i) + " data mismatch: " + log_signal(ma.data) + " != " + log_signal(mb.data);
if (!compare_sigspec(ma.enable, mb.enable))
return "mem_write_actions " + std::to_string(i) + " enable mismatch: " + log_signal(ma.enable) + " != " + log_signal(mb.enable);
if (ma.priority_mask != mb.priority_mask)
return "mem_write_actions " + std::to_string(i) + " priority_mask mismatch: " + log_const(ma.priority_mask) + " != " + log_const(mb.priority_mask);
if (std::string mismatch = compare_attributes(&ma, &mb); !mismatch.empty())
return "mem_write_actions " + std::to_string(i) + " " + mismatch;
}
return "";
}
std::string compare_processes(const RTLIL::Process *a, const RTLIL::Process *b)
{
if (a->name != b->name) return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
return mismatch;
if (std::string mismatch = compare_case_rules(&a->root_case, &b->root_case); !mismatch.empty())
return "case rule " + mismatch;
if (a->syncs.size() != b->syncs.size())
return "sync count mismatch: " + std::to_string(a->syncs.size()) + " != " + std::to_string(b->syncs.size());
for (size_t i = 0; i < a->syncs.size(); i++)
if (std::string mismatch = compare_sync_rules(a->syncs[i], b->syncs[i]); !mismatch.empty())
return "sync " + std::to_string(i) + " " + mismatch;
return "";
}
void check_processes()
{
for (auto &it : mod_a->processes) {
if (mod_b->processes.count(it.first) == 0)
error("Module %s missing process %s in second design.\n", log_id(mod_a->name), log_id(it.first));
if (std::string mismatch = compare_processes(it.second, mod_b->processes.at(it.first)); !mismatch.empty())
error("Module %s process %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch.c_str());
}
for (auto &it : mod_b->processes)
if (mod_a->processes.count(it.first) == 0)
error("Module %s missing process %s in first design.\n", log_id(mod_b->name), log_id(it.first));
}
void check_connections()
{
const auto &conns_a = mod_a->connections();
const auto &conns_b = mod_b->connections();
if (conns_a.size() != conns_b.size()) {
error("Module %s connection count differs: %zu != %zu\n", log_id(mod_a->name), conns_a.size(), conns_b.size());
} else {
for (size_t i = 0; i < conns_a.size(); i++) {
if (!compare_sigspec(conns_a[i].first, conns_b[i].first))
error("Module %s connection %zu LHS %s != %s.\n", log_id(mod_a->name), i, log_signal(conns_a[i].first), log_signal(conns_b[i].first));
if (!compare_sigspec(conns_a[i].second, conns_b[i].second))
error("Module %s connection %zu RHS %s != %s.\n", log_id(mod_a->name), i, log_signal(conns_a[i].second), log_signal(conns_b[i].second));
}
}
}
void check()
{
if (mod_a->name != mod_b->name)
error("Modules have different names: %s != %s\n", log_id(mod_a->name), log_id(mod_b->name));
if (std::string mismatch = compare_attributes(mod_a, mod_b); !mismatch.empty())
error("Module %s %s.\n", log_id(mod_a->name), mismatch);
check_wires();
check_cells();
check_memories();
check_connections();
check_processes();
}
};
struct DesignEqualPass : public Pass {
DesignEqualPass() : Pass("design_equal", "check if two designs are the same") { }
void help() override
{
log("\n");
log(" design_equal <name>\n");
log("\n");
log("Compare the current design with the design previously saved under the given\n");
log("name. Abort with an error if the designs are different.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
if (args.size() != 2)
log_cmd_error("Missing argument.\n");
std::string check_name = args[1];
if (saved_designs.count(check_name) == 0)
log_cmd_error("No saved design '%s' found!\n", check_name.c_str());
RTLIL::Design *other = saved_designs.at(check_name);
for (auto &it : design->modules_) {
RTLIL::Module *mod = it.second;
if (!other->has(mod->name))
log_error("Second design missing module %s.\n", log_id(mod->name));
ModuleComparator cmp(mod, other->module(mod->name));
cmp.check();
}
for (auto &it : other->modules_) {
RTLIL::Module *mod = it.second;
if (!design->has(mod->name))
log_error("First design missing module %s.\n", log_id(mod->name));
}
log("Designs are identical.\n");
}
} DesignEqualPass;
YOSYS_NAMESPACE_END

View file

@ -77,7 +77,7 @@ struct ExampleDtPass : public Pass
auto enqueue = [&](DriveSpec const &spec) {
int index = queue(spec);
if (index == GetSize(graph_nodes))
graph_nodes.emplace_back(compute_graph.add(ID($pending).id_string(), index).index());
graph_nodes.emplace_back(compute_graph.add(ID($pending), index).index());
//if (index >= GetSize(graph_nodes))
return compute_graph[graph_nodes[index]];
};

View file

@ -163,7 +163,6 @@ struct IcellLiberty : Pass {
log_header(d, "Executing ICELL_LIBERTY pass.\n");
size_t argidx;
IdString naming_attr;
std::string liberty_filename;
auto liberty_file = std::make_unique<std::ofstream>();

102
passes/cmds/linux_perf.cc Normal file
View file

@ -0,0 +1,102 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2014 Claire Xenia Wolf <claire@yosyshq.com>
* Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.at>
*
* 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/log_help.h"
#include <fcntl.h>
#include <stdlib.h>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
#ifdef __linux__
#include <unistd.h>
struct LinuxPerf : public Pass {
LinuxPerf() : Pass("linux_perf", "turn linux perf recording off or on") {
internal();
}
bool formatted_help() override
{
auto *help = PrettyHelp::get_current();
auto content_root = help->get_root();
content_root->usage("linux_perf [on|off]");
content_root->paragraph(
"This pass turns Linux 'perf' profiling on or off, when it has been configured to use control FIFOs."
"YOSYS_PERF_CTL and YOSYS_PERF_ACK must point to Linux perf control FIFOs."
);
content_root->paragraph("Example shell command line:");
content_root->codeblock(
"mkfifo /tmp/perf.fifo /tmp/perf-ack.fifo\n"
"YOSYS_PERF_CTL=/tmp/perf.fifo YOSYS_PERF_ACK=/tmp/perf-ack.fifo \\\n"
" perf record --latency --delay=-1 \\\n"
" --control=fifo:/tmp/perf.fifo,/tmp/perf-ack.fifo --call-graph=dwarf ./yosys \\\n"
" -dt -p \"read_rtlil design.rtlil; linux_perf on; opt_clean; linux_perf off\"\n"
);
return true;
}
void execute(std::vector<std::string> args, RTLIL::Design *) override
{
if (args.size() > 2)
cmd_error(args, 2, "Unexpected argument.");
std::string_view ctl_msg;
if (args.size() == 2) {
if (args[1] == "on")
ctl_msg = "enable\n";
else if (args[1] == "off")
ctl_msg = "disable\n";
else
cmd_error(args, 1, "Unexpected argument.");
}
const char *ctl_fifo = std::getenv("YOSYS_PERF_CTL");
if (!ctl_fifo)
log_error("YOSYS_PERF_CTL environment variable not set.");
const char *ack_fifo = std::getenv("YOSYS_PERF_ACK");
if (!ack_fifo)
log_error("YOSYS_PERF_ACK environment variable not set.");
int ctl_fd = open(ctl_fifo, O_WRONLY);
if (ctl_fd < 0)
log_error("Failed to open YOSYS_PERF_CTL.");
int ack_fd = open(ack_fifo, O_RDONLY);
if (ack_fd < 0)
log_error("Failed to open YOSYS_PERF_ACK.");
int result = write(ctl_fd, ctl_msg.data(), ctl_msg.size());
if (result != static_cast<int>(ctl_msg.size()))
log_error("Failed to write to YOSYS_PERF_CTL.");
char buffer[64];
result = read(ack_fd, buffer, sizeof(buffer));
close(ctl_fd);
close(ack_fd);
if (result <= 0)
log_error("Failed to read from YOSYS_PERF_ACK.");
if (strcmp(buffer, "ack\n") != 0)
log_error("YOSYS_PERF_ACK did not return 'ack'.");
}
} LinuxPerf;
#endif
PRIVATE_NAMESPACE_END

View file

@ -45,7 +45,7 @@ struct PrintAttrsPass : public Pass {
return stringf("%*s", indent, "");
}
static void log_const(const RTLIL::IdString &s, const RTLIL::Const &x, const unsigned int indent) {
static void log_const(RTLIL::IdString s, const RTLIL::Const &x, const unsigned int indent) {
if (x.flags & RTLIL::CONST_FLAG_STRING)
log("%s(* %s=\"%s\" *)\n", get_indent_str(indent), log_id(s), x.decode_string());
else if (x.flags == RTLIL::CONST_FLAG_NONE || x.flags == RTLIL::CONST_FLAG_SIGNED)

View file

@ -37,7 +37,7 @@ struct ScratchpadPass : public Pass {
log("\n");
log(" scratchpad [options]\n");
log("\n");
log("This pass allows to read and modify values from the scratchpad of the current\n");
log("This pass allows reading and modifying values from the scratchpad of the current\n");
log("design. Options:\n");
log("\n");
log(" -get <identifier>\n");

View file

@ -8,6 +8,11 @@
#include <optional>
#include <iostream>
#if TCL_MAJOR_VERSION < 9
typedef int YS_Tcl_Size;
#else
typedef Tcl_Size YS_Tcl_Size;
#endif
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@ -160,7 +165,12 @@ struct SdcObjects {
if (!top)
log_error("Top module couldn't be determined. Check 'top' attribute usage");
for (auto port : top->ports) {
design_ports.push_back(std::make_pair(port.str().substr(1), top->wire(port)));
RTLIL::Wire *wire = top->wire(port);
if (!wire) {
// This should not be possible. See https://github.com/YosysHQ/yosys/pull/5594#issue-3791198573
log_error("Port %s doesn't exist", log_id(port));
}
design_ports.push_back(std::make_pair(port.str().substr(1), wire));
}
std::list<std::string> hierarchy{};
sniff_module(hierarchy, top);
@ -432,7 +442,7 @@ static size_t get_node_count(Tcl_Interp* interp) {
std::vector<std::vector<std::string>> gather_nested_calls(Tcl_Interp* interp) {
Tcl_Obj* listObj = Tcl_GetVar2Ex(interp, "sdc_calls", nullptr, TCL_GLOBAL_ONLY);
int listLength;
YS_Tcl_Size listLength;
std::vector<std::vector<std::string>> sdc_calls;
if (Tcl_ListObjLength(interp, listObj, &listLength) == TCL_OK) {
@ -442,7 +452,7 @@ std::vector<std::vector<std::string>> gather_nested_calls(Tcl_Interp* interp) {
if (Tcl_ListObjIndex(interp, listObj, i, &subListObj) != TCL_OK) {
log_error("broken list of lists\n");
}
int subListLength;
YS_Tcl_Size subListLength;
if (Tcl_ListObjLength(interp, subListObj, &subListLength) == TCL_OK) {
// Valid list - extract elements
for (int j = 0; j < subListLength; j++) {

View file

@ -570,7 +570,7 @@ static void select_op_expand(RTLIL::Design *design, const std::string &arg, char
ct.setup(design);
if (pos < int(arg.size()) && arg[pos] == '*') {
levels = 1000000;
levels = 1'000'000;
pos++;
} else
if (pos < int(arg.size()) && '0' <= arg[pos] && arg[pos] <= '9') {

View file

@ -36,7 +36,7 @@ struct TraceMonitor : public RTLIL::Monitor
log("#TRACE# Module delete: %s\n", log_id(module));
}
void notify_connect(RTLIL::Cell *cell, const RTLIL::IdString &port, const RTLIL::SigSpec &old_sig, const RTLIL::SigSpec &sig) override
void notify_connect(RTLIL::Cell *cell, RTLIL::IdString port, const RTLIL::SigSpec &old_sig, const RTLIL::SigSpec &sig) override
{
log("#TRACE# Cell connect: %s.%s.%s = %s (was: %s)\n", log_id(cell->module), log_id(cell), log_id(port), log_signal(sig), log_signal(old_sig));
}
@ -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 {

View file

@ -39,6 +39,7 @@ struct PassOptions {
bool no_auto_distributed;
bool no_auto_block;
bool no_auto_huge;
bool force_params;
double logic_cost_rom;
double logic_cost_ram;
};
@ -1859,7 +1860,7 @@ void MemMapping::emit_port(const MemConfig &cfg, std::vector<Cell*> &cells, cons
cell->setParam(stringf("\\PORT_%s_WR_BE_WIDTH", name), GetSize(hw_wren));
} else {
cell->setPort(stringf("\\PORT_%s_WR_EN", name), hw_wren);
if (cfg.def->byte != 0 && cfg.def->width_mode != WidthMode::Single)
if (cfg.def->byte != 0 && (cfg.def->width_mode != WidthMode::Single || opts.force_params))
cell->setParam(stringf("\\PORT_%s_WR_EN_WIDTH", name), GetSize(hw_wren));
}
}
@ -2068,8 +2069,10 @@ void MemMapping::emit(const MemConfig &cfg) {
std::vector<Cell *> cells;
for (int rd = 0; rd < cfg.repl_d; rd++) {
Cell *cell = mem.module->addCell(stringf("%s.%d.%d", mem.memid, rp, rd), cfg.def->id);
if (cfg.def->width_mode == WidthMode::Global)
if (cfg.def->width_mode == WidthMode::Global || opts.force_params)
cell->setParam(ID::WIDTH, cfg.def->dbits[cfg.base_width_log2]);
if (opts.force_params)
cell->setParam(ID::ABITS, cfg.def->abits);
if (cfg.def->widthscale) {
std::vector<State> val;
for (auto &bit: init_swz.bits[rd])
@ -2179,6 +2182,9 @@ struct MemoryLibMapPass : public Pass {
log(" Disables automatic mapping of given kind of RAMs. Manual mapping\n");
log(" (using ram_style or other attributes) is still supported.\n");
log("\n");
log(" -force-params\n");
log(" Always generate memories with WIDTH and ABITS parameters.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
@ -2188,6 +2194,7 @@ struct MemoryLibMapPass : public Pass {
opts.no_auto_distributed = false;
opts.no_auto_block = false;
opts.no_auto_huge = false;
opts.force_params = false;
opts.logic_cost_ram = 1.0;
opts.logic_cost_rom = 1.0/16.0;
log_header(design, "Executing MEMORY_LIBMAP pass (mapping memories to cells).\n");
@ -2214,6 +2221,10 @@ struct MemoryLibMapPass : public Pass {
opts.no_auto_huge = true;
continue;
}
if (args[argidx] == "-force-params") {
opts.force_params = true;
continue;
}
if (args[argidx] == "-logic-cost-rom" && argidx+1 < args.size()) {
opts.logic_cost_rom = strtod(args[++argidx].c_str(), nullptr);
continue;

View file

@ -23,6 +23,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

View file

@ -193,7 +193,6 @@ struct OptPass : public Pass {
}
design->optimize();
design->sort();
design->check();
log_header(design, "Finished fast OPT passes.%s\n", fast_mode ? "" : " (There is nothing left to do.)");

View file

@ -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

View file

@ -275,6 +275,9 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool &regs, SigPoo
return conns.check_any(s2);
}
if (w1 == w2)
return s2.offset < s1.offset;
if (w1->port_output != w2->port_output)
return w2->port_output;
@ -347,7 +350,7 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
RTLIL::Wire *wire = it.second;
for (int i = 0; i < wire->width; i++) {
RTLIL::SigBit s1 = RTLIL::SigBit(wire, i), s2 = assign_map(s1);
if (!compare_signals(s1, s2, register_signals, connected_signals, direct_wires))
if (compare_signals(s2, s1, register_signals, connected_signals, direct_wires))
assign_map.add(s1);
}
}
@ -471,8 +474,6 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
wire->attributes.erase(ID::init);
else
wire->attributes.at(ID::init) = initval;
used_signals.add(new_conn.first);
used_signals.add(new_conn.second);
module->connect(new_conn);
}
@ -713,7 +714,6 @@ struct OptCleanPass : public Pass {
log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires);
design->optimize();
design->sort();
design->check();
keep_cache.reset();
@ -773,7 +773,6 @@ struct CleanPass : public Pass {
log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires);
design->optimize();
design->sort();
design->check();
keep_cache.reset();

File diff suppressed because it is too large Load diff

View file

@ -298,8 +298,6 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
log_debug("\n");
}
cover_list("opt.opt_expr.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell->type.str());
module->remove(cell);
did_something = true;
return true;
@ -518,7 +516,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
for (auto cell : cells.sorted)
{
#define ACTION_DO(_p_, _s_) do { cover("opt.opt_expr.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0)
#define ACTION_DO(_p_, _s_) do { replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0)
#define ACTION_DO_Y(_v_) ACTION_DO(ID::Y, RTLIL::SigSpec(RTLIL::State::S ## _v_))
bool detect_const_and = false;
@ -565,19 +563,16 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
if (detect_const_and && (found_zero || found_inv || (found_undef && consume_x))) {
cover("opt.opt_expr.const_and");
replace_cell(assign_map, module, cell, "const_and", ID::Y, RTLIL::State::S0);
goto next_cell;
}
if (detect_const_or && (found_one || found_inv || (found_undef && consume_x))) {
cover("opt.opt_expr.const_or");
replace_cell(assign_map, module, cell, "const_or", ID::Y, RTLIL::State::S1);
goto next_cell;
}
if (non_const_input != State::Sm && !found_undef) {
cover("opt.opt_expr.and_or_buffer");
replace_cell(assign_map, module, cell, "and_or_buffer", ID::Y, non_const_input);
goto next_cell;
}
@ -589,12 +584,10 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
SigBit sig_b = assign_map(cell->getPort(ID::B));
if (!keepdc && (sig_a == sig_b || sig_a == State::Sx || sig_a == State::Sz || sig_b == State::Sx || sig_b == State::Sz)) {
if (cell->type.in(ID($xor), ID($_XOR_))) {
cover("opt.opt_expr.const_xor");
replace_cell(assign_map, module, cell, "const_xor", ID::Y, RTLIL::State::S0);
goto next_cell;
}
if (cell->type.in(ID($xnor), ID($_XNOR_))) {
cover("opt.opt_expr.const_xnor");
// For consistency since simplemap does $xnor -> $_XOR_ + $_NOT_
int width = GetSize(cell->getPort(ID::Y));
replace_cell(assign_map, module, cell, "const_xnor", ID::Y, SigSpec(RTLIL::State::S1, width));
@ -607,7 +600,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
std::swap(sig_a, sig_b);
if (sig_b == State::S0 || sig_b == State::S1) {
if (cell->type.in(ID($xor), ID($_XOR_))) {
cover("opt.opt_expr.xor_buffer");
SigSpec sig_y;
if (cell->type == ID($xor))
sig_y = (sig_b == State::S1 ? module->Not(NEW_ID, sig_a).as_bit() : sig_a);
@ -618,7 +610,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
goto next_cell;
}
if (cell->type.in(ID($xnor), ID($_XNOR_))) {
cover("opt.opt_expr.xnor_buffer");
SigSpec sig_y;
if (cell->type == ID($xnor)) {
sig_y = (sig_b == State::S1 ? sig_a : module->Not(NEW_ID, sig_a).as_bit());
@ -639,13 +630,11 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
GetSize(cell->getPort(ID::A)) == 1 && GetSize(cell->getPort(ID::Y)) == 1)
{
if (cell->type == ID($reduce_xnor)) {
cover("opt.opt_expr.reduce_xnor_not");
log_debug("Replacing %s cell `%s' in module `%s' with $not cell.\n",
log_id(cell->type), log_id(cell->name), log_id(module));
cell->type = ID($not);
did_something = true;
} else {
cover("opt.opt_expr.unary_buffer");
replace_cell(assign_map, module, cell, "unary_buffer", ID::Y, cell->getPort(ID::A));
}
goto next_cell;
@ -661,7 +650,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (a_fully_const != b_fully_const)
{
cover("opt.opt_expr.bitwise_logic_one_const");
log_debug("Replacing %s cell `%s' in module `%s' having one fully constant input\n",
log_id(cell->type), log_id(cell->name), log_id(module));
RTLIL::SigSpec sig_y = assign_map(cell->getPort(ID::Y));
@ -813,7 +801,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
new_sig_a.append(neutral_bit);
if (GetSize(new_sig_a) < GetSize(sig_a)) {
cover_list("opt.opt_expr.fine.neutral_A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_and", "$reduce_bool", cell->type.str());
log_debug("Replacing port A of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_sig_a));
cell->setPort(ID::A, new_sig_a);
@ -836,7 +823,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
new_sig_b.append(neutral_bit);
if (GetSize(new_sig_b) < GetSize(sig_b)) {
cover_list("opt.opt_expr.fine.neutral_B", "$logic_and", "$logic_or", cell->type.str());
log_debug("Replacing port B of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_sig_b));
cell->setPort(ID::B, new_sig_b);
@ -862,7 +848,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) {
cover("opt.opt_expr.fine.$reduce_and");
log_debug("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a));
cell->setPort(ID::A, sig_a = new_a);
@ -888,7 +873,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) {
cover_list("opt.opt_expr.fine.A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_bool", cell->type.str());
log_debug("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a));
cell->setPort(ID::A, sig_a = new_a);
@ -914,7 +898,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
if (new_b != RTLIL::State::Sm && RTLIL::SigSpec(new_b) != sig_b) {
cover_list("opt.opt_expr.fine.B", "$logic_and", "$logic_or", cell->type.str());
log_debug("Replacing port B of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_b));
cell->setPort(ID::B, sig_b = new_b);
@ -949,7 +932,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
break;
}
if (i > 0) {
cover_list("opt.opt_expr.fine", "$add", "$sub", cell->type.str());
log_debug("Stripping %d LSB bits of %s cell %s in module %s.\n", i, log_id(cell->type), log_id(cell), log_id(module));
SigSpec new_a = sig_a.extract_end(i);
SigSpec new_b = sig_b.extract_end(i);
@ -1006,7 +988,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
break;
}
if (i > 0) {
cover("opt.opt_expr.fine.$alu");
log_debug("Stripping %d LSB bits of %s cell %s in module %s.\n", i, log_id(cell->type), log_id(cell), log_id(module));
SigSpec new_a = sig_a.extract_end(i);
SigSpec new_b = sig_b.extract_end(i);
@ -1045,8 +1026,6 @@ skip_fine_alu:
if (0) {
found_the_x_bit:
cover_list("opt.opt_expr.xbit", "$reduce_xor", "$reduce_xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
"$lt", "$le", "$ge", "$gt", "$neg", "$add", "$sub", "$mul", "$div", "$mod", "$divfloor", "$modfloor", "$pow", cell->type.str());
if (cell->type.in(ID($reduce_xor), ID($reduce_xnor), ID($lt), ID($le), ID($ge), ID($gt)))
replace_cell(assign_map, module, cell, "x-bit in input", ID::Y, RTLIL::State::Sx);
else
@ -1068,7 +1047,6 @@ skip_fine_alu:
}
if (width < GetSize(sig_a)) {
cover_list("opt.opt_expr.trim", "$shiftx", "$shift", cell->type.str());
sig_a.remove(width, GetSize(sig_a)-width);
cell->setPort(ID::A, sig_a);
cell->setParam(ID::A_WIDTH, width);
@ -1079,13 +1057,11 @@ skip_fine_alu:
if (cell->type.in(ID($_NOT_), ID($not), ID($logic_not)) && GetSize(cell->getPort(ID::Y)) == 1 &&
invert_map.count(assign_map(cell->getPort(ID::A))) != 0) {
cover_list("opt.opt_expr.invert.double", "$_NOT_", "$not", "$logic_not", cell->type.str());
replace_cell(assign_map, module, cell, "double_invert", ID::Y, invert_map.at(assign_map(cell->getPort(ID::A))));
goto next_cell;
}
if (cell->type.in(ID($_MUX_), ID($mux)) && invert_map.count(assign_map(cell->getPort(ID::S))) != 0) {
cover_list("opt.opt_expr.invert.muxsel", "$_MUX_", "$mux", cell->type.str());
log_debug("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", log_id(cell->type), log_id(cell), log_id(module));
RTLIL::SigSpec tmp = cell->getPort(ID::A);
cell->setPort(ID::A, cell->getPort(ID::B));
@ -1168,7 +1144,6 @@ skip_fine_alu:
if (input.match(" 1")) ACTION_DO(ID::Y, input.extract(1, 1));
if (input.match("01 ")) ACTION_DO(ID::Y, input.extract(0, 1));
if (input.match("10 ")) {
cover("opt.opt_expr.mux_to_inv");
cell->type = ID($_NOT_);
cell->setPort(ID::A, input.extract(0, 1));
cell->unsetPort(ID::B);
@ -1195,7 +1170,6 @@ skip_fine_alu:
if (input == State::S1)
ACTION_DO(ID::Y, cell->getPort(ID::A));
if (input == State::S0 && !a.is_fully_undef()) {
cover("opt.opt_expr.action_" S__LINE__);
log_debug("Replacing data input of %s cell `%s' in module `%s' with constant undef.\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str());
cell->setPort(ID::A, SigSpec(State::Sx, GetSize(a)));
@ -1220,7 +1194,6 @@ skip_fine_alu:
log_assert(GetSize(a) == GetSize(b));
for (int i = 0; i < GetSize(a); i++) {
if (a[i].wire == NULL && b[i].wire == NULL && a[i] != b[i] && a[i].data <= RTLIL::State::S1 && b[i].data <= RTLIL::State::S1) {
cover_list("opt.opt_expr.eqneq.isneq", "$eq", "$ne", "$eqx", "$nex", cell->type.str());
RTLIL::SigSpec new_y = RTLIL::SigSpec(cell->type.in(ID($eq), ID($eqx)) ? RTLIL::State::S0 : RTLIL::State::S1);
new_y.extend_u0(cell->parameters[ID::Y_WIDTH].as_int(), false);
replace_cell(assign_map, module, cell, "isneq", ID::Y, new_y);
@ -1233,7 +1206,6 @@ skip_fine_alu:
}
if (new_a.size() == 0) {
cover_list("opt.opt_expr.eqneq.empty", "$eq", "$ne", "$eqx", "$nex", cell->type.str());
RTLIL::SigSpec new_y = RTLIL::SigSpec(cell->type.in(ID($eq), ID($eqx)) ? RTLIL::State::S1 : RTLIL::State::S0);
new_y.extend_u0(cell->parameters[ID::Y_WIDTH].as_int(), false);
replace_cell(assign_map, module, cell, "empty", ID::Y, new_y);
@ -1241,7 +1213,6 @@ skip_fine_alu:
}
if (new_a.size() < a.size() || new_b.size() < b.size()) {
cover_list("opt.opt_expr.eqneq.resize", "$eq", "$ne", "$eqx", "$nex", cell->type.str());
cell->setPort(ID::A, new_a);
cell->setPort(ID::B, new_b);
cell->parameters[ID::A_WIDTH] = new_a.size();
@ -1256,7 +1227,6 @@ skip_fine_alu:
RTLIL::SigSpec b = assign_map(cell->getPort(ID::B));
if (a.is_fully_const() && !b.is_fully_const()) {
cover_list("opt.opt_expr.eqneq.swapconst", "$eq", "$ne", cell->type.str());
cell->setPort(ID::A, b);
cell->setPort(ID::B, a);
std::swap(a, b);
@ -1271,7 +1241,6 @@ skip_fine_alu:
RTLIL::SigSpec input = b;
ACTION_DO(ID::Y, cell->getPort(ID::A));
} else {
cover_list("opt.opt_expr.eqneq.isnot", "$eq", "$ne", cell->type.str());
log_debug("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
cell->type = ID($not);
cell->parameters.erase(ID::B_WIDTH);
@ -1286,7 +1255,6 @@ skip_fine_alu:
if (cell->type.in(ID($eq), ID($ne)) &&
(assign_map(cell->getPort(ID::A)).is_fully_zero() || assign_map(cell->getPort(ID::B)).is_fully_zero()))
{
cover_list("opt.opt_expr.eqneq.cmpzero", "$eq", "$ne", cell->type.str());
log_debug("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell),
log_id(module), cell->type == ID($eq) ? "$logic_not" : "$reduce_bool");
cell->type = cell->type == ID($eq) ? ID($logic_not) : ID($reduce_bool);
@ -1334,8 +1302,6 @@ skip_fine_alu:
sig_y[i] = sig_a[GetSize(sig_a)-1];
}
cover_list("opt.opt_expr.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell->type.str());
log_debug("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n",
log_id(cell->type), log_id(cell), log_signal(assign_map(cell->getPort(ID::B))), shift_bits, log_id(module), log_signal(sig_y));
@ -1408,11 +1374,6 @@ skip_fine_alu:
if (identity_wrt_a || identity_wrt_b)
{
if (identity_wrt_a)
cover_list("opt.opt_expr.identwrt.a", "$add", "$sub", "$alu", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str());
if (identity_wrt_b)
cover_list("opt.opt_expr.identwrt.b", "$add", "$sub", "$alu", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str());
log_debug("Replacing %s cell `%s' in module `%s' with identity for port %c.\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), identity_wrt_a ? 'A' : 'B');
@ -1461,14 +1422,12 @@ skip_identity:
if (mux_bool && cell->type.in(ID($mux), ID($_MUX_)) &&
cell->getPort(ID::A) == State::S0 && cell->getPort(ID::B) == State::S1) {
cover_list("opt.opt_expr.mux_bool", "$mux", "$_MUX_", cell->type.str());
replace_cell(assign_map, module, cell, "mux_bool", ID::Y, cell->getPort(ID::S));
goto next_cell;
}
if (mux_bool && cell->type.in(ID($mux), ID($_MUX_)) &&
cell->getPort(ID::A) == State::S1 && cell->getPort(ID::B) == State::S0) {
cover_list("opt.opt_expr.mux_invert", "$mux", "$_MUX_", cell->type.str());
log_debug("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
cell->setPort(ID::A, cell->getPort(ID::S));
cell->unsetPort(ID::B);
@ -1487,7 +1446,6 @@ skip_identity:
}
if (consume_x && mux_bool && cell->type.in(ID($mux), ID($_MUX_)) && cell->getPort(ID::A) == State::S0) {
cover_list("opt.opt_expr.mux_and", "$mux", "$_MUX_", cell->type.str());
log_debug("Replacing %s cell `%s' in module `%s' with and-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
cell->setPort(ID::A, cell->getPort(ID::S));
cell->unsetPort(ID::S);
@ -1507,7 +1465,6 @@ skip_identity:
}
if (consume_x && mux_bool && cell->type.in(ID($mux), ID($_MUX_)) && cell->getPort(ID::B) == State::S1) {
cover_list("opt.opt_expr.mux_or", "$mux", "$_MUX_", cell->type.str());
log_debug("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
cell->setPort(ID::B, cell->getPort(ID::S));
cell->unsetPort(ID::S);
@ -1531,7 +1488,6 @@ skip_identity:
int width = GetSize(cell->getPort(ID::A));
if ((cell->getPort(ID::A).is_fully_undef() && cell->getPort(ID::B).is_fully_undef()) ||
cell->getPort(ID::S).is_fully_undef()) {
cover_list("opt.opt_expr.mux_undef", "$mux", "$pmux", cell->type.str());
replace_cell(assign_map, module, cell, "mux_undef", ID::Y, cell->getPort(ID::A));
goto next_cell;
}
@ -1550,17 +1506,14 @@ skip_identity:
new_s = new_s.extract(0, new_s.size()-1);
}
if (new_s.size() == 0) {
cover_list("opt.opt_expr.mux_empty", "$mux", "$pmux", cell->type.str());
replace_cell(assign_map, module, cell, "mux_empty", ID::Y, new_a);
goto next_cell;
}
if (new_a == RTLIL::SigSpec(RTLIL::State::S0) && new_b == RTLIL::SigSpec(RTLIL::State::S1)) {
cover_list("opt.opt_expr.mux_sel01", "$mux", "$pmux", cell->type.str());
replace_cell(assign_map, module, cell, "mux_sel01", ID::Y, new_s);
goto next_cell;
}
if (cell->getPort(ID::S).size() != new_s.size()) {
cover_list("opt.opt_expr.mux_reduce", "$mux", "$pmux", cell->type.str());
log_debug("Optimized away %d select inputs of %s cell `%s' in module `%s'.\n",
GetSize(cell->getPort(ID::S)) - GetSize(new_s), log_id(cell->type), log_id(cell), log_id(module));
cell->setPort(ID::A, new_a);
@ -1600,7 +1553,6 @@ skip_identity:
RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), dummy_arg, \
cell->parameters[ID::A_SIGNED].as_bool(), false, \
cell->parameters[ID::Y_WIDTH].as_int())); \
cover("opt.opt_expr.const.$" #_t); \
replace_cell(assign_map, module, cell, stringf("%s", log_signal(a)), ID::Y, y); \
goto next_cell; \
} \
@ -1615,7 +1567,6 @@ skip_identity:
cell->parameters[ID::A_SIGNED].as_bool(), \
cell->parameters[ID::B_SIGNED].as_bool(), \
cell->parameters[ID::Y_WIDTH].as_int())); \
cover("opt.opt_expr.const.$" #_t); \
replace_cell(assign_map, module, cell, stringf("%s, %s", log_signal(a), log_signal(b)), ID::Y, y); \
goto next_cell; \
} \
@ -1627,7 +1578,6 @@ skip_identity:
assign_map.apply(a), assign_map.apply(b); \
if (a.is_fully_const() && b.is_fully_const()) { \
RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), b.as_const())); \
cover("opt.opt_expr.const.$" #_t); \
replace_cell(assign_map, module, cell, stringf("%s, %s", log_signal(a), log_signal(b)), ID::Y, y); \
goto next_cell; \
} \
@ -1640,7 +1590,6 @@ skip_identity:
assign_map.apply(a), assign_map.apply(b), assign_map.apply(s); \
if (a.is_fully_const() && b.is_fully_const() && s.is_fully_const()) { \
RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), b.as_const(), s.as_const())); \
cover("opt.opt_expr.const.$" #_t); \
replace_cell(assign_map, module, cell, stringf("%s, %s, %s", log_signal(a), log_signal(b), log_signal(s)), ID::Y, y); \
goto next_cell; \
} \
@ -1716,7 +1665,11 @@ skip_identity:
int bit_idx;
const auto onehot = sig_a.is_onehot(&bit_idx);
if (onehot) {
// Power of two
// A is unsigned or positive
if (onehot && (!cell->parameters[ID::A_SIGNED].as_bool() || bit_idx < sig_a.size() - 1)) {
cell->parameters[ID::A_SIGNED] = 0;
// 2^B = 1<<B
if (bit_idx == 1) {
log_debug("Replacing pow cell `%s' in module `%s' with left-shift\n",
cell->name.c_str(), module->name.c_str());
@ -1728,7 +1681,6 @@ skip_identity:
log_debug("Replacing pow cell `%s' in module `%s' with multiply and left-shift\n",
cell->name.c_str(), module->name.c_str());
cell->type = ID($mul);
cell->parameters[ID::A_SIGNED] = 0;
cell->setPort(ID::A, Const(bit_idx, cell->parameters[ID::A_WIDTH].as_int()));
SigSpec y_wire = module->addWire(NEW_ID, y_size);
@ -1757,8 +1709,6 @@ skip_identity:
{
if (sig_a.is_fully_zero())
{
cover("opt.opt_expr.mul_shift.zero");
log_debug("Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n",
cell->name.c_str(), module->name.c_str());
@ -1772,11 +1722,6 @@ skip_identity:
int exp;
if (sig_a.is_onehot(&exp) && !(a_signed && exp == GetSize(sig_a) - 1))
{
if (swapped_ab)
cover("opt.opt_expr.mul_shift.swapped");
else
cover("opt.opt_expr.mul_shift.unswapped");
log_debug("Replacing multiply-by-%s cell `%s' in module `%s' with shift-by-%d.\n",
log_signal(sig_a), cell->name.c_str(), module->name.c_str(), exp);
@ -1810,8 +1755,6 @@ skip_identity:
break;
if (a_zeros || b_zeros) {
int y_zeros = a_zeros + b_zeros;
cover("opt.opt_expr.mul_low_zeros");
log_debug("Removing low %d A and %d B bits from cell `%s' in module `%s'.\n",
a_zeros, b_zeros, cell->name.c_str(), module->name.c_str());
@ -1853,8 +1796,6 @@ skip_identity:
{
if (sig_b.is_fully_zero())
{
cover("opt.opt_expr.divmod_zero");
log_debug("Replacing divide-by-zero cell `%s' in module `%s' with undef-driver.\n",
cell->name.c_str(), module->name.c_str());
@ -1870,8 +1811,6 @@ skip_identity:
{
if (cell->type.in(ID($div), ID($divfloor)))
{
cover("opt.opt_expr.div_shift");
bool is_truncating = cell->type == ID($div);
log_debug("Replacing %s-divide-by-%s cell `%s' in module `%s' with shift-by-%d.\n",
is_truncating ? "truncating" : "flooring",
@ -1900,8 +1839,6 @@ skip_identity:
}
else if (cell->type.in(ID($mod), ID($modfloor)))
{
cover("opt.opt_expr.mod_mask");
bool is_truncating = cell->type == ID($mod);
log_debug("Replacing %s-modulo-by-%s cell `%s' in module `%s' with bitmask.\n",
is_truncating ? "truncating" : "flooring",
@ -2026,7 +1963,6 @@ skip_identity:
sig_ci = p.second;
}
cover("opt.opt_expr.alu_split");
module->remove(cell);
did_something = true;

View file

@ -22,6 +22,7 @@
#include "kernel/sigtools.h"
#include "kernel/log.h"
#include "kernel/celltypes.h"
#include "kernel/threading.h"
#include "libs/sha1/sha1.h"
#include <stdlib.h>
#include <stdio.h>
@ -37,16 +38,73 @@ PRIVATE_NAMESPACE_BEGIN
template <typename T, typename U>
inline Hasher hash_pair(const T &t, const U &u) { return hash_ops<std::pair<T, U>>::hash(t, u); }
struct OptMergeWorker
// Some cell and its hash value.
struct CellHash
{
RTLIL::Design *design;
RTLIL::Module *module;
SigMap assign_map;
FfInitVals initvals;
bool mode_share_all;
// Index of a cell in the module
int cell_index;
Hasher::hash_t hash_value;
};
CellTypes ct;
int total_count;
// The algorithm:
// 1) Compute and store the hashes of all relevant cells, in parallel.
// 2) Given N = the number of threads, partition the cells into N buckets by hash value:
// bucket k contains the cells whose hash value mod N = k.
// 3) For each bucket in parallel, build a hashtable of that buckets cells (using the
// precomputed hashes) and record the duplicates found.
// 4) On the main thread, process the list of duplicates to remove cells.
// For efficiency we fuse the second step into the first step by having the parallel
// threads write the cells into buckets directly.
// To avoid synchronization overhead, we divide each bucket into N shards. Each
// thread j adds a cell to bucket k by writing to shard j of bucket k —
// no synchronization required. In the next phase, thread k builds the hashtable for
// bucket k by iterating over all shards of the bucket.
// The input to each thread in the "compute cell hashes" phase.
struct CellRange
{
int begin;
int end;
};
// The output from each thread in the "compute cell hashes" phase.
struct CellHashes
{
// Entry i contains the hashes where hash_value % bucketed_cell_hashes.size() == i
std::vector<std::vector<CellHash>> bucketed_cell_hashes;
};
// A duplicate cell that has been found.
struct DuplicateCell
{
// Remove this cell from the design
int remove_cell;
// ... and use this cell instead.
int keep_cell;
};
// The input to each thread in the "find duplicate cells" phase.
// Shards of buckets of cell hashes
struct Shards
{
std::vector<std::vector<std::vector<CellHash>>> &bucketed_cell_hashes;
};
// The output from each thread in the "find duplicate cells" phase.
struct FoundDuplicates
{
std::vector<DuplicateCell> duplicates;
};
struct OptMergeThreadWorker
{
const RTLIL::Module *module;
const SigMap &assign_map;
const FfInitVals &initvals;
const CellTypes &ct;
int workers;
bool mode_share_all;
bool mode_keepdc;
static Hasher hash_pmux_in(const SigSpec& sig_s, const SigSpec& sig_b, Hasher h)
{
@ -62,8 +120,8 @@ struct OptMergeWorker
static void sort_pmux_conn(dict<RTLIL::IdString, RTLIL::SigSpec> &conn)
{
SigSpec sig_s = conn.at(ID::S);
SigSpec sig_b = conn.at(ID::B);
const SigSpec &sig_s = conn.at(ID::S);
const SigSpec &sig_b = conn.at(ID::B);
int s_width = GetSize(sig_s);
int width = GetSize(sig_b) / s_width;
@ -144,7 +202,6 @@ struct OptMergeWorker
if (cell1->parameters != cell2->parameters)
return false;
if (cell1->connections_.size() != cell2->connections_.size())
return false;
for (const auto &it : cell1->connections_)
@ -199,7 +256,7 @@ struct OptMergeWorker
return conn1 == conn2;
}
bool has_dont_care_initval(const RTLIL::Cell *cell)
bool has_dont_care_initval(const RTLIL::Cell *cell) const
{
if (!cell->is_builtin_ff())
return false;
@ -207,31 +264,134 @@ struct OptMergeWorker
return !initvals(cell->getPort(ID::Q)).is_fully_def();
}
OptMergeWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux, bool mode_share_all, bool mode_keepdc) :
design(design), module(module), mode_share_all(mode_share_all)
OptMergeThreadWorker(const RTLIL::Module *module, const FfInitVals &initvals,
const SigMap &assign_map, const CellTypes &ct, int workers,
bool mode_share_all, bool mode_keepdc) :
module(module), assign_map(assign_map), initvals(initvals), ct(ct),
workers(workers), mode_share_all(mode_share_all), mode_keepdc(mode_keepdc)
{
total_count = 0;
ct.setup_internals();
ct.setup_internals_mem();
ct.setup_stdcells();
ct.setup_stdcells_mem();
}
if (mode_nomux) {
ct.cell_types.erase(ID($mux));
ct.cell_types.erase(ID($pmux));
CellHashes compute_cell_hashes(const CellRange &cell_range) const
{
std::vector<std::vector<CellHash>> bucketed_cell_hashes(workers);
for (int cell_index = cell_range.begin; cell_index < cell_range.end; ++cell_index) {
const RTLIL::Cell *cell = module->cell_at(cell_index);
if (!module->selected(cell))
continue;
if (cell->type.in(ID($meminit), ID($meminit_v2), ID($mem), ID($mem_v2))) {
// Ignore those for performance: meminit can have an excessively large port,
// mem can have an excessively large parameter holding the init data
continue;
}
if (cell->type == ID($scopeinfo))
continue;
if (mode_keepdc && has_dont_care_initval(cell))
continue;
if (!cell->known())
continue;
if (!mode_share_all && !ct.cell_known(cell->type))
continue;
Hasher::hash_t h = hash_cell_function(cell, Hasher()).yield();
int bucket_index = h % workers;
bucketed_cell_hashes[bucket_index].push_back({cell_index, h});
}
return {std::move(bucketed_cell_hashes)};
}
ct.cell_types.erase(ID($tribuf));
ct.cell_types.erase(ID($_TBUF_));
ct.cell_types.erase(ID($anyseq));
ct.cell_types.erase(ID($anyconst));
ct.cell_types.erase(ID($allseq));
ct.cell_types.erase(ID($allconst));
FoundDuplicates find_duplicate_cells(int index, const Shards &in) const
{
// We keep a set of known cells. They're hashed with our hash_cell_function
// and compared with our compare_cell_parameters_and_connections.
struct CellHashOp {
std::size_t operator()(const CellHash &c) const {
return (std::size_t)c.hash_value;
}
};
struct CellEqualOp {
const OptMergeThreadWorker& worker;
CellEqualOp(const OptMergeThreadWorker& w) : worker(w) {}
bool operator()(const CellHash &lhs, const CellHash &rhs) const {
return worker.compare_cell_parameters_and_connections(
worker.module->cell_at(lhs.cell_index),
worker.module->cell_at(rhs.cell_index));
}
};
std::unordered_set<
CellHash,
CellHashOp,
CellEqualOp> known_cells(0, CellHashOp(), CellEqualOp(*this));
std::vector<DuplicateCell> duplicates;
for (const std::vector<std::vector<CellHash>> &buckets : in.bucketed_cell_hashes) {
// Clear out our buckets as we go. This keeps the work of deallocation
// off the main thread.
std::vector<CellHash> bucket = std::move(buckets[index]);
for (CellHash c : bucket) {
auto [cell_in_map, inserted] = known_cells.insert(c);
if (inserted)
continue;
CellHash map_c = *cell_in_map;
if (module->cell_at(c.cell_index)->has_keep_attr()) {
if (module->cell_at(map_c.cell_index)->has_keep_attr())
continue;
known_cells.erase(map_c);
known_cells.insert(c);
std::swap(c, map_c);
}
duplicates.push_back({c.cell_index, map_c.cell_index});
}
}
return {duplicates};
}
};
template <typename T>
void initialize_queues(std::vector<ConcurrentQueue<T>> &queues, int size) {
queues.reserve(size);
for (int i = 0; i < size; ++i)
queues.emplace_back(1);
}
struct OptMergeWorker
{
int total_count;
OptMergeWorker(RTLIL::Module *module, const CellTypes &ct, bool mode_share_all, bool mode_keepdc) :
total_count(0)
{
SigMap assign_map(module);
FfInitVals initvals;
initvals.set(&assign_map, module);
log("Finding identical cells in module `%s'.\n", module->name);
assign_map.set(module);
initvals.set(&assign_map, module);
// Use no more than one worker per thousand cells, rounded down, so
// we only start multithreading with at least 2000 cells.
int num_worker_threads = ThreadPool::pool_size(0, module->cells_size()/1000);
int workers = std::max(1, num_worker_threads);
// The main thread doesn't do any work, so if there is only one worker thread,
// just run everything on the main thread instead.
// This avoids creating and waiting on a thread, which is pretty high overhead
// for very small modules.
if (num_worker_threads == 1)
num_worker_threads = 0;
OptMergeThreadWorker thread_worker(module, initvals, assign_map, ct, workers, mode_share_all, mode_keepdc);
std::vector<ConcurrentQueue<CellRange>> cell_ranges_queues(num_worker_threads);
std::vector<ConcurrentQueue<CellHashes>> cell_hashes_queues(num_worker_threads);
std::vector<ConcurrentQueue<Shards>> shards_queues(num_worker_threads);
std::vector<ConcurrentQueue<FoundDuplicates>> duplicates_queues(num_worker_threads);
ThreadPool thread_pool(num_worker_threads, [&](int i) {
while (std::optional<CellRange> c = cell_ranges_queues[i].pop_front()) {
cell_hashes_queues[i].push_back(thread_worker.compute_cell_hashes(*c));
std::optional<Shards> shards = shards_queues[i].pop_front();
duplicates_queues[i].push_back(thread_worker.find_duplicate_cells(i, *shards));
}
});
bool did_something = true;
// A cell may have to go through a lot of collisions if the hash
@ -239,87 +399,99 @@ struct OptMergeWorker
// beyond the user's control.
while (did_something)
{
std::vector<RTLIL::Cell*> cells;
cells.reserve(module->cells().size());
for (auto cell : module->cells()) {
if (!design->selected(module, cell))
continue;
if (cell->type.in(ID($meminit), ID($meminit_v2), ID($mem), ID($mem_v2))) {
// Ignore those for performance: meminit can have an excessively large port,
// mem can have an excessively large parameter holding the init data
continue;
}
if (cell->type == ID($scopeinfo))
continue;
if (mode_keepdc && has_dont_care_initval(cell))
continue;
if (!cell->known())
continue;
if (!mode_share_all && !ct.cell_known(cell->type))
continue;
cells.push_back(cell);
}
int cells_size = module->cells_size();
log("Computing hashes of %d cells of `%s'.\n", cells_size, module->name);
std::vector<std::vector<std::vector<CellHash>>> sharded_bucketed_cell_hashes(workers);
did_something = false;
// We keep a set of known cells. They're hashed with our hash_cell_function
// and compared with our compare_cell_parameters_and_connections.
// Both need to capture OptMergeWorker to access initvals
struct CellPtrHash {
const OptMergeWorker& worker;
CellPtrHash(const OptMergeWorker& w) : worker(w) {}
std::size_t operator()(const Cell* c) const {
return (std::size_t)worker.hash_cell_function(c, Hasher()).yield();
}
};
struct CellPtrEqual {
const OptMergeWorker& worker;
CellPtrEqual(const OptMergeWorker& w) : worker(w) {}
bool operator()(const Cell* lhs, const Cell* rhs) const {
return worker.compare_cell_parameters_and_connections(lhs, rhs);
}
};
std::unordered_set<
RTLIL::Cell*,
CellPtrHash,
CellPtrEqual> known_cells (0, CellPtrHash(*this), CellPtrEqual(*this));
for (auto cell : cells)
int cell_index = 0;
int cells_size_mod_workers = cells_size % workers;
{
auto [cell_in_map, inserted] = known_cells.insert(cell);
if (!inserted) {
// We've failed to insert since we already have an equivalent cell
Cell* other_cell = *cell_in_map;
if (cell->has_keep_attr()) {
if (other_cell->has_keep_attr())
continue;
known_cells.erase(other_cell);
known_cells.insert(cell);
std::swap(other_cell, cell);
}
did_something = true;
log_debug(" Cell `%s' is identical to cell `%s'.\n", cell->name, other_cell->name);
for (auto &it : cell->connections()) {
if (cell->output(it.first)) {
RTLIL::SigSpec other_sig = other_cell->getPort(it.first);
log_debug(" Redirecting output %s: %s = %s\n", it.first,
log_signal(it.second), log_signal(other_sig));
Const init = initvals(other_sig);
initvals.remove_init(it.second);
initvals.remove_init(other_sig);
module->connect(RTLIL::SigSig(it.second, other_sig));
assign_map.add(it.second, other_sig);
initvals.set_init(other_sig, init);
}
}
log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type, cell->name, module->name);
module->remove(cell);
total_count++;
Multithreading multithreading;
for (int i = 0; i < workers; ++i) {
int num_cells = cells_size/workers + ((i < cells_size_mod_workers) ? 1 : 0);
CellRange c = { cell_index, cell_index + num_cells };
cell_index += num_cells;
if (num_worker_threads > 0)
cell_ranges_queues[i].push_back(c);
else
sharded_bucketed_cell_hashes[i] = std::move(thread_worker.compute_cell_hashes(c).bucketed_cell_hashes);
}
log_assert(cell_index == cells_size);
if (num_worker_threads > 0)
for (int i = 0; i < workers; ++i)
sharded_bucketed_cell_hashes[i] = std::move(cell_hashes_queues[i].pop_front()->bucketed_cell_hashes);
}
log("Finding duplicate cells in `%s'.\n", module->name);
std::vector<DuplicateCell> merged_duplicates;
{
Multithreading multithreading;
for (int i = 0; i < workers; ++i) {
Shards thread_shards = { sharded_bucketed_cell_hashes };
if (num_worker_threads > 0)
shards_queues[i].push_back(thread_shards);
else {
std::vector<DuplicateCell> d = std::move(thread_worker.find_duplicate_cells(i, thread_shards).duplicates);
merged_duplicates.insert(merged_duplicates.end(), d.begin(), d.end());
}
}
if (num_worker_threads > 0)
for (int i = 0; i < workers; ++i) {
std::vector<DuplicateCell> d = std::move(duplicates_queues[i].pop_front()->duplicates);
merged_duplicates.insert(merged_duplicates.end(), d.begin(), d.end());
}
}
std::sort(merged_duplicates.begin(), merged_duplicates.end(), [](const DuplicateCell &lhs, const DuplicateCell &rhs) {
// Sort them by the order in which duplicates would have been detected in a single-threaded
// run. The cell at which the duplicate would have been detected is the latter of the two
// cells involved.
return std::max(lhs.remove_cell, lhs.keep_cell) < std::max(rhs.remove_cell, rhs.keep_cell);
});
// Convert to cell pointers because removing cells will invalidate the indices.
std::vector<std::pair<RTLIL::Cell*, RTLIL::Cell*>> cell_ptrs;
for (DuplicateCell dup : merged_duplicates)
cell_ptrs.push_back({module->cell_at(dup.remove_cell), module->cell_at(dup.keep_cell)});
for (auto [remove_cell, keep_cell] : cell_ptrs)
{
log_debug(" Cell `%s' is identical to cell `%s'.\n", remove_cell->name, keep_cell->name);
for (auto &it : remove_cell->connections()) {
if (remove_cell->output(it.first)) {
RTLIL::SigSpec keep_sig = keep_cell->getPort(it.first);
log_debug(" Redirecting output %s: %s = %s\n", it.first,
log_signal(it.second), log_signal(keep_sig));
Const init = initvals(keep_sig);
initvals.remove_init(it.second);
initvals.remove_init(keep_sig);
module->connect(RTLIL::SigSig(it.second, keep_sig));
auto keep_sig_it = keep_sig.begin();
for (SigBit remove_sig_bit : it.second) {
assign_map.add(remove_sig_bit, *keep_sig_it);
++keep_sig_it;
}
initvals.set_init(keep_sig, init);
}
}
log_debug(" Removing %s cell `%s' from module `%s'.\n", remove_cell->type, remove_cell->name, module->name);
module->remove(remove_cell);
total_count++;
}
did_something = !merged_duplicates.empty();
}
for (ConcurrentQueue<CellRange> &q : cell_ranges_queues)
q.close();
for (ConcurrentQueue<Shards> &q : shards_queues)
q.close();
for (ConcurrentQueue<CellRange> &q : cell_ranges_queues)
q.close();
for (ConcurrentQueue<Shards> &q : shards_queues)
q.close();
log_suppressed();
}
};
@ -372,9 +544,25 @@ struct OptMergePass : public Pass {
}
extra_args(args, argidx, design);
CellTypes ct;
ct.setup_internals();
ct.setup_internals_mem();
ct.setup_stdcells();
ct.setup_stdcells_mem();
if (mode_nomux) {
ct.cell_types.erase(ID($mux));
ct.cell_types.erase(ID($pmux));
}
ct.cell_types.erase(ID($tribuf));
ct.cell_types.erase(ID($_TBUF_));
ct.cell_types.erase(ID($anyseq));
ct.cell_types.erase(ID($anyconst));
ct.cell_types.erase(ID($allseq));
ct.cell_types.erase(ID($allconst));
int total_count = 0;
for (auto module : design->selected_modules()) {
OptMergeWorker worker(design, module, mode_nomux, mode_share_all, mode_keepdc);
OptMergeWorker worker(module, ct, mode_share_all, mode_keepdc);
total_count += worker.total_count;
}

View file

@ -64,7 +64,7 @@ struct OptMuxtreeWorker
RTLIL::Module *module;
SigMap assign_map;
int removed_count;
int glob_evals_left = 10000000;
int glob_evals_left = 10'000'000;
struct bitinfo_t {
// Is bit directly used by non-mux cells or ports?

View file

@ -97,6 +97,7 @@ void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did
all_empty = false;
if (all_empty)
{
did_something = true;
for (auto cs : sw->cases)
delete cs;
sw->cases.clear();

View file

@ -31,6 +31,22 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
static SatSolver *find_satsolver(const std::string &name)
{
for (auto solver = yosys_satsolver_list; solver != nullptr; solver = solver->next)
if (solver->name == name)
return solver;
return nullptr;
}
static std::string list_satsolvers()
{
std::string result;
for (auto solver = yosys_satsolver_list; solver != nullptr; solver = solver->next)
result += result.empty() ? solver->name : ", " + solver->name;
return result;
}
struct SatHelper
{
RTLIL::Design *design;
@ -60,8 +76,8 @@ struct SatHelper
int max_timestep, timeout;
bool gotTimeout;
SatHelper(RTLIL::Design *design, RTLIL::Module *module, bool enable_undef, bool set_def_formal) :
design(design), module(module), sigmap(module), ct(design), satgen(ez.get(), &sigmap)
SatHelper(RTLIL::Design *design, RTLIL::Module *module, SatSolver *solver, bool enable_undef, bool set_def_formal) :
design(design), module(module), sigmap(module), ct(design), ez(solver), satgen(ez.get(), &sigmap)
{
this->enable_undef = enable_undef;
satgen.model_undef = enable_undef;
@ -441,7 +457,7 @@ struct SatHelper
log_assert(gotTimeout == false);
ez->setSolverTimeout(timeout);
bool success = ez->solve(modelExpressions, modelValues, assumptions);
if (ez->getSolverTimoutStatus())
if (ez->getSolverTimeoutStatus())
gotTimeout = true;
return success;
}
@ -451,7 +467,7 @@ struct SatHelper
log_assert(gotTimeout == false);
ez->setSolverTimeout(timeout);
bool success = ez->solve(modelExpressions, modelValues, a, b, c, d, e, f);
if (ez->getSolverTimoutStatus())
if (ez->getSolverTimeoutStatus())
gotTimeout = true;
return success;
}
@ -1066,6 +1082,10 @@ struct SatPass : public Pass {
log(" -timeout <N>\n");
log(" Maximum number of seconds a single SAT instance may take.\n");
log("\n");
log(" -select-solver <name>\n");
log(" Select SAT solver implementation for this invocation.\n");
log(" If not given, uses scratchpad key 'sat.solver' if set, otherwise default.\n");
log("\n");
log(" -verify\n");
log(" Return an error and stop the synthesis script if the proof fails.\n");
log("\n");
@ -1097,8 +1117,14 @@ struct SatPass : public Pass {
log_header(design, "Executing SAT pass (solving SAT problems in the circuit).\n");
std::string solver_name = design->scratchpad_get_string("sat.solver", "");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-select-solver" && argidx+1 < args.size()) {
solver_name = args[++argidx];
continue;
}
if (args[argidx] == "-all") {
loopcount = -1;
continue;
@ -1336,6 +1362,14 @@ struct SatPass : public Pass {
}
extra_args(args, argidx, design);
SatSolver *solver = yosys_satsolver;
if (!solver_name.empty()) {
solver = find_satsolver(solver_name);
if (solver == nullptr)
log_cmd_error("Unknown SAT solver '%s'. Available solvers: %s\n",
solver_name, list_satsolvers());
}
RTLIL::Module *module = NULL;
for (auto mod : design->selected_modules()) {
if (module)
@ -1398,13 +1432,15 @@ struct SatPass : public Pass {
shows.push_back(wire->name.str());
}
log("Using SAT solver `%s`.\n", solver->name.c_str());
if (tempinduct)
{
if (loopcount > 0 || max_undef)
log_cmd_error("The options -max, -all, and -max_undef are not supported for temporal induction proofs!\n");
SatHelper basecase(design, module, enable_undef, set_def_formal);
SatHelper inductstep(design, module, enable_undef, set_def_formal);
SatHelper basecase(design, module, solver, enable_undef, set_def_formal);
SatHelper inductstep(design, module, solver, enable_undef, set_def_formal);
basecase.sets = sets;
basecase.set_assumes = set_assumes;
@ -1593,7 +1629,7 @@ struct SatPass : public Pass {
if (maxsteps > 0)
log_cmd_error("The options -maxsteps is only supported for temporal induction proofs!\n");
SatHelper sathelper(design, module, enable_undef, set_def_formal);
SatHelper sathelper(design, module, solver, enable_undef, set_def_formal);
sathelper.sets = sets;
sathelper.set_assumes = set_assumes;

View file

@ -550,31 +550,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;
}

View file

@ -39,6 +39,7 @@ OBJS += passes/techmap/muxcover.o
OBJS += passes/techmap/aigmap.o
OBJS += passes/techmap/tribuf.o
OBJS += passes/techmap/lut2mux.o
OBJS += passes/techmap/lut2bmux.o
OBJS += passes/techmap/nlutmap.o
OBJS += passes/techmap/shregmap.o
OBJS += passes/techmap/deminout.o

View file

@ -143,6 +143,14 @@ struct AbcConfig
bool markgroups = false;
pool<std::string> enabled_gates;
bool cmos_cost = false;
bool is_yosys_abc() const {
#ifdef ABCEXTERNAL
return false;
#else
return exe_file == yosys_abc_executable;
#endif
}
};
struct AbcSigVal {
@ -155,7 +163,12 @@ struct AbcSigVal {
}
};
#if defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN)
// REUSE_YOSYS_ABC_PROCESSES only works when ABC is built with ENABLE_READLINE.
#if defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN) && defined(YOSYS_ENABLE_READLINE)
#define REUSE_YOSYS_ABC_PROCESSES
#endif
#ifdef REUSE_YOSYS_ABC_PROCESSES
struct AbcProcess
{
pid_t pid;
@ -188,10 +201,10 @@ struct AbcProcess
int status;
int ret = waitpid(pid, &status, 0);
if (ret != pid) {
log_error("waitpid(%d) failed", pid);
log_error("waitpid(%d) failed\n", pid);
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
log_error("ABC failed with status %X", status);
log_error("ABC failed with status %X\n", status);
}
if (from_child_pipe >= 0)
close(from_child_pipe);
@ -203,12 +216,12 @@ std::optional<AbcProcess> spawn_abc(const char* abc_exe, DeferredLogs &logs) {
// fork()s.
int to_child_pipe[2];
if (pipe2(to_child_pipe, O_CLOEXEC) != 0) {
logs.log_error("pipe failed");
logs.log_error("pipe failed\n");
return std::nullopt;
}
int from_child_pipe[2];
if (pipe2(from_child_pipe, O_CLOEXEC) != 0) {
logs.log_error("pipe failed");
logs.log_error("pipe failed\n");
return std::nullopt;
}
@ -221,39 +234,39 @@ std::optional<AbcProcess> spawn_abc(const char* abc_exe, DeferredLogs &logs) {
posix_spawn_file_actions_t file_actions;
if (posix_spawn_file_actions_init(&file_actions) != 0) {
logs.log_error("posix_spawn_file_actions_init failed");
logs.log_error("posix_spawn_file_actions_init failed\n");
return std::nullopt;
}
if (posix_spawn_file_actions_addclose(&file_actions, to_child_pipe[1]) != 0) {
logs.log_error("posix_spawn_file_actions_addclose failed");
logs.log_error("posix_spawn_file_actions_addclose failed\n");
return std::nullopt;
}
if (posix_spawn_file_actions_addclose(&file_actions, from_child_pipe[0]) != 0) {
logs.log_error("posix_spawn_file_actions_addclose failed");
logs.log_error("posix_spawn_file_actions_addclose failed\n");
return std::nullopt;
}
if (posix_spawn_file_actions_adddup2(&file_actions, to_child_pipe[0], STDIN_FILENO) != 0) {
logs.log_error("posix_spawn_file_actions_adddup2 failed");
logs.log_error("posix_spawn_file_actions_adddup2 failed\n");
return std::nullopt;
}
if (posix_spawn_file_actions_adddup2(&file_actions, from_child_pipe[1], STDOUT_FILENO) != 0) {
logs.log_error("posix_spawn_file_actions_adddup2 failed");
logs.log_error("posix_spawn_file_actions_adddup2 failed\n");
return std::nullopt;
}
if (posix_spawn_file_actions_addclose(&file_actions, to_child_pipe[0]) != 0) {
logs.log_error("posix_spawn_file_actions_addclose failed");
logs.log_error("posix_spawn_file_actions_addclose failed\n");
return std::nullopt;
}
if (posix_spawn_file_actions_addclose(&file_actions, from_child_pipe[1]) != 0) {
logs.log_error("posix_spawn_file_actions_addclose failed");
logs.log_error("posix_spawn_file_actions_addclose failed\n");
return std::nullopt;
}
char arg1[] = "-s";
char* argv[] = { strdup(abc_exe), arg1, nullptr };
if (0 != posix_spawn(&result.pid, abc_exe, &file_actions, nullptr, argv, environ)) {
logs.log_error("posix_spawn %s failed", abc_exe);
if (0 != posix_spawnp(&result.pid, abc_exe, &file_actions, nullptr, argv, environ)) {
logs.log_error("posix_spawnp %s failed (errno=%s)\n", abc_exe, strerror(errno));
return std::nullopt;
}
free(argv[0]);
@ -272,7 +285,7 @@ using AbcSigMap = SigValMap<AbcSigVal>;
struct RunAbcState {
const AbcConfig &config;
std::string tempdir_name;
std::string per_run_tempdir_name;
std::vector<gate_t> signal_list;
bool did_run = false;
bool err = false;
@ -823,16 +836,23 @@ std::string fold_abc_cmd(std::string str)
return new_str;
}
std::string replace_tempdir(std::string text, std::string tempdir_name, bool show_tempdir)
std::string replace_tempdir(std::string text, std::string_view global_tempdir_name, std::string_view per_run_tempdir_name, bool show_tempdir)
{
if (show_tempdir)
return text;
while (1) {
size_t pos = text.find(tempdir_name);
size_t pos = text.find(global_tempdir_name);
if (pos == std::string::npos)
break;
text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(tempdir_name));
text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(global_tempdir_name));
}
while (1) {
size_t pos = text.find(per_run_tempdir_name);
if (pos == std::string::npos)
break;
text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(per_run_tempdir_name));
}
std::string selfdir_name = proc_self_dirname();
@ -854,11 +874,12 @@ struct abc_output_filter
bool got_cr;
int escape_seq_state;
std::string linebuf;
std::string tempdir_name;
std::string global_tempdir_name;
std::string per_run_tempdir_name;
bool show_tempdir;
abc_output_filter(RunAbcState& state, std::string tempdir_name, bool show_tempdir)
: state(state), tempdir_name(tempdir_name), show_tempdir(show_tempdir)
abc_output_filter(RunAbcState& state, std::string global_tempdir_name, std::string per_run_tempdir_name, bool show_tempdir)
: state(state), global_tempdir_name(global_tempdir_name), per_run_tempdir_name(per_run_tempdir_name), show_tempdir(show_tempdir)
{
got_cr = false;
escape_seq_state = 0;
@ -885,7 +906,7 @@ struct abc_output_filter
return;
}
if (ch == '\n') {
state.logs.log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir));
state.logs.log("ABC: %s\n", replace_tempdir(linebuf, global_tempdir_name, per_run_tempdir_name, show_tempdir));
got_cr = false, linebuf.clear();
return;
}
@ -986,15 +1007,15 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module
const AbcConfig &config = run_abc.config;
if (config.cleanup)
run_abc.tempdir_name = get_base_tmpdir() + "/";
run_abc.per_run_tempdir_name = get_base_tmpdir() + "/";
else
run_abc.tempdir_name = "_tmp_";
run_abc.tempdir_name += proc_program_prefix() + "yosys-abc-XXXXXX";
run_abc.tempdir_name = make_temp_dir(run_abc.tempdir_name);
run_abc.per_run_tempdir_name = "_tmp_";
run_abc.per_run_tempdir_name += proc_program_prefix() + "yosys-abc-XXXXXX";
run_abc.per_run_tempdir_name = make_temp_dir(run_abc.per_run_tempdir_name);
log_header(design, "Extracting gate netlist of module `%s' to `%s/input.blif'..\n",
module->name.c_str(), replace_tempdir(run_abc.tempdir_name, run_abc.tempdir_name, config.show_tempdir).c_str());
module->name.c_str(), replace_tempdir(run_abc.per_run_tempdir_name, config.global_tempdir_name, run_abc.per_run_tempdir_name, config.show_tempdir).c_str());
std::string abc_script = stringf("read_blif \"%s/input.blif\"; ", run_abc.tempdir_name);
std::string abc_script = stringf("read_blif \"%s/input.blif\"; ", run_abc.per_run_tempdir_name);
if (!config.liberty_files.empty() || !config.genlib_files.empty()) {
std::string dont_use_args;
@ -1060,18 +1081,19 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module
for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos))
abc_script = abc_script.substr(0, pos) + config.lutin_shared + abc_script.substr(pos+3);
if (config.abc_dress)
abc_script += stringf("; dress \"%s/input.blif\"", run_abc.tempdir_name);
abc_script += stringf("; write_blif %s/output.blif", run_abc.tempdir_name);
abc_script += stringf("; dress \"%s/input.blif\"", run_abc.per_run_tempdir_name);
abc_script += stringf("; write_blif %s/output.blif", run_abc.per_run_tempdir_name);
abc_script = add_echos_to_abc_cmd(abc_script);
#if defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN)
abc_script += "; echo \"YOSYS_ABC_DONE\"\n";
#if defined(REUSE_YOSYS_ABC_PROCESSES)
if (config.is_yosys_abc())
abc_script += "; echo; echo \"YOSYS_ABC_DONE\"\n";
#endif
for (size_t i = 0; i+1 < abc_script.size(); i++)
if (abc_script[i] == ';' && abc_script[i+1] == ' ')
abc_script[i+1] = '\n';
std::string buffer = stringf("%s/abc.script", run_abc.tempdir_name);
std::string buffer = stringf("%s/abc.script", run_abc.per_run_tempdir_name);
FILE *f = fopen(buffer.c_str(), "wt");
if (f == nullptr)
log_error("Opening %s for writing failed: %s\n", buffer, strerror(errno));
@ -1127,42 +1149,86 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module
handle_loops(assign_map, module);
}
#if defined(REUSE_YOSYS_ABC_PROCESSES)
static bool is_abc_prompt(const std::string &line, std::string &rest) {
size_t pos = 0;
while (true) {
// The prompt may not start at the start of the line, because
// ABC can output progress and maybe other data that isn't
// newline-terminated.
size_t start = line.find("abc ", pos);
if (start == std::string::npos)
return false;
pos = start + 4;
size_t digits = 0;
while (pos + digits < line.size() && line[pos + digits] >= '0' && line[pos + digits] <= '9')
++digits;
if (digits < 2)
return false;
if (line.substr(pos + digits, 2) == "> ") {
rest = line.substr(pos + digits + 2);
return true;
}
}
}
bool read_until_abc_done(abc_output_filter &filt, int fd, DeferredLogs &logs) {
std::string line;
char buf[1024];
bool seen_source_cmd = false;
bool seen_yosys_abc_done = false;
while (true) {
int ret = read(fd, buf, sizeof(buf) - 1);
if (ret < 0) {
logs.log_error("Failed to read from ABC, errno=%d", errno);
logs.log_error("Failed to read from ABC, errno=%d\n", errno);
return false;
}
if (ret == 0) {
logs.log_error("ABC exited prematurely");
logs.log_error("ABC exited prematurely\n");
return false;
}
char *start = buf;
char *end = buf + ret;
while (start < end) {
char *p = static_cast<char*>(memchr(start, '\n', end - start));
if (p == nullptr) {
break;
char *upto = p == nullptr ? end : p + 1;
line.append(start, upto - start);
start = upto;
std::string rest;
bool is_prompt = is_abc_prompt(line, rest);
if (is_prompt && seen_source_cmd) {
// This is the first prompt after we sourced the script.
// We are done here.
// We won't have seen a newline yet since ABC is waiting at the prompt.
if (!seen_yosys_abc_done)
logs.log_error("ABC script did not complete successfully\n");
return seen_yosys_abc_done;
}
line.append(start, p + 1 - start);
if (line.substr(0, 14) == "YOSYS_ABC_DONE") {
// Ignore any leftover output, there should only be a prompt perhaps
return true;
if (line.empty() || line[line.size() - 1] != '\n') {
// No newline yet, wait for more text
continue;
}
filt.next_line(line);
if (is_prompt && rest.substr(0, 7) == "source ")
seen_source_cmd = true;
if (line.substr(0, 14) == "YOSYS_ABC_DONE")
seen_yosys_abc_done = true;
line.clear();
start = p + 1;
}
line.append(start, end - start);
}
}
#endif
#if defined(REUSE_YOSYS_ABC_PROCESSES)
void RunAbcState::run(ConcurrentStack<AbcProcess> &process_pool)
#else
void RunAbcState::run(ConcurrentStack<AbcProcess> &)
#endif
{
std::string buffer = stringf("%s/input.blif", tempdir_name);
std::string buffer = stringf("%s/input.blif", per_run_tempdir_name);
FILE *f = fopen(buffer.c_str(), "wt");
if (f == nullptr) {
logs.log("Opening %s for writing failed: %s\n", buffer, strerror(errno));
@ -1285,15 +1351,19 @@ void RunAbcState::run(ConcurrentStack<AbcProcess> &process_pool)
logs.log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n",
count_gates, GetSize(signal_list), count_input, count_output);
if (count_output > 0)
{
std::string tmp_script_name = stringf("%s/abc.script", tempdir_name);
logs.log("Running ABC script: %s\n", replace_tempdir(tmp_script_name, tempdir_name, config.show_tempdir));
if (count_output == 0) {
log("Don't call ABC as there is nothing to map.\n");
return;
}
int ret;
std::string tmp_script_name = stringf("%s/abc.script", per_run_tempdir_name);
do {
logs.log("Running ABC script: %s\n", replace_tempdir(tmp_script_name, config.global_tempdir_name, per_run_tempdir_name, config.show_tempdir));
errno = 0;
abc_output_filter filt(*this, tempdir_name, config.show_tempdir);
abc_output_filter filt(*this, config.global_tempdir_name, per_run_tempdir_name, config.show_tempdir);
#ifdef YOSYS_LINK_ABC
string temp_stdouterr_name = stringf("%s/stdouterr.txt", tempdir_name);
string temp_stdouterr_name = stringf("%s/stdouterr.txt", per_run_tempdir_name);
FILE *temp_stdouterr_w = fopen(temp_stdouterr_name.c_str(), "w");
if (temp_stdouterr_w == NULL)
log_error("ABC: cannot open a temporary file for output redirection");
@ -1318,7 +1388,7 @@ void RunAbcState::run(ConcurrentStack<AbcProcess> &process_pool)
abc_argv[2] = strdup("-f");
abc_argv[3] = strdup(tmp_script_name.c_str());
abc_argv[4] = 0;
int ret = abc::Abc_RealMain(4, abc_argv);
ret = abc::Abc_RealMain(4, abc_argv);
free(abc_argv[0]);
free(abc_argv[1]);
free(abc_argv[2]);
@ -1333,39 +1403,42 @@ void RunAbcState::run(ConcurrentStack<AbcProcess> &process_pool)
for (std::string line; std::getline(temp_stdouterr_r, line); )
filt.next_line(line + "\n");
temp_stdouterr_r.close();
#elif defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN)
AbcProcess process;
if (std::optional<AbcProcess> process_opt = process_pool.try_pop_back()) {
process = std::move(process_opt.value());
} else if (std::optional<AbcProcess> process_opt = spawn_abc(config.exe_file.c_str(), logs)) {
process = std::move(process_opt.value());
} else {
return;
}
std::string cmd = stringf(
"empty\n"
"source %s\n", tmp_script_name);
int ret = write(process.to_child_pipe, cmd.c_str(), cmd.size());
if (ret != static_cast<int>(cmd.size())) {
logs.log_error("write failed");
return;
}
ret = read_until_abc_done(filt, process.from_child_pipe, logs) ? 0 : 1;
if (ret == 0) {
process_pool.push_back(std::move(process));
}
break;
#else
std::string cmd = stringf("\"%s\" -s -f %s/abc.script 2>&1", config.exe_file.c_str(), tempdir_name.c_str());
int ret = run_command(cmd, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1));
#endif
if (ret != 0) {
logs.log_error("ABC: execution of script \"%s\" failed: return code %d (errno=%d).\n", tmp_script_name, ret, errno);
return;
#if defined(REUSE_YOSYS_ABC_PROCESSES)
if (config.is_yosys_abc()) {
AbcProcess process;
if (std::optional<AbcProcess> process_opt = process_pool.try_pop_back()) {
process = std::move(process_opt.value());
} else if (std::optional<AbcProcess> process_opt = spawn_abc(config.exe_file.c_str(), logs)) {
process = std::move(process_opt.value());
} else {
return;
}
std::string cmd = stringf(
"empty\n"
"source %s\n", tmp_script_name);
ret = write(process.to_child_pipe, cmd.c_str(), cmd.size());
if (ret != static_cast<int>(cmd.size())) {
logs.log_error("write failed");
return;
}
ret = read_until_abc_done(filt, process.from_child_pipe, logs) ? 0 : 1;
if (ret == 0) {
process_pool.push_back(std::move(process));
}
break;
}
did_run = true;
#endif
std::string cmd = stringf("\"%s\" -s -f %s 2>&1", config.exe_file, tmp_script_name);
ret = run_command(cmd, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1));
#endif
} while (false);
if (ret != 0) {
logs.log_error("ABC: execution of script \"%s\" failed: return code %d (errno=%d).\n", tmp_script_name, ret, errno);
return;
}
log("Don't call ABC as there is nothing to map.\n");
did_run = true;
}
void emit_global_input_files(const AbcConfig &config)
@ -1437,7 +1510,7 @@ void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL
return;
}
std::string buffer = stringf("%s/%s", run_abc.tempdir_name, "output.blif");
std::string buffer = stringf("%s/%s", run_abc.per_run_tempdir_name, "output.blif");
std::ifstream ifs;
ifs.open(buffer);
if (ifs.fail())
@ -1724,7 +1797,7 @@ void AbcModuleState::finish()
if (run_abc.config.cleanup)
{
log("Removing temp directory.\n");
remove_directory(run_abc.tempdir_name);
remove_directory(run_abc.per_run_tempdir_name);
}
log_pop();
}

View file

@ -219,9 +219,7 @@ struct Abc9Pass : public ScriptPass
for (argidx = 1; argidx < args.size(); argidx++) {
std::string arg = args[argidx];
if ((arg == "-exe" || arg == "-script" || arg == "-D" ||
/*arg == "-S" ||*/ arg == "-lut" || arg == "-luts" ||
/*arg == "-box" ||*/ arg == "-W" || arg == "-genlib" ||
arg == "-constr" || arg == "-dont_use" || arg == "-liberty") &&
arg == "-lut" || arg == "-luts" || arg == "-W") &&
argidx+1 < args.size()) {
if (arg == "-lut" || arg == "-luts")
lut_mode = true;

View file

@ -248,7 +248,7 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe
}
abc9_script += stringf("; &ps -l; &write -n %s/output.aig", tempdir_name);
if (design->scratchpad_get_bool("abc9.verify")) {
if (design->scratchpad_get_bool("abc9.verify", true)) {
if (dff_mode)
abc9_script += "; &verify -s";
else

View file

@ -38,7 +38,8 @@ std::vector<Module*> order_modules(Design *design, std::vector<Module *> modules
sort.edge(submodule, m);
}
}
log_assert(sort.sort());
bool is_sorted = sort.sort();
log_assert(is_sorted);
return sort.sorted;
}

Some files were not shown because too many files have changed in this diff Show more