3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-06-20 15:50:27 +00:00

Merge branch 'YosysHQ:main' into master

This commit is contained in:
Eder Monteiro 2026-04-08 09:50:19 -03:00 committed by GitHub
commit 3bc1650999
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
145 changed files with 10312 additions and 1679 deletions

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 libgtest-dev
packages: bison clang flex libffi-dev libfl-dev libreadline-dev pkg-config tcl-dev zlib1g-dev libgtest-dev libgmock-dev
version: ${{ inputs.runs-on }}-buildys
- name: Linux docs dependencies

View file

@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: true
persist-credentials: false
@ -23,7 +23,7 @@ jobs:
get-build-deps: true
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
languages: cpp
queries: security-extended,security-and-quality
@ -32,4 +32,4 @@ jobs:
run: make yosys -j6
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4

View file

@ -1,23 +1,20 @@
name: Test extra build flows
on:
# always test main
push:
branches:
- main
merge_group:
# test PRs
pull_request:
# allow triggering tests, ignores skip check
merge_group:
#push:
# branches: [ main ]
workflow_dispatch:
jobs:
pre_job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
should_skip: ${{ steps.set_output.outputs.should_skip }}
steps:
- id: skip_check
if: ${{ github.event_name != 'merge_group' }}
uses: fkirc/skip-duplicate-actions@v5
with:
# don't run on documentation changes
@ -26,20 +23,28 @@ jobs:
# but never cancel main
cancel_others: ${{ github.ref != 'refs/heads/main' }}
- id: set_output
run: |
if [ "${{ github.event_name }}" = "merge_group" ]; then
echo "should_skip=false" >> $GITHUB_OUTPUT
else
echo "should_skip=${{ steps.skip_check.outputs.should_skip }}" >> $GITHUB_OUTPUT
fi
vs-prep:
name: Prepare Visual Studio build
runs-on: ubuntu-latest
needs: [pre_job]
if: needs.pre_job.outputs.should_skip != 'true'
if: (github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch') && needs.pre_job.outputs.should_skip != 'true'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
submodules: true
persist-credentials: false
- run: sudo apt-get install libfl-dev
- name: Build
run: make vcxsrc YOSYS_COMPILER="Visual Studio" VCX_DIR_NAME=yosys-win32-vcxsrc-latest
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v7
with:
name: vcxsrc
path: yosys-win32-vcxsrc-latest.zip
@ -48,9 +53,9 @@ jobs:
name: Visual Studio build
runs-on: windows-latest
needs: [vs-prep, pre_job]
if: needs.pre_job.outputs.should_skip != 'true'
if: (github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch') && needs.pre_job.outputs.should_skip != 'true'
steps:
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
name: vcxsrc
path: .
@ -65,10 +70,10 @@ jobs:
wasi-build:
name: WASI build
needs: pre_job
if: needs.pre_job.outputs.should_skip != 'true'
if: (github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch') && needs.pre_job.outputs.should_skip != 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
submodules: true
persist-credentials: false
@ -111,14 +116,14 @@ jobs:
nix-build:
name: "Build nix flake"
needs: pre_job
if: needs.pre_job.outputs.should_skip != 'true'
if: (github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch') && needs.pre_job.outputs.should_skip != 'true'
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
submodules: true
persist-credentials: false
@ -126,3 +131,21 @@ jobs:
with:
install_url: https://releases.nixos.org/nix/nix-2.30.0/install
- run: nix build .?submodules=1 -L
extra-builds-result:
runs-on: ubuntu-latest
needs:
- vs-build
- wasi-build
- nix-build
if: always()
steps:
- name: Check results
run: |
echo "Needs results: ${{ join(needs.*.result, ',') }}"
if [[ "${{ join(needs.*.result, ',') }}" == *failure* ]] || \
[[ "${{ join(needs.*.result, ',') }}" == *cancelled* ]]; then
echo "Some jobs failed or were cancelled"
exit 1
fi
- run: echo "All good"

View file

@ -1,17 +1,24 @@
name: Build docs artifact with Verific
on: [push, pull_request, merge_group]
on:
pull_request:
merge_group:
push:
branches: [ main, "docs-preview/**", "docs-preview*" ]
tags: [ "*" ]
workflow_dispatch:
jobs:
check_docs_rebuild:
runs-on: ubuntu-latest
outputs:
skip_check: ${{ steps.skip_check.outputs.should_skip }}
should_skip: ${{ steps.set_output.outputs.should_skip }}
docs_export: ${{ steps.docs_var.outputs.docs_export }}
env:
docs_export: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/docs-preview') || startsWith(github.ref, 'refs/tags/') }}
steps:
- id: skip_check
if: ${{ github.event_name != 'merge_group' }}
uses: fkirc/skip-duplicate-actions@v5
with:
paths_ignore: '["**/README.md"]'
@ -22,11 +29,19 @@ jobs:
- id: docs_var
run: echo "docs_export=${docs_export}" >> $GITHUB_OUTPUT
- id: set_output
run: |
if [ "${{ github.event_name }}" = "merge_group" ]; then
echo "should_skip=false" >> $GITHUB_OUTPUT
else
echo "should_skip=${{ steps.skip_check.outputs.should_skip }}" >> $GITHUB_OUTPUT
fi
prepare-docs:
# docs builds are needed for anything on main, any tagged versions, and any tag
# or branch starting with docs-preview
needs: check_docs_rebuild
if: ${{ needs.check_docs_rebuild.outputs.should_skip != 'true' && github.repository == 'YosysHQ/Yosys' }}
if: ${{ needs.check_docs_rebuild.outputs.should_skip != 'true' && github.repository_owner == 'YosysHQ' }}
runs-on: [self-hosted, linux, x64, fast]
steps:
- name: Checkout Yosys
@ -75,7 +90,7 @@ jobs:
make -C docs html -j$procs TARGETS= EXTRA_TARGETS=
- name: Trigger RTDs build
if: ${{ needs.check_docs_rebuild.outputs.docs_export == 'true' }}
if: ${{ needs.check_docs_rebuild.outputs.docs_export == 'true' && github.repository == 'YosysHQ/Yosys' }}
uses: dfm/rtds-action@v1.1.0
with:
webhook_url: ${{ secrets.RTDS_WEBHOOK_URL }}

View file

@ -1,13 +1,18 @@
name: Create source archive with vendored dependencies
on: [push, workflow_dispatch]
on:
pull_request:
merge_group:
push:
branches: [ main ]
workflow_dispatch:
jobs:
vendor-sources:
runs-on: ubuntu-latest
steps:
- name: Checkout repository with submodules
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: 'recursive'
persist-credentials: false
@ -27,7 +32,7 @@ jobs:
gzip yosys-src-vendored.tar
- name: Store tarball artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: vendored-sources
path: yosys-src-vendored.tar.gz

View file

@ -1,23 +1,20 @@
name: Build and run tests
on:
# always test main
push:
branches:
- main
merge_group:
# test PRs
pull_request:
# allow triggering tests, ignores skip check
merge_group:
#push:
# branches: [ main ]
workflow_dispatch:
jobs:
pre_job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
should_skip: ${{ steps.set_output.outputs.should_skip }}
steps:
- id: skip_check
if: ${{ github.event_name != 'merge_group' }}
uses: fkirc/skip-duplicate-actions@v5
with:
# don't run on documentation changes
@ -26,12 +23,21 @@ jobs:
# but never cancel main
cancel_others: ${{ github.ref != 'refs/heads/main' }}
- id: set_output
run: |
if [ "${{ github.event_name }}" = "merge_group" ]; then
echo "should_skip=false" >> $GITHUB_OUTPUT
else
echo "should_skip=${{ steps.skip_check.outputs.should_skip }}" >> $GITHUB_OUTPUT
fi
pre_docs_job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
should_skip: ${{ steps.set_output.outputs.should_skip }}
steps:
- id: skip_check
if: ${{ github.event_name != 'merge_group' }}
uses: fkirc/skip-duplicate-actions@v5
with:
# don't run on readme changes
@ -40,6 +46,14 @@ jobs:
# but never cancel main
cancel_others: ${{ github.ref != 'refs/heads/main' }}
- id: set_output
run: |
if [ "${{ github.event_name }}" = "merge_group" ]; then
echo "should_skip=false" >> $GITHUB_OUTPUT
else
echo "should_skip=${{ steps.skip_check.outputs.should_skip }}" >> $GITHUB_OUTPUT
fi
build-yosys:
name: Reusable build
runs-on: ${{ matrix.os }}
@ -54,7 +68,7 @@ jobs:
fail-fast: false
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: true
persist-credentials: false
@ -85,7 +99,7 @@ jobs:
tar -cvf ../build.tar share/ yosys yosys-* libyosys.so
- name: Store build artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: build-${{ matrix.os }}
path: build.tar
@ -104,7 +118,7 @@ jobs:
fail-fast: false
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
@ -116,7 +130,7 @@ jobs:
get-iverilog: true
- name: Download build artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: build-${{ matrix.os }}
@ -152,7 +166,7 @@ jobs:
os: [ubuntu-latest]
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
@ -162,7 +176,7 @@ jobs:
runs-on: ${{ matrix.os }}
- name: Download build artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: build-${{ matrix.os }}
@ -192,7 +206,7 @@ jobs:
fail-fast: false
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
@ -204,7 +218,7 @@ jobs:
get-docs-deps: true
- name: Download build artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: build-${{ matrix.os }}
@ -226,7 +240,7 @@ jobs:
name: Try build docs
runs-on: [self-hosted, linux, x64, fast]
needs: [pre_docs_job]
if: ${{ needs.pre_docs_job.outputs.should_skip != 'true' && github.repository == 'YosysHQ/Yosys' }}
if: ${{ needs.pre_docs_job.outputs.should_skip != 'true' && github.repository_owner == 'YosysHQ' }}
strategy:
matrix:
docs-target: [html, latexpdf]
@ -265,3 +279,22 @@ jobs:
name: docs-build-${{ matrix.docs-target }}
path: docs/build/
retention-days: 7
test-build-result:
runs-on: ubuntu-latest
needs:
- test-yosys
- test-cells
- test-docs
- test-docs-build
if: always()
steps:
- name: Check results
run: |
echo "Needs results: ${{ join(needs.*.result, ',') }}"
if [[ "${{ join(needs.*.result, ',') }}" == *failure* ]] || \
[[ "${{ join(needs.*.result, ',') }}" == *cancelled* ]]; then
echo "Some jobs failed or were cancelled"
exit 1
fi
- run: echo "All good"

View file

@ -1,23 +1,20 @@
name: Compiler testing
on:
# always test main
push:
branches:
- main
merge_group:
# test PRs
pull_request:
# allow triggering tests, ignores skip check
merge_group:
#push:
# branches: [ main ]
workflow_dispatch:
jobs:
pre_job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
should_skip: ${{ steps.set_output.outputs.should_skip }}
steps:
- id: skip_check
if: ${{ github.event_name != 'merge_group' }}
uses: fkirc/skip-duplicate-actions@v5
with:
# don't run on documentation changes
@ -26,10 +23,18 @@ jobs:
# but never cancel main
cancel_others: ${{ github.ref != 'refs/heads/main' }}
- id: set_output
run: |
if [ "${{ github.event_name }}" = "merge_group" ]; then
echo "should_skip=false" >> $GITHUB_OUTPUT
else
echo "should_skip=${{ steps.skip_check.outputs.should_skip }}" >> $GITHUB_OUTPUT
fi
test-compile:
runs-on: ${{ matrix.os }}
needs: pre_job
if: needs.pre_job.outputs.should_skip != 'true'
if: (github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch') && needs.pre_job.outputs.should_skip != 'true'
env:
CXXFLAGS: ${{ startsWith(matrix.compiler, 'gcc') && '-Wp,-D_GLIBCXX_ASSERTIONS' || ''}}
CC_SHORT: ${{ startsWith(matrix.compiler, 'gcc') && 'gcc' || 'clang' }}
@ -54,7 +59,7 @@ jobs:
fail-fast: false
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: true
persist-credentials: false
@ -90,3 +95,19 @@ jobs:
run: |
make config-$CC_SHORT
make -j$procs CXXSTD=c++20 compile-only
test-compile-result:
runs-on: ubuntu-latest
needs:
- test-compile
if: always()
steps:
- name: Check results
run: |
echo "Needs results: ${{ join(needs.*.result, ',') }}"
if [[ "${{ join(needs.*.result, ',') }}" == *failure* ]] || \
[[ "${{ join(needs.*.result, ',') }}" == *cancelled* ]]; then
echo "Some jobs failed or were cancelled"
exit 1
fi
- run: echo "All good"

View file

@ -1,32 +1,41 @@
name: Check clang sanitizers
on:
# always test main
push:
branches:
- main
pull_request:
merge_group:
# ignore PRs due to time needed
# allow triggering tests, ignores skip check
#push:
# branches: [ main ]
workflow_dispatch:
jobs:
pre_job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
should_skip: ${{ steps.set_output.outputs.should_skip }}
steps:
- id: skip_check
if: ${{ github.event_name != 'merge_group' }}
uses: fkirc/skip-duplicate-actions@v5
with:
# don't run on documentation changes
paths_ignore: '["**/README.md", "docs/**", "guidelines/**"]'
# cancel previous builds if a new commit is pushed
# but never cancel main
cancel_others: ${{ github.ref != 'refs/heads/main' }}
- id: set_output
run: |
if [ "${{ github.event_name }}" = "merge_group" ]; then
echo "should_skip=false" >> $GITHUB_OUTPUT
else
echo "should_skip=${{ steps.skip_check.outputs.should_skip }}" >> $GITHUB_OUTPUT
fi
run_san:
name: Build and run tests
runs-on: ${{ matrix.os }}
needs: pre_job
if: needs.pre_job.outputs.should_skip != 'true'
if: (github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch') && needs.pre_job.outputs.should_skip != 'true'
env:
CC: clang
ASAN_OPTIONS: halt_on_error=1
@ -38,7 +47,7 @@ jobs:
fail-fast: false
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: true
persist-credentials: false
@ -73,3 +82,18 @@ jobs:
run: |
find tests/**/*.err -print -exec cat {} \;
test-sanitizers-result:
runs-on: ubuntu-latest
needs:
- run_san
if: always()
steps:
- name: Check results
run: |
echo "Needs results: ${{ join(needs.*.result, ',') }}"
if [[ "${{ join(needs.*.result, ',') }}" == *failure* ]] || \
[[ "${{ join(needs.*.result, ',') }}" == *cancelled* ]]; then
echo "Some jobs failed or were cancelled"
exit 1
fi
- run: echo "All good"

View file

@ -1,23 +1,20 @@
name: Build and run tests with Verific (Linux)
on:
# always test main
push:
branches:
- main
merge_group:
# test PRs
pull_request:
# allow triggering tests, ignores skip check
merge_group:
#push:
# branches: [ main ]
workflow_dispatch:
jobs:
pre-job:
pre_job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
should_skip: ${{ steps.set_output.outputs.should_skip }}
steps:
- id: skip_check
if: ${{ github.event_name != 'merge_group' }}
uses: fkirc/skip-duplicate-actions@v5
with:
# don't run on documentation changes
@ -26,9 +23,17 @@ jobs:
# but never cancel main
cancel_others: ${{ github.ref != 'refs/heads/main' }}
- id: set_output
run: |
if [ "${{ github.event_name }}" = "merge_group" ]; then
echo "should_skip=false" >> $GITHUB_OUTPUT
else
echo "should_skip=${{ steps.skip_check.outputs.should_skip }}" >> $GITHUB_OUTPUT
fi
test-verific:
needs: pre-job
if: ${{ needs.pre-job.outputs.should_skip != 'true' && github.repository == 'YosysHQ/Yosys' }}
needs: pre_job
if: ${{ needs.pre_job.outputs.should_skip != 'true' && github.repository_owner == 'YosysHQ' }}
runs-on: [self-hosted, linux, x64, fast]
steps:
- name: Checkout Yosys
@ -76,13 +81,13 @@ jobs:
cd tests/svtypes && bash run-test.sh
- name: Run SBY tests
if: ${{ github.ref == 'refs/heads/main' }}
if: ${{ github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch' }}
run: |
make -C sby run_ci
test-pyosys:
needs: pre-job
if: ${{ needs.pre-job.outputs.should_skip != 'true' && github.repository == 'YosysHQ/Yosys' }}
needs: pre_job
if: ${{ needs.pre_job.outputs.should_skip != 'true' && github.repository_owner == 'YosysHQ' }}
runs-on: [self-hosted, linux, x64, fast]
steps:
- name: Checkout Yosys
@ -118,3 +123,20 @@ jobs:
run: |
export PYTHONPATH=${GITHUB_WORKSPACE}/.local/usr/lib/python3/site-packages:$PYTHONPATH
python3 tests/pyosys/run_tests.py
test-verific-result:
runs-on: ubuntu-latest
needs:
- test-verific
- test-pyosys
if: always()
steps:
- name: Check results
run: |
echo "Needs results: ${{ join(needs.*.result, ',') }}"
if [[ "${{ join(needs.*.result, ',') }}" == *failure* ]] || \
[[ "${{ join(needs.*.result, ',') }}" == *cancelled* ]]; then
echo "Some jobs failed or were cancelled"
exit 1
fi
- run: echo "All good"

View file

@ -49,7 +49,7 @@ jobs:
name: Build Wheels | ${{ matrix.os.name }} | ${{ matrix.os.archs }}
runs-on: ${{ matrix.os.runner }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0
submodules: true
@ -108,7 +108,7 @@ jobs:
PATH="$PWD/bison/src:$PATH"
CIBW_BEFORE_BUILD: bash ./.github/workflows/wheels/cibw_before_build.sh
CIBW_TEST_COMMAND: python3 {project}/tests/pyosys/run_tests.py
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v7
with:
name: python-wheels-${{ matrix.os.runner }}
path: ./wheelhouse/*.whl
@ -123,7 +123,7 @@ jobs:
id-token: write
needs: build_wheels
steps:
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
path: "."
pattern: python-wheels-*

View file

@ -5,6 +5,14 @@ List of major changes and improvements between releases
Yosys 0.63 .. Yosys 0.64-dev
--------------------------
* Various
- Removed rarely-used options from ABC/ABC9.
- Calls to "abc -g AND -fast" to map logic to
AND-Inverter Graph form should be replaced with
"aigmap".
- The above change was made to SBY, so we recommend
updating it.
Yosys 0.62 .. Yosys 0.63
--------------------------
* Various

View file

@ -613,6 +613,7 @@ $(eval $(call add_include_file,kernel/bitpattern.h))
$(eval $(call add_include_file,kernel/cellaigs.h))
$(eval $(call add_include_file,kernel/celledges.h))
$(eval $(call add_include_file,kernel/celltypes.h))
$(eval $(call add_include_file,kernel/newcelltypes.h))
$(eval $(call add_include_file,kernel/consteval.h))
$(eval $(call add_include_file,kernel/constids.inc))
$(eval $(call add_include_file,kernel/cost.h))
@ -919,6 +920,7 @@ endif
# Tests that generate .mk with tests/gen-tests-makefile.sh
MK_TEST_DIRS =
MK_TEST_DIRS += tests/arch/analogdevices
MK_TEST_DIRS += tests/arch/anlogic
MK_TEST_DIRS += tests/arch/ecp5
MK_TEST_DIRS += tests/arch/efinix

2
abc

@ -1 +1 @@
Subproject commit 8e401543d3ecf65e3a3631c7a271793a4d356cb0
Subproject commit de0ebae1c5ddbb345871c2e3c4c2a99c9c881ad2

View file

@ -23,7 +23,7 @@
// - zero-width operands
#include "kernel/register.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/rtlil.h"
USING_YOSYS_NAMESPACE
@ -45,8 +45,22 @@ PRIVATE_NAMESPACE_BEGIN
// TODO
//#define ARITH_OPS ID($add), ID($sub), ID($neg)
#define KNOWN_OPS BITWISE_OPS, REDUCE_OPS, LOGIC_OPS, GATE_OPS, ID($pos), CMP_OPS, \
ID($pmux), ID($bmux) /*, ARITH_OPS*/
static constexpr auto known_ops = []() constexpr {
StaticCellTypes::Categories::Category c{};
for (auto id : {BITWISE_OPS})
c.set_id(id);
for (auto id : {REDUCE_OPS})
c.set_id(id);
for (auto id : {LOGIC_OPS})
c.set_id(id);
for (auto id : {GATE_OPS})
c.set_id(id);
for (auto id : {CMP_OPS})
c.set_id(id);
for (auto id : {ID($pos), ID($pmux), ID($bmux)})
c.set_id(id);
return c;
}();
template<typename Writer, typename Lit, Lit CFALSE, Lit CTRUE>
struct Index {
@ -92,7 +106,7 @@ struct Index {
int pos = index_wires(info, m);
for (auto cell : m->cells()) {
if (cell->type.in(KNOWN_OPS) || cell->type.in(ID($scopeinfo), ID($specify2), ID($specify3), ID($input_port)))
if (known_ops(cell->type) || cell->type.in(ID($scopeinfo), ID($specify2), ID($specify3), ID($input_port)))
continue;
Module *submodule = m->design->module(cell->type);
@ -104,7 +118,7 @@ struct Index {
pos += index_module(submodule);
} else {
if (allow_blackboxes) {
info.found_blackboxes.insert(cell);
info.found_blackboxes.insert(cell);
} else {
// Even if we don't allow blackboxes these might still be
// present outside of any traversed input cones, so we
@ -163,6 +177,8 @@ struct Index {
if (!strashing) {
return (static_cast<Writer*>(this))->emit_gate(a, b);
} else {
// AigMaker::node2index
if (a < b) std::swap(a, b);
auto pair = std::make_pair(a, b);
@ -183,7 +199,9 @@ struct Index {
Lit OR(Lit a, Lit b)
{
return NOT(AND(NOT(a), NOT(b)));
Lit not_a = NOT(a);
Lit not_b = NOT(b);
return NOT(AND(not_a, not_b));
}
Lit MUX(Lit a, Lit b, Lit s)
@ -197,17 +215,24 @@ struct Index {
return b;
}
return OR(AND(a, NOT(s)), AND(b, s));
Lit not_s = NOT(s);
Lit a_active = AND(a, not_s);
Lit b_active = AND(b, s);
return OR(a_active, b_active);
}
Lit XOR(Lit a, Lit b)
{
return OR(AND(a, NOT(b)), AND(NOT(a), b));
Lit not_a = NOT(a);
Lit not_b = NOT(b);
Lit a_and_not_b = AND(a, not_b);
Lit not_a_and_b = AND(not_a, b);
return OR(a_and_not_b, not_a_and_b);
}
Lit XNOR(Lit a, Lit b)
{
return NOT(OR(AND(a, NOT(b)), AND(NOT(a), b)));
return NOT(XOR(a, b));
}
Lit CARRY(Lit a, Lit b, Lit c)
@ -219,7 +244,10 @@ struct Index {
return AND(a, b);
}
}
return OR(AND(a, b), AND(c, OR(a, b)));
Lit a_or_b = OR(a, b);
Lit a_or_b_and_c = AND(c, a_or_b);
Lit a_and_b = AND(a, b);
return OR(a_and_b, a_or_b_and_c);
}
Lit REDUCE(std::vector<Lit> lits, bool op_xor=false)
@ -269,7 +297,7 @@ struct Index {
} else if (cell->type.in(ID($lt), ID($le), ID($gt), ID($ge))) {
if (cell->type.in(ID($gt), ID($ge)))
std::swap(aport, bport);
int carry = cell->type.in(ID($le), ID($ge)) ? CFALSE : CTRUE;
int carry = cell->type.in(ID($le), ID($ge)) ? CFALSE : CTRUE;
Lit a = Writer::EMPTY_LIT;
Lit b = Writer::EMPTY_LIT;
// TODO: this might not be the most economic structure; revisit at a later date
@ -367,7 +395,7 @@ struct Index {
} else if (cell->type.in(ID($xor), ID($_XOR_))) {
return XOR(a, b);
} else if (cell->type.in(ID($xnor), ID($_XNOR_))) {
return NOT(XOR(a, b));
return XNOR(a, b);
} else if (cell->type.in(ID($_ANDNOT_))) {
return AND(a, NOT(b));
} else if (cell->type.in(ID($_ORNOT_))) {
@ -387,7 +415,9 @@ struct Index {
if (oport == ID::Y) {
return XOR(ab, c);
} else /* oport == ID::X */ {
return OR(AND(a, b), AND(c, ab));
Lit a_and_b = AND(a, b);
Lit c_and_ab = AND(c, ab);
return OR(a_and_b, c_and_ab);
}
} else if (cell->type.in(ID($_AOI3_), ID($_OAI3_), ID($_AOI4_), ID($_OAI4_))) {
Lit c, d;
@ -398,10 +428,15 @@ struct Index {
else
d = cell->type == ID($_AOI3_) ? CTRUE : CFALSE;
if (/* aoi */ cell->type.in(ID($_AOI3_), ID($_AOI4_)))
return NOT(OR(AND(a, b), AND(c, d)));
else
return NOT(AND(OR(a, b), OR(c, d)));
if (/* aoi */ cell->type.in(ID($_AOI3_), ID($_AOI4_))) {
Lit a_and_b = AND(a, b);
Lit c_and_d = AND(c, d);
return NOT(OR(a_and_b, c_and_d));
} else {
Lit a_or_b = OR(a, b);
Lit c_or_d = OR(c, d);
return NOT(AND(a_or_b, c_or_d));
}
} else {
log_abort();
}
@ -422,7 +457,11 @@ struct Index {
sels.push_back(NOT(s));
}
return OR(AND(REDUCE(sels), a), NOT(REDUCE(bar)));
Lit reduce_sels = REDUCE(sels);
Lit reduce_sels_and_a = AND(reduce_sels, a);
Lit reduce_bar = NOT(REDUCE(bar));
return OR(reduce_sels_and_a, reduce_bar);
} else if (cell->type == ID($bmux)) {
SigSpec aport = cell->getPort(ID::A);
SigSpec sport = cell->getPort(ID::S);
@ -579,7 +618,7 @@ struct Index {
// an output of a cell
Cell *driver = bit.wire->driverCell();
if (driver->type.in(KNOWN_OPS)) {
if (known_ops(driver->type)) {
ret = impl_op(cursor, driver, bit.wire->driverPort(), bit.offset);
} else {
Module *def = cursor.enter(*this, driver);
@ -730,15 +769,15 @@ struct AigerWriter : Index<AigerWriter, unsigned int, 0, 1> {
// populate inputs
std::vector<SigBit> inputs;
for (auto id : top->ports) {
Wire *w = top->wire(id);
log_assert(w);
if (w->port_input && !w->port_output)
for (int i = 0; i < w->width; i++) {
pi_literal(SigBit(w, i)) = lit_counter;
inputs.push_back(SigBit(w, i));
lit_counter += 2;
ninputs++;
}
Wire *w = top->wire(id);
log_assert(w);
if (w->port_input && !w->port_output)
for (int i = 0; i < w->width; i++) {
pi_literal(SigBit(w, i)) = lit_counter;
inputs.push_back(SigBit(w, i));
lit_counter += 2;
ninputs++;
}
}
this->f = f;
@ -746,27 +785,27 @@ struct AigerWriter : Index<AigerWriter, unsigned int, 0, 1> {
write_header();
// insert padding where output literals will go (once known)
for (auto id : top->ports) {
Wire *w = top->wire(id);
log_assert(w);
if (w->port_output) {
for (auto bit : SigSpec(w)) {
(void) bit;
char buf[16];
snprintf(buf, sizeof(buf) - 1, "%08d\n", 0);
f->write(buf, strlen(buf));
noutputs++;
Wire *w = top->wire(id);
log_assert(w);
if (w->port_output) {
for (auto bit : SigSpec(w)) {
(void) bit;
char buf[16];
snprintf(buf, sizeof(buf) - 1, "%08d\n", 0);
f->write(buf, strlen(buf));
noutputs++;
}
}
}
}
auto data_start = f->tellp();
// now the guts
std::vector<std::pair<SigBit, int>> outputs;
for (auto w : top->wires())
if (w->port_output) {
for (auto bit : SigSpec(w))
outputs.push_back({bit, eval_po(bit)});
}
if (w->port_output) {
for (auto bit : SigSpec(w))
outputs.push_back({bit, eval_po(bit)});
}
auto data_end = f->tellp();
// revisit header and the list of outputs
@ -871,33 +910,34 @@ struct XAigerAnalysis : Index<XAigerAnalysis, int, 0, 0> {
Wire *w = top->wire(id);
log_assert(w);
if (w->port_input && !w->port_output)
for (int i = 0; i < w->width; i++)
pi_literal(SigBit(w, i)) = 0;
for (int i = 0; i < w->width; i++)
pi_literal(SigBit(w, i)) = 0;
}
HierCursor cursor;
for (auto box : top_minfo->found_blackboxes) {
Module *def = design->module(box->type);
if (!(def && def->has_attribute(ID::abc9_box_id)))
for (auto &conn : box->connections_)
if (box->port_dir(conn.first) != RTLIL::PD_INPUT)
for (auto bit : conn.second)
pi_literal(bit, &cursor) = 0;
for (auto &conn : box->connections_)
if (box->port_dir(conn.first) != RTLIL::PD_INPUT)
for (auto bit : conn.second)
pi_literal(bit, &cursor) = 0;
}
for (auto w : top->wires())
if (w->port_output) {
for (auto bit : SigSpec(w))
(void) eval_po(bit);
for (auto w : top->wires()) {
if (w->port_output) {
for (auto bit : SigSpec(w))
(void) eval_po(bit);
}
}
for (auto box : top_minfo->found_blackboxes) {
Module *def = design->module(box->type);
if (!(def && def->has_attribute(ID::abc9_box_id)))
for (auto &conn : box->connections_)
if (box->port_dir(conn.first) == RTLIL::PD_INPUT)
for (auto bit : conn.second)
(void) eval_po(bit);
for (auto &conn : box->connections_)
if (box->port_dir(conn.first) == RTLIL::PD_INPUT)
for (auto bit : conn.second)
(void) eval_po(bit);
}
}
};
@ -916,15 +956,15 @@ struct XAigerWriter : AigerWriter {
std::vector<HierBit> pos;
std::vector<HierBit> pis;
// * The aiger output port sequence is COs (inputs to modeled boxes),
// inputs to opaque boxes, then module outputs. COs going first is
// required by abc.
// * proper_pos_counter counts ports which follow after COs
// * The mapping file `pseudopo` and `po` statements use indexing relative
// to the first port following COs.
// * If a module output is directly driven by an opaque box, the emission
// of the po statement in the mapping file is skipped. This is done to
// aid re-integration of the mapped result.
// * The aiger output port sequence is COs (inputs to modeled boxes),
// inputs to opaque boxes, then module outputs. COs going first is
// required by abc.
// * proper_pos_counter counts ports which follow after COs
// * The mapping file `pseudopo` and `po` statements use indexing relative
// to the first port following COs.
// * If a module output is directly driven by an opaque box, the emission
// of the po statement in the mapping file is skipped. This is done to
// aid re-integration of the mapped result.
int proper_pos_counter = 0;
pool<SigBit> driven_by_opaque_box;
@ -1331,41 +1371,50 @@ struct Aiger2Backend : Backend {
log(" perform structural hashing while writing\n");
log("\n");
log(" -flatten\n");
log(" allow descending into submodules and write a flattened view of the design\n");
log(" hierarchy starting at the selected top\n");
log("\n");
log(" allow descending into submodules and write a flattened view of the design\n");
log(" hierarchy starting at the selected top\n");
log("\n");
log("This command is able to ingest all combinational cells except for:\n");
log("\n");
pool<IdString> supported = {KNOWN_OPS};
CellTypes ct;
ct.setup_internals_eval();
log(" ");
int col = 0;
for (auto pair : ct.cell_types)
if (!supported.count(pair.first)) {
if (col + pair.first.size() + 2 > 72) {
for (size_t i = 0; i < StaticCellTypes::builder.count; i++) {
auto &cell = StaticCellTypes::builder.cells[i];
if (!cell.features.is_evaluable)
continue;
if (cell.features.is_stdcell)
continue;
if (known_ops(cell.type))
continue;
std::string name = log_id(cell.type);
if (col + name.size() + 2 > 72) {
log("\n ");
col = 0;
}
col += pair.first.size() + 2;
log("%s, ", log_id(pair.first));
col += name.size() + 2;
log("%s, ", name.c_str());
}
log("\n");
log("\n");
log("And all combinational gates except for:\n");
log("\n");
CellTypes ct2;
ct2.setup_stdcells();
log(" ");
col = 0;
for (auto pair : ct2.cell_types)
if (!supported.count(pair.first)) {
if (col + pair.first.size() + 2 > 72) {
for (size_t i = 0; i < StaticCellTypes::builder.count; i++) {
auto &cell = StaticCellTypes::builder.cells[i];
if (!cell.features.is_evaluable)
continue;
if (!cell.features.is_stdcell)
continue;
if (known_ops(cell.type))
continue;
std::string name = log_id(cell.type);
if (col + name.size() + 2 > 72) {
log("\n ");
col = 0;
}
col += pair.first.size() + 2;
log("%s, ", log_id(pair.first));
col += name.size() + 2;
log("%s, ", name.c_str());
}
log("\n");
}
@ -1423,20 +1472,20 @@ struct XAiger2Backend : Backend {
log(" perform structural hashing while writing\n");
log("\n");
log(" -flatten\n");
log(" allow descending into submodules and write a flattened view of the design\n");
log(" hierarchy starting at the selected top\n");
log("\n");
log(" -mapping_prep\n");
log(" after the file is written, prepare the module for reintegration of\n");
log(" a mapping in a subsequent command. all cells which are not blackboxed nor\n");
log(" whiteboxed are removed from the design as well as all wires which only\n");
log(" connect to removed cells\n");
log(" (conflicts with -flatten)\n");
log("\n");
log(" -map2 <file>\n");
log(" write a map2 file which 'read_xaiger2 -sc_mapping' can read to\n");
log(" reintegrate a mapping\n");
log(" (conflicts with -flatten)\n");
log(" allow descending into submodules and write a flattened view of the design\n");
log(" hierarchy starting at the selected top\n");
log("\n");
log(" -mapping_prep\n");
log(" after the file is written, prepare the module for reintegration of\n");
log(" a mapping in a subsequent command. all cells which are not blackboxed nor\n");
log(" whiteboxed are removed from the design as well as all wires which only\n");
log(" connect to removed cells\n");
log(" (conflicts with -flatten)\n");
log("\n");
log(" -map2 <file>\n");
log(" write a map2 file which 'read_xaiger2 -sc_mapping' can read to\n");
log(" reintegrate a mapping\n");
log(" (conflicts with -flatten)\n");
log("\n");
}

View file

@ -24,7 +24,7 @@
#include "kernel/rtlil.h"
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/log.h"
#include <string>
@ -61,7 +61,7 @@ struct BlifDumper
RTLIL::Module *module;
RTLIL::Design *design;
BlifDumperConfig *config;
CellTypes ct;
NewCellTypes ct;
SigMap sigmap;
dict<SigBit, int> init_bits;

View file

@ -2776,7 +2776,8 @@ struct CxxrtlWorker {
{
RTLIL::Module *top_module = nullptr;
std::vector<RTLIL::Module*> modules;
TopoSort<RTLIL::Module*> topo_design;
using Order = IdString::compare_ptr_by_name<RTLIL::NamedObject>;
TopoSort<RTLIL::Module*, Order> topo_design;
for (auto module : design->modules()) {
if (!design->selected_module(module))
continue;

View file

@ -614,7 +614,7 @@ struct value : public expr_base<value<Bits>> {
int64_t divisor_shift = divisor.ctlz() - dividend.ctlz();
assert(divisor_shift >= 0);
divisor = divisor.shl(value<Bits>{(chunk::type) divisor_shift});
for (size_t step = 0; step <= divisor_shift; step++) {
for (size_t step = 0; step <= (uint64_t) divisor_shift; step++) {
quotient = quotient.shl(value<Bits>{1u});
if (!dividend.ucmp(divisor)) {
dividend = dividend.sub(divisor);
@ -1119,7 +1119,7 @@ struct fmt_part {
case STRING: {
buf.reserve(Bits/8);
for (int i = 0; i < Bits; i += 8) {
for (size_t i = 0; i < Bits; i += 8) {
char ch = 0;
for (int j = 0; j < 8 && i + j < int(Bits); j++)
if (val.bit(i + j))

View file

@ -23,7 +23,7 @@
#include "kernel/rtlil.h"
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/log.h"
#include <string>
@ -138,7 +138,7 @@ struct EdifBackend : public Backend {
bool lsbidx = false;
std::map<RTLIL::IdString, std::map<RTLIL::IdString, int>> lib_cell_ports;
bool nogndvcc = false, gndvccy = false, keepmode = false;
CellTypes ct(design);
NewCellTypes ct(design);
EdifNames edif_names;
size_t argidx;

View file

@ -20,7 +20,7 @@
#include "kernel/rtlil.h"
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/log.h"
#include <string>
@ -117,7 +117,7 @@ struct IntersynthBackend : public Backend {
std::set<std::string> conntypes_code, celltypes_code;
std::string netlists_code;
CellTypes ct(design);
NewCellTypes ct(design);
for (auto lib : libs)
ct.setup_design(lib);

View file

@ -20,7 +20,7 @@
#include "kernel/rtlil.h"
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/log.h"
#include "kernel/mem.h"
#include "libs/json11/json11.hpp"
@ -32,7 +32,7 @@ PRIVATE_NAMESPACE_BEGIN
struct Smt2Worker
{
CellTypes ct;
NewCellTypes ct;
SigMap sigmap;
RTLIL::Module *module;
bool bvmode, memmode, wiresmode, verbose, statebv, statedt, forallmode;

View file

@ -20,7 +20,7 @@
#include "kernel/rtlil.h"
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/log.h"
#include <string>
@ -29,7 +29,7 @@ PRIVATE_NAMESPACE_BEGIN
struct SmvWorker
{
CellTypes ct;
NewCellTypes ct;
SigMap sigmap;
RTLIL::Module *module;
std::ostream &f;

View file

@ -37,7 +37,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "aigerparse.h"
YOSYS_NAMESPACE_BEGIN

View file

@ -342,6 +342,9 @@ struct AST_INTERNAL::ProcessGenerator
// The most recently assigned $print or $check cell \PRIORITY.
int last_effect_priority;
// Track which signals have been assigned in current_case to avoid unnecessary removeSignalFromCaseTree calls
pool<RTLIL::SigBit> current_case_assigned_bits;
ProcessGenerator(std::unique_ptr<AstNode> a, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(std::move(a)), initSyncSignals(initSyncSignalsArg), last_effect_priority(0)
{
// rewrite lookahead references
@ -430,6 +433,10 @@ struct AST_INTERNAL::ProcessGenerator
subst_rvalue_map = subst_lvalue_from.to_sigbit_dict(RTLIL::SigSpec(RTLIL::State::Sx, GetSize(subst_lvalue_from)));
} else {
addChunkActions(current_case->actions, subst_lvalue_to, subst_lvalue_from);
// Track initial assignments
for (auto &bit : subst_lvalue_to)
if (bit.wire != NULL)
current_case_assigned_bits.insert(bit);
}
// process the AST
@ -557,14 +564,42 @@ struct AST_INTERNAL::ProcessGenerator
// e.g. when the last statement in the code "a = 23; if (b) a = 42; a = 0;" is processed this
// function is called to clean up the first two assignments as they are overwritten by
// the third assignment.
void removeSignalFromCaseTree(const RTLIL::SigSpec &pattern, RTLIL::CaseRule *cs)
void removeSignalFromCaseTree(const pool<RTLIL::SigBit> &pattern_bits, RTLIL::CaseRule *cs)
{
for (auto it = cs->actions.begin(); it != cs->actions.end(); it++)
it->first.remove2(pattern, &it->second);
// Optimization: check actions in reverse order and stop early if we've found all pattern bits
pool<RTLIL::SigBit> remaining_bits = pattern_bits;
for (auto it = cs->actions.rbegin(); it != cs->actions.rend(); ++it) {
bool has_pattern = false;
for (auto &bit : it->first) {
if (bit.wire != NULL && remaining_bits.count(bit)) {
has_pattern = true;
remaining_bits.erase(bit);
}
}
if (has_pattern) {
it->first.remove2(pattern_bits, &it->second);
}
// Early exit if we've processed all bits in pattern
if (remaining_bits.empty())
break;
}
for (auto it = cs->switches.begin(); it != cs->switches.end(); it++)
for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++)
removeSignalFromCaseTree(pattern, *it2);
removeSignalFromCaseTree(pattern_bits, *it2);
}
void removeSignalFromCaseTree(const RTLIL::SigSpec &pattern, RTLIL::CaseRule *cs)
{
pool<RTLIL::SigBit> pattern_bits;
pattern_bits.reserve(pattern.size());
for (auto &bit : pattern)
if (bit.wire != NULL)
pattern_bits.insert(bit);
removeSignalFromCaseTree(pattern_bits, cs);
}
// add an assignment (aka "action") but split it up in chunks. this way huge assignments
@ -623,7 +658,23 @@ struct AST_INTERNAL::ProcessGenerator
subst_rvalue_map.set(unmapped_lvalue[i], rvalue[i]);
}
removeSignalFromCaseTree(lvalue, current_case);
// Check if any bits in lvalue have been assigned before in current_case
bool has_overlap = false;
for (auto &bit : lvalue) {
if (bit.wire != NULL && current_case_assigned_bits.count(bit)) {
has_overlap = true;
break;
}
}
if (has_overlap)
removeSignalFromCaseTree(lvalue, current_case);
// Track newly assigned bits
for (auto &bit : lvalue)
if (bit.wire != NULL)
current_case_assigned_bits.insert(bit);
remove_unwanted_lvalue_bits(lvalue, rvalue);
current_case->actions.push_back(RTLIL::SigSig(lvalue, rvalue));
}
@ -670,9 +721,15 @@ struct AST_INTERNAL::ProcessGenerator
RTLIL::CaseRule *backup_case = current_case;
current_case = new RTLIL::CaseRule;
pool<RTLIL::SigBit> backup_assigned_bits = std::move(current_case_assigned_bits);
current_case_assigned_bits.clear();
set_src_attr(current_case, child.get());
last_generated_case = current_case;
addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue);
// Track temp assignments
for (auto &bit : this_case_eq_ltemp)
if (bit.wire != NULL)
current_case_assigned_bits.insert(bit);
for (auto& node : child->children) {
if (node->type == AST_DEFAULT)
default_case = current_case;
@ -686,6 +743,7 @@ struct AST_INTERNAL::ProcessGenerator
else
log_assert(current_case->compare.size() == 0);
current_case = backup_case;
current_case_assigned_bits = std::move(backup_assigned_bits);
subst_lvalue_map.restore();
subst_rvalue_map.restore();
@ -714,8 +772,24 @@ struct AST_INTERNAL::ProcessGenerator
subst_rvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]);
this_case_eq_lvalue.replace(subst_lvalue_map.stdmap());
removeSignalFromCaseTree(this_case_eq_lvalue, current_case);
// Check if any bits in lvalue have been assigned before in current_case
bool has_overlap = false;
for (auto &bit : this_case_eq_lvalue) {
if (bit.wire != NULL && current_case_assigned_bits.count(bit)) {
has_overlap = true;
break;
}
}
if (has_overlap)
removeSignalFromCaseTree(this_case_eq_lvalue, current_case);
addChunkActions(current_case->actions, this_case_eq_lvalue, this_case_eq_ltemp);
// Track newly assigned bits
for (auto &bit : this_case_eq_lvalue)
if (bit.wire != NULL)
current_case_assigned_bits.insert(bit);
}
break;

View file

@ -269,6 +269,83 @@ static int add_dimension(AstNode *node, AstNode *rnode)
node->input_error("Unpacked array in packed struct/union member %s\n", node->str);
}
// Check if node is an unexpanded array reference (AST_IDENTIFIER -> AST_MEMORY without indexing)
static bool is_unexpanded_array_ref(AstNode *node)
{
if (node->type != AST_IDENTIFIER)
return false;
if (node->id2ast == nullptr || node->id2ast->type != AST_MEMORY)
return false;
// No indexing children = whole array reference
return node->children.empty();
}
// Check if two memories have compatible unpacked dimensions for array assignment
static bool arrays_have_compatible_dims(AstNode *mem_a, AstNode *mem_b)
{
if (mem_a->unpacked_dimensions != mem_b->unpacked_dimensions)
return false;
for (int i = 0; i < mem_a->unpacked_dimensions; i++) {
if (mem_a->dimensions[i].range_width != mem_b->dimensions[i].range_width)
return false;
}
// Also check packed dimensions (element width)
int a_width, a_size, a_bits;
int b_width, b_size, b_bits;
mem_a->meminfo(a_width, a_size, a_bits);
mem_b->meminfo(b_width, b_size, b_bits);
return a_width == b_width;
}
// Convert per-dimension element positions to declared index values.
// Position 0 is the first declared element for each unpacked dimension.
static std::vector<int> array_indices_from_position(AstNode *mem, const std::vector<int> &position)
{
int num_dims = mem->unpacked_dimensions;
log_assert(GetSize(position) == num_dims);
std::vector<int> indices(num_dims);
for (int d = 0; d < num_dims; d++) {
int low = mem->dimensions[d].range_right;
int high = low + mem->dimensions[d].range_width - 1;
indices[d] = mem->dimensions[d].range_swapped ? (low + position[d]) : (high - position[d]);
}
return indices;
}
// Generate all element positions for a multi-dimensional unpacked array and
// call callback once for each combination.
static void foreach_array_position(AstNode *mem, std::function<void(const std::vector<int>&)> callback)
{
int num_dims = mem->unpacked_dimensions;
if (num_dims == 0) {
callback({});
return;
}
std::vector<int> position(num_dims, 0);
std::vector<int> sizes(num_dims);
for (int d = 0; d < num_dims; d++)
sizes[d] = mem->dimensions[d].range_width;
// Iterate through all position combinations (rightmost dimension fastest).
while (true) {
callback(position);
int d = num_dims - 1;
while (d >= 0) {
position[d]++;
if (position[d] < sizes[d])
break;
position[d] = 0;
d--;
}
if (d < 0)
break;
}
}
static int size_packed_struct(AstNode *snode, int base_offset)
{
// Struct members will be laid out in the structure contiguously from left to right.
@ -3200,6 +3277,123 @@ skip_dynamic_range_lvalue_expansion:;
}
}
// Expand array assignment: arr_out = arr_in OR arr_out = cond ? arr_a : arr_b
// Supports multi-dimensional unpacked arrays
if ((type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE || type == AST_ASSIGN) &&
is_unexpanded_array_ref(children[0].get()))
{
AstNode *lhs = children[0].get();
AstNode *rhs = children[1].get();
AstNode *lhs_mem = lhs->id2ast;
// Case 1: Direct array assignment (b = a)
bool is_direct_assign = is_unexpanded_array_ref(rhs);
// Case 2: Ternary array assignment (out = sel ? a : b)
bool is_ternary_assign = (rhs->type == AST_TERNARY &&
is_unexpanded_array_ref(rhs->children[1].get()) &&
is_unexpanded_array_ref(rhs->children[2].get()));
if (is_direct_assign || is_ternary_assign)
{
AstNode *direct_rhs_mem = nullptr;
AstNode *true_mem = nullptr;
AstNode *false_mem = nullptr;
// Validate array compatibility
if (is_direct_assign) {
direct_rhs_mem = rhs->id2ast;
if (!arrays_have_compatible_dims(lhs_mem, direct_rhs_mem))
input_error("Array dimension mismatch in assignment\n");
} else {
true_mem = rhs->children[1]->id2ast;
false_mem = rhs->children[2]->id2ast;
if (!arrays_have_compatible_dims(lhs_mem, true_mem) ||
!arrays_have_compatible_dims(lhs_mem, false_mem))
input_error("Array dimension mismatch in ternary expression\n");
}
int num_dims = lhs_mem->unpacked_dimensions;
// Helper to add index to an identifier clone
auto add_indices_to_id = [&](std::unique_ptr<AstNode> id, const std::vector<int>& indices) {
if (num_dims == 1) {
// Single dimension: use AST_RANGE
id->children.push_back(std::make_unique<AstNode>(location, AST_RANGE,
mkconst_int(location, indices[0], true)));
} else {
// Multiple dimensions: use AST_MULTIRANGE
auto multirange = std::make_unique<AstNode>(location, AST_MULTIRANGE);
for (int idx : indices) {
multirange->children.push_back(std::make_unique<AstNode>(location, AST_RANGE,
mkconst_int(location, idx, true)));
}
id->children.push_back(std::move(multirange));
}
id->integer = num_dims;
// Reset basic_prep so multirange gets resolved during subsequent simplify passes
id->basic_prep = false;
return id;
};
// Calculate total number of elements and warn if large
int total_elements = 1;
for (int d = 0; d < num_dims; d++)
total_elements *= lhs_mem->dimensions[d].range_width;
if (total_elements > 10000)
log_warning("Expanding array assignment with %d elements at %s, this may be slow.\n",
total_elements, location.to_string().c_str());
// Collect all assignments
std::vector<std::unique_ptr<AstNode>> assignments;
foreach_array_position(lhs_mem, [&](const std::vector<int>& position) {
auto lhs_indices = array_indices_from_position(lhs_mem, position);
auto lhs_idx = add_indices_to_id(lhs->clone(), lhs_indices);
std::unique_ptr<AstNode> rhs_expr;
if (is_direct_assign) {
auto rhs_indices = array_indices_from_position(direct_rhs_mem, position);
rhs_expr = add_indices_to_id(rhs->clone(), rhs_indices);
} else {
// Ternary case
AstNode *cond = rhs->children[0].get();
AstNode *true_val = rhs->children[1].get();
AstNode *false_val = rhs->children[2].get();
auto true_indices = array_indices_from_position(true_mem, position);
auto false_indices = array_indices_from_position(false_mem, position);
auto true_idx = add_indices_to_id(true_val->clone(), true_indices);
auto false_idx = add_indices_to_id(false_val->clone(), false_indices);
rhs_expr = std::make_unique<AstNode>(location, AST_TERNARY,
cond->clone(), std::move(true_idx), std::move(false_idx));
}
auto assign = std::make_unique<AstNode>(location, type,
std::move(lhs_idx), std::move(rhs_expr));
assign->was_checked = true;
assignments.push_back(std::move(assign));
});
// For continuous assignments, add to module; for procedural, use block
if (type == AST_ASSIGN) {
// Add all but last to module
for (size_t i = 0; i + 1 < assignments.size(); i++)
current_ast_mod->children.push_back(std::move(assignments[i]));
// Last one replaces current node
newNode = std::move(assignments.back());
} else {
// Wrap in AST_BLOCK for procedural
newNode = std::make_unique<AstNode>(location, AST_BLOCK);
for (auto& assign : assignments)
newNode->children.push_back(std::move(assign));
}
goto apply_newNode;
}
}
// assignment with memory in left-hand side expression -> replace with memory write port
if (stage > 1 && (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) && children[0]->type == AST_IDENTIFIER &&
children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY && children[0]->id2ast->children.size() >= 2 &&

View file

@ -286,6 +286,7 @@ struct RTLILFrontendWorker {
if (width > MAX_CONST_WIDTH)
error("Constant width %lld out of range before `%s`.", width, error_token());
bits.reserve(width);
int start_idx = idx;
while (true) {
RTLIL::State bit;
switch (line[idx]) {
@ -300,8 +301,9 @@ struct RTLILFrontendWorker {
bits.push_back(bit);
++idx;
}
done:
std::reverse(bits.begin(), bits.end());
done:
if (start_idx < idx)
std::reverse(bits.begin(), bits.end());
if (GetSize(bits) > width)
bits.resize(width);

View file

@ -66,14 +66,7 @@ struct AigMaker
Cell *cell;
idict<AigNode> aig_indices;
int the_true_node;
int the_false_node;
AigMaker(Aig *aig, Cell *cell) : aig(aig), cell(cell)
{
the_true_node = -1;
the_false_node = -1;
}
AigMaker(Aig *aig, Cell *cell) : aig(aig), cell(cell) {}
int node2index(const AigNode &node)
{

View file

@ -21,6 +21,7 @@
#define CELLTYPES_H
#include "kernel/yosys.h"
#include "kernel/newcelltypes.h"
YOSYS_NAMESPACE_BEGIN
@ -87,22 +88,22 @@ struct CellTypes
{
setup_internals_eval();
setup_type(ID($tribuf), {ID::A, ID::EN}, {ID::Y}, true);
setup_type(ID($tribuf), {ID::A, ID::EN}, {ID::Y});
setup_type(ID($assert), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
setup_type(ID($assume), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
setup_type(ID($live), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
setup_type(ID($fair), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
setup_type(ID($cover), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
setup_type(ID($initstate), pool<RTLIL::IdString>(), {ID::Y}, true);
setup_type(ID($anyconst), pool<RTLIL::IdString>(), {ID::Y}, true);
setup_type(ID($anyseq), pool<RTLIL::IdString>(), {ID::Y}, true);
setup_type(ID($allconst), pool<RTLIL::IdString>(), {ID::Y}, true);
setup_type(ID($allseq), pool<RTLIL::IdString>(), {ID::Y}, true);
setup_type(ID($equiv), {ID::A, ID::B}, {ID::Y}, true);
setup_type(ID($specify2), {ID::EN, ID::SRC, ID::DST}, pool<RTLIL::IdString>(), true);
setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool<RTLIL::IdString>(), true);
setup_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, pool<RTLIL::IdString>(), true);
setup_type(ID($assert), {ID::A, ID::EN}, pool<RTLIL::IdString>());
setup_type(ID($assume), {ID::A, ID::EN}, pool<RTLIL::IdString>());
setup_type(ID($live), {ID::A, ID::EN}, pool<RTLIL::IdString>());
setup_type(ID($fair), {ID::A, ID::EN}, pool<RTLIL::IdString>());
setup_type(ID($cover), {ID::A, ID::EN}, pool<RTLIL::IdString>());
setup_type(ID($initstate), pool<RTLIL::IdString>(), {ID::Y});
setup_type(ID($anyconst), pool<RTLIL::IdString>(), {ID::Y});
setup_type(ID($anyseq), pool<RTLIL::IdString>(), {ID::Y});
setup_type(ID($allconst), pool<RTLIL::IdString>(), {ID::Y});
setup_type(ID($allseq), pool<RTLIL::IdString>(), {ID::Y});
setup_type(ID($equiv), {ID::A, ID::B}, {ID::Y});
setup_type(ID($specify2), {ID::EN, ID::SRC, ID::DST}, pool<RTLIL::IdString>());
setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool<RTLIL::IdString>());
setup_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, pool<RTLIL::IdString>());
setup_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, pool<RTLIL::IdString>());
setup_type(ID($check), {ID::A, ID::EN, ID::ARGS, ID::TRG}, pool<RTLIL::IdString>());
setup_type(ID($set_tag), {ID::A, ID::SET, ID::CLR}, {ID::Y});
@ -195,7 +196,7 @@ struct CellTypes
{
setup_stdcells_eval();
setup_type(ID($_TBUF_), {ID::A, ID::E}, {ID::Y}, true);
setup_type(ID($_TBUF_), {ID::A, ID::E}, {ID::Y});
}
void setup_stdcells_eval()
@ -548,9 +549,6 @@ struct CellTypes
}
};
// initialized by yosys_setup()
extern CellTypes yosys_celltypes;
YOSYS_NAMESPACE_END
#endif

View file

@ -24,6 +24,7 @@
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/macc.h"
#include "kernel/newcelltypes.h"
YOSYS_NAMESPACE_BEGIN
@ -44,9 +45,8 @@ struct ConstEval
ConstEval(RTLIL::Module *module, RTLIL::State defaultval = RTLIL::State::Sm) : module(module), assign_map(module), defaultval(defaultval)
{
CellTypes ct;
ct.setup_internals();
ct.setup_stdcells();
auto ct = NewCellTypes();
ct.static_cell_types = StaticCellTypes::Compat::nomem_noff;
for (auto &it : module->cells_) {
if (!ct.cell_known(it.second->type))

View file

@ -23,6 +23,7 @@
#define CXXOPTS_VECTOR_DELIMITER '\0'
#include "libs/cxxopts/include/cxxopts.hpp"
#include <iostream>
#include <chrono>
#ifdef YOSYS_ENABLE_READLINE
# include <readline/readline.h>
@ -195,6 +196,7 @@ namespace Yosys {
int main(int argc, char **argv)
{
auto wall_clock_start = std::chrono::steady_clock::now();
std::string frontend_command = "auto";
std::string backend_command = "auto";
std::vector<std::string> vlog_defines;
@ -700,8 +702,11 @@ int main(int argc, char **argv)
meminfo = stringf(", MEM: %.2f MB peak",
ru_buffer.ru_maxrss / (1024.0 * 1024.0));
#endif
log("End of script. Logfile hash: %s%sCPU: user %.2fs system %.2fs%s\n", hash,
stats_divider.c_str(), ru_buffer.ru_utime.tv_sec + 1e-6 * ru_buffer.ru_utime.tv_usec,
double wall_seconds = std::chrono::duration<double>(
std::chrono::steady_clock::now() - wall_clock_start).count();
log("End of script. Logfile hash: %s%stime: %.2fs, user: %.2fs, system: %.2fs%s\n", hash,
stats_divider.c_str(), wall_seconds, ru_buffer.ru_utime.tv_sec + 1e-6 * ru_buffer.ru_utime.tv_usec,
ru_buffer.ru_stime.tv_sec + 1e-6 * ru_buffer.ru_stime.tv_usec, meminfo.c_str());
#endif
log("%s\n", yosys_maybe_version());

View file

@ -25,7 +25,7 @@
#include "kernel/rtlil.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
YOSYS_NAMESPACE_BEGIN
@ -1093,10 +1093,10 @@ private:
struct DriverMap
{
CellTypes celltypes;
NewCellTypes celltypes;
DriverMap() { celltypes.setup(); }
DriverMap(Design *design) { celltypes.setup(); celltypes.setup_design(design); }
DriverMap(Design *design) { celltypes.setup(design); }
private:

View file

@ -22,6 +22,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/threading.h"
YOSYS_NAMESPACE_BEGIN
@ -35,34 +36,55 @@ struct FfInitVals
sigmap = sigmap_;
initbits.clear();
for (auto wire : module->wires())
if (wire->attributes.count(ID::init))
process_wire(wire);
}
void process_wire(RTLIL::Wire *wire)
{
SigSpec wirebits = (*sigmap)(wire);
Const initval = wire->attributes.at(ID::init);
for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++)
{
if (wire->attributes.count(ID::init) == 0)
SigBit bit = wirebits[i];
State val = initval[i];
if (val != State::S0 && val != State::S1 && bit.wire != nullptr)
continue;
SigSpec wirebits = (*sigmap)(wire);
Const initval = wire->attributes.at(ID::init);
for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++)
{
SigBit bit = wirebits[i];
State val = initval[i];
if (val != State::S0 && val != State::S1 && bit.wire != nullptr)
continue;
if (initbits.count(bit)) {
if (initbits.at(bit).first != val)
log_error("Conflicting init values for signal %s (%s = %s != %s).\n",
log_signal(bit), log_signal(SigBit(wire, i)),
log_signal(val), log_signal(initbits.at(bit).first));
continue;
}
initbits[bit] = std::make_pair(val,SigBit(wire,i));
if (initbits.count(bit)) {
if (initbits.at(bit).first != val)
log_error("Conflicting init values for signal %s (%s = %s != %s).\n",
log_signal(bit), log_signal(SigBit(wire, i)),
log_signal(val), log_signal(initbits.at(bit).first));
continue;
}
initbits[bit] = std::make_pair(val,SigBit(wire,i));
}
}
void set_parallel(const SigMapView *sigmap_, ParallelDispatchThreadPool &thread_pool, RTLIL::Module *module)
{
sigmap = sigmap_;
initbits.clear();
const RTLIL::Module *const_module = module;
ParallelDispatchThreadPool::Subpool subpool(thread_pool, ThreadPool::work_pool_size(0, module->wires_size(), 1000));
ShardedVector<RTLIL::Wire*> init_wires(subpool);
subpool.run([const_module, &init_wires](const ParallelDispatchThreadPool::RunCtx &ctx) {
for (int i : ctx.item_range(const_module->wires_size())) {
RTLIL::Wire *wire = const_module->wire_at(i);
if (wire->attributes.count(ID::init))
init_wires.insert(ctx, wire);
}
});
for (RTLIL::Wire *wire : init_wires)
process_wire(wire);
}
RTLIL::State operator()(RTLIL::SigBit bit) const
{
auto it = initbits.find((*sigmap)(bit));

View file

@ -324,6 +324,14 @@ void log_formatted_file_info(std::string_view filename, int lineno, std::string
log("%s:%d: Info: %s", filename, lineno, str);
}
void log_suppressed() {
if (log_debug_suppressed && !log_make_debug) {
constexpr const char* format = "<suppressed ~%d debug messages>\n";
logv_string(format, stringf(format, log_debug_suppressed));
log_debug_suppressed = 0;
}
}
[[noreturn]]
static void log_error_with_prefix(std::string_view prefix, std::string str)
{
@ -345,7 +353,9 @@ static void log_error_with_prefix(std::string_view prefix, std::string str)
}
log_last_error = std::move(str);
log("%s%s", prefix, log_last_error);
std::string message(prefix);
message += log_last_error;
logv_string("%s%s", message);
log_flush();
log_make_debug = bak_log_make_debug;
@ -355,7 +365,7 @@ static void log_error_with_prefix(std::string_view prefix, std::string str)
item.current_count++;
for (auto &[_, item] : log_expect_prefix_error)
if (std::regex_search(string(prefix) + string(log_last_error), item.pattern))
if (std::regex_search(message, item.pattern))
item.current_count++;
log_check_expected();

View file

@ -206,12 +206,7 @@ template <typename... Args>
log_formatted_cmd_error(fmt.format(args...));
}
static inline void log_suppressed() {
if (log_debug_suppressed && !log_make_debug) {
log("<suppressed ~%d debug messages>\n", log_debug_suppressed);
log_debug_suppressed = 0;
}
}
void log_suppressed();
struct LogMakeDebugHdl {
bool status = false;

View file

@ -23,6 +23,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
YOSYS_NAMESPACE_BEGIN
@ -177,8 +178,8 @@ struct ModIndex : public RTLIL::Monitor
return false;
}
return true;
#endif
return true;
}
void check()
@ -357,7 +358,7 @@ struct ModWalker
RTLIL::Design *design;
RTLIL::Module *module;
CellTypes ct;
NewCellTypes ct;
SigMap sigmap;
dict<RTLIL::SigBit, pool<PortBit>> signal_drivers;

651
kernel/newcelltypes.h Normal file
View file

@ -0,0 +1,651 @@
#ifndef NEWCELLTYPES_H
#define NEWCELLTYPES_H
#include "kernel/rtlil.h"
#include "kernel/yosys.h"
YOSYS_NAMESPACE_BEGIN
/**
* This API is unstable.
* It may change or be removed in future versions and break dependent code.
*/
namespace StaticCellTypes {
// Given by last internal cell type IdString constids.inc, compilation error if too low
constexpr int MAX_CELLS = 300;
// Currently given by _MUX16_, compilation error if too low
constexpr int MAX_PORTS = 20;
struct CellTableBuilder {
struct PortList {
std::array<RTLIL::IdString, MAX_PORTS> ports{};
size_t count = 0;
constexpr PortList() = default;
constexpr PortList(std::initializer_list<RTLIL::IdString> init) {
for (auto p : init) {
ports[count++] = p;
}
}
constexpr auto begin() const { return ports.begin(); }
constexpr auto end() const { return ports.begin() + count; }
constexpr bool contains(RTLIL::IdString port) const {
for (size_t i = 0; i < count; i++) {
if (port == ports[i])
return true;
}
return false;
}
constexpr size_t size() const { return count; }
};
struct Features {
bool is_evaluable = false;
bool is_combinatorial = false;
bool is_synthesizable = false;
bool is_stdcell = false;
bool is_ff = false;
bool is_mem_noff = false;
bool is_anyinit = false;
bool is_tristate = false;
};
struct CellInfo {
RTLIL::IdString type;
PortList inputs, outputs;
Features features;
};
std::array<CellInfo, MAX_CELLS> cells{};
size_t count = 0;
constexpr void setup_type(RTLIL::IdString type, std::initializer_list<RTLIL::IdString> inputs, std::initializer_list<RTLIL::IdString> outputs, const Features& features) {
cells[count++] = {type, PortList(inputs), PortList(outputs), features};
}
constexpr void setup_internals_other()
{
Features features {};
features.is_tristate = true;
setup_type(ID($tribuf), {ID::A, ID::EN}, {ID::Y}, features);
features = {};
setup_type(ID($assert), {ID::A, ID::EN}, {}, features);
setup_type(ID($assume), {ID::A, ID::EN}, {}, features);
setup_type(ID($live), {ID::A, ID::EN}, {}, features);
setup_type(ID($fair), {ID::A, ID::EN}, {}, features);
setup_type(ID($cover), {ID::A, ID::EN}, {}, features);
setup_type(ID($initstate), {}, {ID::Y}, features);
setup_type(ID($anyconst), {}, {ID::Y}, features);
setup_type(ID($anyseq), {}, {ID::Y}, features);
setup_type(ID($allconst), {}, {ID::Y}, features);
setup_type(ID($allseq), {}, {ID::Y}, features);
setup_type(ID($equiv), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($specify2), {ID::EN, ID::SRC, ID::DST}, {}, features);
setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, {}, features);
setup_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, {}, features);
setup_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, {}, features);
setup_type(ID($check), {ID::A, ID::EN, ID::ARGS, ID::TRG}, {}, features);
setup_type(ID($set_tag), {ID::A, ID::SET, ID::CLR}, {ID::Y}, features);
setup_type(ID($get_tag), {ID::A}, {ID::Y}, features);
setup_type(ID($overwrite_tag), {ID::A, ID::SET, ID::CLR}, {}, features);
setup_type(ID($original_tag), {ID::A}, {ID::Y}, features);
setup_type(ID($future_ff), {ID::A}, {ID::Y}, features);
setup_type(ID($scopeinfo), {}, {}, features);
setup_type(ID($input_port), {}, {ID::Y}, features);
setup_type(ID($connect), {ID::A, ID::B}, {}, features);
}
constexpr void setup_internals_eval()
{
Features features {};
features.is_evaluable = true;
std::initializer_list<RTLIL::IdString> unary_ops = {
ID($not), ID($pos), ID($buf), ID($neg),
ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool),
ID($logic_not), ID($slice), ID($lut), ID($sop)
};
std::initializer_list<RTLIL::IdString> binary_ops = {
ID($and), ID($or), ID($xor), ID($xnor),
ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx),
ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt),
ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow),
ID($logic_and), ID($logic_or), ID($concat), ID($macc),
ID($bweqx)
};
for (auto type : unary_ops)
setup_type(type, {ID::A}, {ID::Y}, features);
for (auto type : binary_ops)
setup_type(type, {ID::A, ID::B}, {ID::Y}, features);
for (auto type : {ID($mux), ID($pmux), ID($bwmux)})
setup_type(type, {ID::A, ID::B, ID::S}, {ID::Y}, features);
for (auto type : {ID($bmux), ID($demux)})
setup_type(type, {ID::A, ID::S}, {ID::Y}, features);
setup_type(ID($lcu), {ID::P, ID::G, ID::CI}, {ID::CO}, features);
setup_type(ID($alu), {ID::A, ID::B, ID::CI, ID::BI}, {ID::X, ID::Y, ID::CO}, features);
setup_type(ID($macc_v2), {ID::A, ID::B, ID::C}, {ID::Y}, features);
setup_type(ID($fa), {ID::A, ID::B, ID::C}, {ID::X, ID::Y}, features);
}
constexpr void setup_internals_ff()
{
Features features {};
features.is_ff = true;
setup_type(ID($sr), {ID::SET, ID::CLR}, {ID::Q}, features);
setup_type(ID($ff), {ID::D}, {ID::Q}, features);
setup_type(ID($dff), {ID::CLK, ID::D}, {ID::Q}, features);
setup_type(ID($dffe), {ID::CLK, ID::EN, ID::D}, {ID::Q}, features);
setup_type(ID($dffsr), {ID::CLK, ID::SET, ID::CLR, ID::D}, {ID::Q}, features);
setup_type(ID($dffsre), {ID::CLK, ID::SET, ID::CLR, ID::D, ID::EN}, {ID::Q}, features);
setup_type(ID($adff), {ID::CLK, ID::ARST, ID::D}, {ID::Q}, features);
setup_type(ID($adffe), {ID::CLK, ID::ARST, ID::D, ID::EN}, {ID::Q}, features);
setup_type(ID($aldff), {ID::CLK, ID::ALOAD, ID::AD, ID::D}, {ID::Q}, features);
setup_type(ID($aldffe), {ID::CLK, ID::ALOAD, ID::AD, ID::D, ID::EN}, {ID::Q}, features);
setup_type(ID($sdff), {ID::CLK, ID::SRST, ID::D}, {ID::Q}, features);
setup_type(ID($sdffe), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q}, features);
setup_type(ID($sdffce), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q}, features);
setup_type(ID($dlatch), {ID::EN, ID::D}, {ID::Q}, features);
setup_type(ID($adlatch), {ID::EN, ID::D, ID::ARST}, {ID::Q}, features);
setup_type(ID($dlatchsr), {ID::EN, ID::SET, ID::CLR, ID::D}, {ID::Q}, features);
}
constexpr void setup_internals_anyinit()
{
Features features {};
features.is_anyinit = true;
setup_type(ID($anyinit), {ID::D}, {ID::Q}, features);
}
constexpr void setup_internals_mem_noff()
{
Features features {};
features.is_mem_noff = true;
// NOT setup_internals_ff()
setup_type(ID($memrd), {ID::CLK, ID::EN, ID::ADDR}, {ID::DATA}, features);
setup_type(ID($memrd_v2), {ID::CLK, ID::EN, ID::ARST, ID::SRST, ID::ADDR}, {ID::DATA}, features);
setup_type(ID($memwr), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, {}, features);
setup_type(ID($memwr_v2), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, {}, features);
setup_type(ID($meminit), {ID::ADDR, ID::DATA}, {}, features);
setup_type(ID($meminit_v2), {ID::ADDR, ID::DATA, ID::EN}, {}, features);
setup_type(ID($mem), {ID::RD_CLK, ID::RD_EN, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA}, features);
setup_type(ID($mem_v2), {ID::RD_CLK, ID::RD_EN, ID::RD_ARST, ID::RD_SRST, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA}, features);
// What?
setup_type(ID($fsm), {ID::CLK, ID::ARST, ID::CTRL_IN}, {ID::CTRL_OUT}, features);
}
constexpr void setup_stdcells_tristate()
{
Features features {};
features.is_stdcell = true;
features.is_tristate = true;
setup_type(ID($_TBUF_), {ID::A, ID::E}, {ID::Y}, features);
}
constexpr void setup_stdcells_eval()
{
Features features {};
features.is_stdcell = true;
features.is_evaluable = true;
setup_type(ID($_BUF_), {ID::A}, {ID::Y}, features);
setup_type(ID($_NOT_), {ID::A}, {ID::Y}, features);
setup_type(ID($_AND_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_NAND_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_OR_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_NOR_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_XOR_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_XNOR_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_ANDNOT_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_ORNOT_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_MUX_), {ID::A, ID::B, ID::S}, {ID::Y}, features);
setup_type(ID($_NMUX_), {ID::A, ID::B, ID::S}, {ID::Y}, features);
setup_type(ID($_MUX4_), {ID::A, ID::B, ID::C, ID::D, ID::S, ID::T}, {ID::Y}, features);
setup_type(ID($_MUX8_), {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::S, ID::T, ID::U}, {ID::Y}, features);
setup_type(ID($_MUX16_), {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::I, ID::J, ID::K, ID::L, ID::M, ID::N, ID::O, ID::P, ID::S, ID::T, ID::U, ID::V}, {ID::Y}, features);
setup_type(ID($_AOI3_), {ID::A, ID::B, ID::C}, {ID::Y}, features);
setup_type(ID($_OAI3_), {ID::A, ID::B, ID::C}, {ID::Y}, features);
setup_type(ID($_AOI4_), {ID::A, ID::B, ID::C, ID::D}, {ID::Y}, features);
setup_type(ID($_OAI4_), {ID::A, ID::B, ID::C, ID::D}, {ID::Y}, features);
}
constexpr void setup_stdcells_ff() {
Features features {};
features.is_stdcell = true;
features.is_ff = true;
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// setup_type(std::string("$_SR_") + c1 + c2 + "_", {ID::S, ID::R}, {ID::Q}, features);
setup_type(ID($_SR_NN_), {ID::S, ID::R}, {ID::Q}, features);
setup_type(ID($_SR_NP_), {ID::S, ID::R}, {ID::Q}, features);
setup_type(ID($_SR_PN_), {ID::S, ID::R}, {ID::Q}, features);
setup_type(ID($_SR_PP_), {ID::S, ID::R}, {ID::Q}, features);
setup_type(ID($_FF_), {ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// setup_type(std::string("$_DFF_") + c1 + "_", {ID::C, ID::D}, {ID::Q}, features);
setup_type(ID::$_DFF_N_, {ID::C, ID::D}, {ID::Q}, features);
setup_type(ID::$_DFF_P_, {ID::C, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// setup_type(std::string("$_DFFE_") + c1 + c2 + "_", {ID::C, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID::$_DFFE_NN_, {ID::C, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID::$_DFFE_NP_, {ID::C, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID::$_DFFE_PN_, {ID::C, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID::$_DFFE_PP_, {ID::C, ID::D, ID::E}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_01)
// setup_type(std::string("$_DFF_") + c1 + c2 + c3 + "_", {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_NN0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_NN1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_NP0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_NP1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_PN0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_PN1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_PP0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_PP1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_01)
// for (auto c4 : list_np)
// setup_type(std::string("$_DFFE_") + c1 + c2 + c3 + c4 + "_", {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// setup_type(std::string("$_ALDFF_") + c1 + c2 + "_", {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
setup_type(ID($_ALDFF_NN_), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
setup_type(ID($_ALDFF_NP_), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
setup_type(ID($_ALDFF_PN_), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
setup_type(ID($_ALDFF_PP_), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_np)
// setup_type(std::string("$_ALDFFE_") + c1 + c2 + c3 + "_", {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_NNN_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_NNP_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_NPN_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_NPP_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_PNN_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_PNP_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_PPN_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_PPP_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_np)
// setup_type(std::string("$_DFFSR_") + c1 + c2 + c3 + "_", {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_NNN_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_NNP_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_NPN_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_NPP_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_PNN_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_PNP_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_PPN_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_PPP_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_np)
// for (auto c4 : list_np)
// setup_type(std::string("$_DFFSRE_") + c1 + c2 + c3 + c4 + "_", {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NNNN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NNNP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NNPN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NNPP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NPNN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NPNP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NPPN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NPPP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PNNN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PNNP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PNPN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PNPP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PPNN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PPNP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PPPN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PPPP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_01)
// setup_type(std::string("$_SDFF_") + c1 + c2 + c3 + "_", {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_NN0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_NN1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_NP0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_NP1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_PN0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_PN1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_PP0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_PP1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_01)
// for (auto c4 : list_np)
// setup_type(std::string("$_SDFFE_") + c1 + c2 + c3 + c4 + "_", {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_01)
// for (auto c4 : list_np)
// setup_type(std::string("$_SDFFCE_") + c1 + c2 + c3 + c4 + "_", {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
// for (auto c1 : list_np)
// setup_type(std::string("$_DLATCH_") + c1 + "_", {ID::E, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_N_), {ID::E, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_P_), {ID::E, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_01)
// setup_type(std::string("$_DLATCH_") + c1 + c2 + c3 + "_", {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_NN0_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_NN1_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_NP0_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_NP1_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_PN0_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_PN1_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_PP0_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_PP1_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_np)
// setup_type(std::string("$_DLATCHSR_") + c1 + c2 + c3 + "_", {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_NNN_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_NNP_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_NPN_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_NPP_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_PNN_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_PNP_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_PPN_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_PPP_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
}
constexpr CellTableBuilder() {
setup_internals_other();
setup_internals_eval();
setup_internals_ff();
setup_internals_anyinit();
setup_internals_mem_noff();
setup_stdcells_tristate();
setup_stdcells_eval();
setup_stdcells_ff();
}
};
constexpr CellTableBuilder builder{};
struct PortInfo {
struct PortLists {
std::array<CellTableBuilder::PortList, MAX_CELLS> data{};
constexpr CellTableBuilder::PortList operator()(IdString type) const {
return data[type.index_];
}
constexpr CellTableBuilder::PortList& operator[](size_t idx) {
return data[idx];
}
constexpr size_t size() const { return data.size(); }
};
PortLists inputs {};
PortLists outputs {};
constexpr PortInfo() {
for (size_t i = 0; i < builder.count; ++i) {
auto& cell = builder.cells[i];
size_t idx = cell.type.index_;
inputs[idx] = cell.inputs;
outputs[idx] = cell.outputs;
}
}
};
struct Categories {
struct Category {
std::array<bool, MAX_CELLS> data{};
constexpr bool operator()(IdString type) const {
size_t idx = type.index_;
if (idx >= MAX_CELLS)
return false;
return data[idx];
}
constexpr bool operator[](size_t idx) {
return data[idx];
}
constexpr void set_id(IdString type, bool val = true) {
size_t idx = type.index_;
if (idx >= MAX_CELLS)
return; // TODO should be an assert but then it's not constexpr
data[idx] = val;
}
constexpr void set(size_t idx, bool val = true) {
data[idx] = val;
}
constexpr size_t size() const { return data.size(); }
};
Category empty {};
Category is_known {};
Category is_evaluable {};
Category is_combinatorial {};
Category is_synthesizable {};
Category is_stdcell {};
Category is_ff {};
Category is_mem_noff {};
Category is_anyinit {};
Category is_tristate {};
constexpr Categories() {
for (size_t i = 0; i < builder.count; ++i) {
auto& cell = builder.cells[i];
size_t idx = cell.type.index_;
is_known.set(idx);
is_evaluable.set(idx, cell.features.is_evaluable);
is_combinatorial.set(idx, cell.features.is_combinatorial);
is_synthesizable.set(idx, cell.features.is_synthesizable);
is_stdcell.set(idx, cell.features.is_stdcell);
is_ff.set(idx, cell.features.is_ff);
is_mem_noff.set(idx, cell.features.is_mem_noff);
is_anyinit.set(idx, cell.features.is_anyinit);
is_tristate.set(idx, cell.features.is_tristate);
}
}
constexpr static Category join(Category left, Category right) {
Category c {};
for (size_t i = 0; i < MAX_CELLS; ++i) {
c.set(i, left[i] || right[i]);
}
return c;
}
constexpr static Category meet(Category left, Category right) {
Category c {};
for (size_t i = 0; i < MAX_CELLS; ++i) {
c.set(i, left[i] && right[i]);
}
return c;
}
// Sketchy! Make sure to always meet with only the known universe.
// In other words, no modus tollens allowed
constexpr static Category complement(Category arg) {
Category c {};
for (size_t i = 0; i < MAX_CELLS; ++i) {
c.set(i, !arg[i]);
}
return c;
}
};
// Pure
static constexpr PortInfo port_info;
static constexpr Categories categories;
// Legacy
namespace Compat {
static constexpr auto internals_all = Categories::meet(categories.is_known, Categories::complement(categories.is_stdcell));
static constexpr auto mem_ff = Categories::join(categories.is_ff, categories.is_mem_noff);
// old setup_internals + setup_stdcells
static constexpr auto nomem_noff = Categories::meet(categories.is_known, Categories::complement(mem_ff));
static constexpr auto internals_mem_ff = Categories::meet(internals_all, mem_ff);
// old setup_internals
static constexpr auto internals_nomem_noff = Categories::meet(internals_all, nomem_noff);
// old setup_stdcells
static constexpr auto stdcells_nomem_noff = Categories::meet(categories.is_stdcell, nomem_noff);
static constexpr auto stdcells_mem = Categories::meet(categories.is_stdcell, categories.is_mem_noff);
// old setup_internals_eval
// static constexpr auto internals_eval = Categories::meet(internals_all, categories.is_evaluable);
};
namespace {
static_assert(categories.is_evaluable(ID($and)));
static_assert(!categories.is_ff(ID($and)));
static_assert(Categories::join(categories.is_evaluable, categories.is_ff)(ID($and)));
static_assert(Categories::join(categories.is_evaluable, categories.is_ff)(ID($dffsr)));
static_assert(!Categories::join(categories.is_evaluable, categories.is_ff)(ID($anyinit)));
}
};
struct NewCellType {
RTLIL::IdString type;
pool<RTLIL::IdString> inputs, outputs;
bool is_evaluable;
bool is_combinatorial;
bool is_synthesizable;
};
struct NewCellTypes {
struct IdStringHash {
std::size_t operator()(const IdString id) const {
return static_cast<size_t>(id.hash_top().yield());
}
};
StaticCellTypes::Categories::Category static_cell_types = StaticCellTypes::categories.empty;
std::unordered_map<RTLIL::IdString, NewCellType, IdStringHash> custom_cell_types {};
NewCellTypes() {
static_cell_types = StaticCellTypes::categories.empty;
}
NewCellTypes(RTLIL::Design *design) {
static_cell_types = StaticCellTypes::categories.empty;
setup(design);
}
void setup(RTLIL::Design *design = NULL) {
if (design)
setup_design(design);
static_cell_types = StaticCellTypes::categories.is_known;
}
void setup_design(RTLIL::Design *design) {
for (auto module : design->modules())
setup_module(module);
}
void setup_module(RTLIL::Module *module) {
pool<RTLIL::IdString> inputs, outputs;
for (RTLIL::IdString wire_name : module->ports) {
RTLIL::Wire *wire = module->wire(wire_name);
if (wire->port_input)
inputs.insert(wire->name);
if (wire->port_output)
outputs.insert(wire->name);
}
setup_type(module->name, inputs, outputs);
}
void setup_type(RTLIL::IdString type, const pool<RTLIL::IdString> &inputs, const pool<RTLIL::IdString> &outputs, bool is_evaluable = false, bool is_combinatorial = false, bool is_synthesizable = false) {
NewCellType ct = {type, inputs, outputs, is_evaluable, is_combinatorial, is_synthesizable};
custom_cell_types[ct.type] = ct;
}
void clear() {
custom_cell_types.clear();
static_cell_types = StaticCellTypes::categories.empty;
}
bool cell_known(const RTLIL::IdString &type) const {
return static_cell_types(type) || custom_cell_types.count(type) != 0;
}
bool cell_output(const RTLIL::IdString &type, const RTLIL::IdString &port) const
{
if (static_cell_types(type) && StaticCellTypes::port_info.outputs(type).contains(port)) {
return true;
}
auto it = custom_cell_types.find(type);
return it != custom_cell_types.end() && it->second.outputs.count(port) != 0;
}
bool cell_input(const RTLIL::IdString &type, const RTLIL::IdString &port) const
{
if (static_cell_types(type) && StaticCellTypes::port_info.inputs(type).contains(port)) {
return true;
}
auto it = custom_cell_types.find(type);
return it != custom_cell_types.end() && it->second.inputs.count(port) != 0;
}
RTLIL::PortDir cell_port_dir(RTLIL::IdString type, RTLIL::IdString port) const
{
bool is_input, is_output;
if (static_cell_types(type)) {
is_input = StaticCellTypes::port_info.inputs(type).contains(port);
is_output = StaticCellTypes::port_info.outputs(type).contains(port);
} else {
auto it = custom_cell_types.find(type);
if (it == custom_cell_types.end())
return RTLIL::PD_UNKNOWN;
is_input = it->second.inputs.count(port);
is_output = it->second.outputs.count(port);
}
return RTLIL::PortDir(is_input + is_output * 2);
}
bool cell_evaluable(const RTLIL::IdString &type) const
{
return static_cell_types(type) && StaticCellTypes::categories.is_evaluable(type);
}
};
extern NewCellTypes yosys_celltypes;
YOSYS_NAMESPACE_END
#endif

View file

@ -22,6 +22,7 @@
#include "kernel/json.h"
#include "kernel/gzip.h"
#include "kernel/log_help.h"
#include "kernel/newcelltypes.h"
#include <string.h>
#include <stdlib.h>
@ -975,16 +976,18 @@ struct HelpPass : public Pass {
json.entry("generator", yosys_maybe_version());
dict<string, vector<string>> groups;
dict<string, pair<SimHelper, CellType>> cells;
dict<string, pair<SimHelper, StaticCellTypes::CellTableBuilder::CellInfo>> cells;
// iterate over cells
bool raise_error = false;
for (auto &it : yosys_celltypes.cell_types) {
auto name = it.first.str();
for (auto it : StaticCellTypes::builder.cells) {
if (!StaticCellTypes::categories.is_known(it.type))
continue;
auto name = it.type.str();
if (cell_help_messages.contains(name)) {
auto cell_help = cell_help_messages.get(name);
groups[cell_help.group].emplace_back(name);
auto cell_pair = pair<SimHelper, CellType>(cell_help, it.second);
auto cell_pair = pair<SimHelper, StaticCellTypes::CellTableBuilder::CellInfo>(cell_help, it);
cells.emplace(name, cell_pair);
} else {
log("ERROR: Missing cell help for cell '%s'.\n", name);
@ -1025,9 +1028,9 @@ struct HelpPass : public Pass {
json.name("outputs"); json.value(outputs);
vector<string> properties;
// CellType properties
if (ct.is_evaluable) properties.push_back("is_evaluable");
if (ct.is_combinatorial) properties.push_back("is_combinatorial");
if (ct.is_synthesizable) properties.push_back("is_synthesizable");
if (ct.features.is_evaluable) properties.push_back("is_evaluable");
if (ct.features.is_combinatorial) properties.push_back("is_combinatorial");
if (ct.features.is_synthesizable) properties.push_back("is_synthesizable");
// SimHelper properties
size_t last = 0; size_t next = 0;
while ((next = ch.tags.find(", ", last)) != string::npos) {

View file

@ -19,9 +19,10 @@
#include "kernel/yosys.h"
#include "kernel/macc.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/binding.h"
#include "kernel/sigtools.h"
#include "kernel/threading.h"
#include "frontends/verilog/verilog_frontend.h"
#include "frontends/verilog/preproc.h"
#include "backends/rtlil/rtlil_backend.h"
@ -142,9 +143,17 @@ static constexpr bool check_well_known_id_order()
// and in sorted ascii order, as required by the ID macro.
static_assert(check_well_known_id_order());
constexpr int STATIC_ID_END = static_cast<int>(RTLIL::StaticId::STATIC_ID_END);
struct IdStringCollector {
IdStringCollector(std::vector<MonotonicFlag> &live_ids)
: live_ids(live_ids) {}
void trace(IdString id) {
live.insert(id.index_);
if (id.index_ >= STATIC_ID_END)
live_ids[id.index_ - STATIC_ID_END].set();
else if (id.index_ < 0)
live_autoidx_ids.push_back(id.index_);
}
template <typename T> void trace(const T* v) {
trace(*v);
@ -178,10 +187,6 @@ struct IdStringCollector {
trace(element);
}
void trace(const RTLIL::Design &design) {
trace_values(design.modules_);
trace(design.selection_vars);
}
void trace(const RTLIL::Selection &selection_var) {
trace(selection_var.selected_modules);
trace(selection_var.selected_members);
@ -190,15 +195,6 @@ struct IdStringCollector {
trace_keys(named.attributes);
trace(named.name);
}
void trace(const RTLIL::Module &module) {
trace_named(module);
trace_values(module.wires_);
trace_values(module.cells_);
trace(module.avail_parameters);
trace_keys(module.parameter_default_values);
trace_values(module.memories);
trace_values(module.processes);
}
void trace(const RTLIL::Wire &wire) {
trace_named(wire);
if (wire.known_driver())
@ -234,7 +230,8 @@ struct IdStringCollector {
trace(action.memid);
}
std::unordered_set<int> live;
std::vector<MonotonicFlag> &live_ids;
std::vector<int> live_autoidx_ids;
};
int64_t RTLIL::OwningIdString::gc_ns;
@ -243,20 +240,55 @@ int RTLIL::OwningIdString::gc_count;
void RTLIL::OwningIdString::collect_garbage()
{
int64_t start = PerformanceTimer::query();
IdStringCollector collector;
for (auto &[idx, design] : *RTLIL::Design::get_all_designs()) {
collector.trace(*design);
}
int size = GetSize(global_id_storage_);
for (int i = static_cast<int>(StaticId::STATIC_ID_END); i < size; ++i) {
RTLIL::IdString::Storage &storage = global_id_storage_.at(i);
if (storage.buf == nullptr)
continue;
if (collector.live.find(i) != collector.live.end())
continue;
if (global_refcount_storage_.find(i) != global_refcount_storage_.end())
continue;
int pool_size = 0;
for (auto &[idx, design] : *RTLIL::Design::get_all_designs())
for (RTLIL::Module *module : design->modules())
pool_size = std::max(pool_size, ThreadPool::work_pool_size(0, module->cells_size(), 1000));
ParallelDispatchThreadPool thread_pool(pool_size);
int size = GetSize(global_id_storage_);
std::vector<MonotonicFlag> live_ids(size - STATIC_ID_END);
std::vector<IdStringCollector> collectors;
int num_threads = thread_pool.num_threads();
collectors.reserve(num_threads);
for (int i = 0; i < num_threads; ++i)
collectors.emplace_back(live_ids);
for (auto &[idx, design] : *RTLIL::Design::get_all_designs()) {
for (RTLIL::Module *module : design->modules()) {
collectors[0].trace_named(*module);
ParallelDispatchThreadPool::Subpool subpool(thread_pool, ThreadPool::work_pool_size(0, module->cells_size(), 1000));
subpool.run([&collectors, module](const ParallelDispatchThreadPool::RunCtx &ctx) {
for (int i : ctx.item_range(module->cells_size()))
collectors[ctx.thread_num].trace(module->cell_at(i));
for (int i : ctx.item_range(module->wires_size()))
collectors[ctx.thread_num].trace(module->wire_at(i));
});
collectors[0].trace(module->avail_parameters);
collectors[0].trace_keys(module->parameter_default_values);
collectors[0].trace_values(module->memories);
collectors[0].trace_values(module->processes);
}
collectors[0].trace(design->selection_vars);
}
ShardedVector<int> free_ids(thread_pool);
thread_pool.run([&live_ids, size, &free_ids](const ParallelDispatchThreadPool::RunCtx &ctx) {
for (int i : ctx.item_range(size - STATIC_ID_END)) {
int index = i + STATIC_ID_END;
RTLIL::IdString::Storage &storage = global_id_storage_.at(index);
if (storage.buf == nullptr)
continue;
if (live_ids[i].load())
continue;
if (global_refcount_storage_.find(index) != global_refcount_storage_.end())
continue;
free_ids.insert(ctx, index);
}
});
for (int i : free_ids) {
RTLIL::IdString::Storage &storage = global_id_storage_.at(i);
if (yosys_xtrace) {
log("#X# Removed IdString '%s' with index %d.\n", storage.buf, i);
log_backtrace("-X- ", yosys_xtrace-1);
@ -268,8 +300,13 @@ void RTLIL::OwningIdString::collect_garbage()
global_free_idx_list_.push_back(i);
}
std::unordered_set<int> live_autoidx_ids;
for (IdStringCollector &collector : collectors)
for (int id : collector.live_autoidx_ids)
live_autoidx_ids.insert(id);
for (auto it = global_autoidx_id_storage_.begin(); it != global_autoidx_id_storage_.end();) {
if (collector.live.find(it->first) != collector.live.end()) {
if (live_autoidx_ids.find(it->first) != live_autoidx_ids.end()) {
++it;
continue;
}
@ -288,159 +325,17 @@ void RTLIL::OwningIdString::collect_garbage()
dict<std::string, std::string> RTLIL::constpad;
static const pool<IdString> &builtin_ff_cell_types_internal() {
static const pool<IdString> res = {
ID($sr),
ID($ff),
ID($dff),
ID($dffe),
ID($dffsr),
ID($dffsre),
ID($adff),
ID($adffe),
ID($aldff),
ID($aldffe),
ID($sdff),
ID($sdffe),
ID($sdffce),
ID($dlatch),
ID($adlatch),
ID($dlatchsr),
ID($_DFFE_NN_),
ID($_DFFE_NP_),
ID($_DFFE_PN_),
ID($_DFFE_PP_),
ID($_DFFSR_NNN_),
ID($_DFFSR_NNP_),
ID($_DFFSR_NPN_),
ID($_DFFSR_NPP_),
ID($_DFFSR_PNN_),
ID($_DFFSR_PNP_),
ID($_DFFSR_PPN_),
ID($_DFFSR_PPP_),
ID($_DFFSRE_NNNN_),
ID($_DFFSRE_NNNP_),
ID($_DFFSRE_NNPN_),
ID($_DFFSRE_NNPP_),
ID($_DFFSRE_NPNN_),
ID($_DFFSRE_NPNP_),
ID($_DFFSRE_NPPN_),
ID($_DFFSRE_NPPP_),
ID($_DFFSRE_PNNN_),
ID($_DFFSRE_PNNP_),
ID($_DFFSRE_PNPN_),
ID($_DFFSRE_PNPP_),
ID($_DFFSRE_PPNN_),
ID($_DFFSRE_PPNP_),
ID($_DFFSRE_PPPN_),
ID($_DFFSRE_PPPP_),
ID($_DFF_N_),
ID($_DFF_P_),
ID($_DFF_NN0_),
ID($_DFF_NN1_),
ID($_DFF_NP0_),
ID($_DFF_NP1_),
ID($_DFF_PN0_),
ID($_DFF_PN1_),
ID($_DFF_PP0_),
ID($_DFF_PP1_),
ID($_DFFE_NN0N_),
ID($_DFFE_NN0P_),
ID($_DFFE_NN1N_),
ID($_DFFE_NN1P_),
ID($_DFFE_NP0N_),
ID($_DFFE_NP0P_),
ID($_DFFE_NP1N_),
ID($_DFFE_NP1P_),
ID($_DFFE_PN0N_),
ID($_DFFE_PN0P_),
ID($_DFFE_PN1N_),
ID($_DFFE_PN1P_),
ID($_DFFE_PP0N_),
ID($_DFFE_PP0P_),
ID($_DFFE_PP1N_),
ID($_DFFE_PP1P_),
ID($_ALDFF_NN_),
ID($_ALDFF_NP_),
ID($_ALDFF_PN_),
ID($_ALDFF_PP_),
ID($_ALDFFE_NNN_),
ID($_ALDFFE_NNP_),
ID($_ALDFFE_NPN_),
ID($_ALDFFE_NPP_),
ID($_ALDFFE_PNN_),
ID($_ALDFFE_PNP_),
ID($_ALDFFE_PPN_),
ID($_ALDFFE_PPP_),
ID($_SDFF_NN0_),
ID($_SDFF_NN1_),
ID($_SDFF_NP0_),
ID($_SDFF_NP1_),
ID($_SDFF_PN0_),
ID($_SDFF_PN1_),
ID($_SDFF_PP0_),
ID($_SDFF_PP1_),
ID($_SDFFE_NN0N_),
ID($_SDFFE_NN0P_),
ID($_SDFFE_NN1N_),
ID($_SDFFE_NN1P_),
ID($_SDFFE_NP0N_),
ID($_SDFFE_NP0P_),
ID($_SDFFE_NP1N_),
ID($_SDFFE_NP1P_),
ID($_SDFFE_PN0N_),
ID($_SDFFE_PN0P_),
ID($_SDFFE_PN1N_),
ID($_SDFFE_PN1P_),
ID($_SDFFE_PP0N_),
ID($_SDFFE_PP0P_),
ID($_SDFFE_PP1N_),
ID($_SDFFE_PP1P_),
ID($_SDFFCE_NN0N_),
ID($_SDFFCE_NN0P_),
ID($_SDFFCE_NN1N_),
ID($_SDFFCE_NN1P_),
ID($_SDFFCE_NP0N_),
ID($_SDFFCE_NP0P_),
ID($_SDFFCE_NP1N_),
ID($_SDFFCE_NP1P_),
ID($_SDFFCE_PN0N_),
ID($_SDFFCE_PN0P_),
ID($_SDFFCE_PN1N_),
ID($_SDFFCE_PN1P_),
ID($_SDFFCE_PP0N_),
ID($_SDFFCE_PP0P_),
ID($_SDFFCE_PP1N_),
ID($_SDFFCE_PP1P_),
ID($_SR_NN_),
ID($_SR_NP_),
ID($_SR_PN_),
ID($_SR_PP_),
ID($_DLATCH_N_),
ID($_DLATCH_P_),
ID($_DLATCH_NN0_),
ID($_DLATCH_NN1_),
ID($_DLATCH_NP0_),
ID($_DLATCH_NP1_),
ID($_DLATCH_PN0_),
ID($_DLATCH_PN1_),
ID($_DLATCH_PP0_),
ID($_DLATCH_PP1_),
ID($_DLATCHSR_NNN_),
ID($_DLATCHSR_NNP_),
ID($_DLATCHSR_NPN_),
ID($_DLATCHSR_NPP_),
ID($_DLATCHSR_PNN_),
ID($_DLATCHSR_PNP_),
ID($_DLATCHSR_PPN_),
ID($_DLATCHSR_PPP_),
ID($_FF_),
};
return res;
}
const pool<IdString> &RTLIL::builtin_ff_cell_types() {
return builtin_ff_cell_types_internal();
static const pool<IdString> res = []() {
pool<IdString> r;
for (size_t i = 0; i < StaticCellTypes::builder.count; i++) {
auto &cell = StaticCellTypes::builder.cells[i];
if (cell.features.is_ff)
r.insert(cell.type);
}
return r;
}();
return res;
}
#define check(condition) log_assert(condition && "malformed Const union")
@ -1466,15 +1361,21 @@ void RTLIL::Design::sort_modules()
modules_.sort(sort_by_id_str());
}
void check_module(RTLIL::Module *module, ParallelDispatchThreadPool &thread_pool);
void RTLIL::Design::check()
{
#ifndef NDEBUG
log_assert(!selection_stack.empty());
int pool_size = 0;
for (auto &it : modules_)
pool_size = std::max(pool_size, ThreadPool::work_pool_size(0, it.second->cells_size(), 1000));
ParallelDispatchThreadPool thread_pool(pool_size);
for (auto &it : modules_) {
log_assert(this == it.second->design);
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
it.second->check();
check_module(it.second, thread_pool);
}
#endif
}
@ -1710,11 +1611,11 @@ size_t RTLIL::Module::count_id(RTLIL::IdString id)
namespace {
struct InternalCellChecker
{
RTLIL::Module *module;
const RTLIL::Module *module;
RTLIL::Cell *cell;
pool<RTLIL::IdString> expected_params, expected_ports;
InternalCellChecker(RTLIL::Module *module, RTLIL::Cell *cell) : module(module), cell(cell) { }
InternalCellChecker(const RTLIL::Module *module, RTLIL::Cell *cell) : module(module), cell(cell) { }
void error(int linenr)
{
@ -2690,88 +2591,96 @@ void RTLIL::Module::sort()
it.second->attributes.sort(sort_by_id_str());
}
void RTLIL::Module::check()
void check_module(RTLIL::Module *module, ParallelDispatchThreadPool &thread_pool)
{
#ifndef NDEBUG
std::vector<bool> ports_declared;
for (auto &it : wires_) {
log_assert(this == it.second->module);
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
log_assert(it.second->width >= 0);
log_assert(it.second->port_id >= 0);
for (auto &it2 : it.second->attributes)
log_assert(!it2.first.empty());
if (it.second->port_id) {
log_assert(GetSize(ports) >= it.second->port_id);
log_assert(ports.at(it.second->port_id-1) == it.first);
log_assert(it.second->port_input || it.second->port_output);
if (GetSize(ports_declared) < it.second->port_id)
ports_declared.resize(it.second->port_id);
log_assert(ports_declared[it.second->port_id-1] == false);
ports_declared[it.second->port_id-1] = true;
} else
log_assert(!it.second->port_input && !it.second->port_output);
}
for (auto port_declared : ports_declared)
log_assert(port_declared == true);
log_assert(GetSize(ports) == GetSize(ports_declared));
ParallelDispatchThreadPool::Subpool subpool(thread_pool, ThreadPool::work_pool_size(0, module->cells_size(), 1000));
const RTLIL::Module *const_module = module;
for (auto &it : memories) {
pool<std::string> memory_strings;
for (auto &it : module->memories) {
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
log_assert(it.second->width >= 0);
log_assert(it.second->size >= 0);
for (auto &it2 : it.second->attributes)
log_assert(!it2.first.empty());
memory_strings.insert(it.second->name.str());
}
pool<IdString> packed_memids;
std::vector<MonotonicFlag> ports_declared(GetSize(module->ports));
ShardedVector<std::string> memids(subpool);
subpool.run([const_module, &ports_declared, &memory_strings, &memids](const ParallelDispatchThreadPool::RunCtx &ctx) {
for (int i : ctx.item_range(const_module->cells_size())) {
auto it = *const_module->cells_.element(i);
log_assert(const_module == it.second->module);
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
log_assert(!it.second->type.empty());
for (auto &it2 : it.second->connections()) {
log_assert(!it2.first.empty());
it2.second.check(const_module);
}
for (auto &it2 : it.second->attributes)
log_assert(!it2.first.empty());
for (auto &it2 : it.second->parameters)
log_assert(!it2.first.empty());
InternalCellChecker checker(const_module, it.second);
checker.check();
if (it.second->has_memid()) {
log_assert(memory_strings.count(it.second->parameters.at(ID::MEMID).decode_string()));
} else if (it.second->is_mem_cell()) {
std::string memid = it.second->parameters.at(ID::MEMID).decode_string();
log_assert(!memory_strings.count(memid));
memids.insert(ctx, std::move(memid));
}
auto cell_mod = const_module->design->module(it.first);
if (cell_mod != nullptr) {
// assertion check below to make sure that there are no
// cases where a cell has a blackbox attribute since
// that is deprecated
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
log_assert(!it.second->get_blackbox_attribute());
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
}
}
for (auto &it : cells_) {
log_assert(this == it.second->module);
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
log_assert(!it.second->type.empty());
for (auto &it2 : it.second->connections()) {
log_assert(!it2.first.empty());
it2.second.check(this);
for (int i : ctx.item_range(const_module->wires_size())) {
auto it = *const_module->wires_.element(i);
log_assert(const_module == it.second->module);
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
log_assert(it.second->width >= 0);
log_assert(it.second->port_id >= 0);
for (auto &it2 : it.second->attributes)
log_assert(!it2.first.empty());
if (it.second->port_id) {
log_assert(GetSize(const_module->ports) >= it.second->port_id);
log_assert(const_module->ports.at(it.second->port_id-1) == it.first);
log_assert(it.second->port_input || it.second->port_output);
log_assert(it.second->port_id <= GetSize(ports_declared));
bool previously_declared = ports_declared[it.second->port_id-1].set_and_return_old();
log_assert(previously_declared == false);
} else
log_assert(!it.second->port_input && !it.second->port_output);
}
for (auto &it2 : it.second->attributes)
log_assert(!it2.first.empty());
for (auto &it2 : it.second->parameters)
log_assert(!it2.first.empty());
InternalCellChecker checker(this, it.second);
checker.check();
if (it.second->has_memid()) {
log_assert(memories.count(it.second->parameters.at(ID::MEMID).decode_string()));
} else if (it.second->is_mem_cell()) {
IdString memid = it.second->parameters.at(ID::MEMID).decode_string();
log_assert(!memories.count(memid));
log_assert(!packed_memids.count(memid));
packed_memids.insert(memid);
}
auto cell_mod = design->module(it.first);
if (cell_mod != nullptr) {
// assertion check below to make sure that there are no
// cases where a cell has a blackbox attribute since
// that is deprecated
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
log_assert(!it.second->get_blackbox_attribute());
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
}
}
});
for (const MonotonicFlag &port_declared : ports_declared)
log_assert(port_declared.load() == true);
pool<std::string> memids_pool;
for (std::string &memid : memids)
log_assert(memids_pool.insert(memid).second);
for (auto &it : processes) {
for (auto &it : module->processes) {
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
log_assert(it.second->root_case.compare.empty());
std::vector<CaseRule*> all_cases = {&it.second->root_case};
std::vector<RTLIL::CaseRule*> all_cases = {&it.second->root_case};
for (size_t i = 0; i < all_cases.size(); i++) {
for (auto &switch_it : all_cases[i]->switches) {
for (auto &case_it : switch_it->cases) {
@ -2784,34 +2693,41 @@ void RTLIL::Module::check()
}
for (auto &sync_it : it.second->syncs) {
switch (sync_it->type) {
case SyncType::ST0:
case SyncType::ST1:
case SyncType::STp:
case SyncType::STn:
case SyncType::STe:
case RTLIL::SyncType::ST0:
case RTLIL::SyncType::ST1:
case RTLIL::SyncType::STp:
case RTLIL::SyncType::STn:
case RTLIL::SyncType::STe:
log_assert(!sync_it->signal.empty());
break;
case SyncType::STa:
case SyncType::STg:
case SyncType::STi:
case RTLIL::SyncType::STa:
case RTLIL::SyncType::STg:
case RTLIL::SyncType::STi:
log_assert(sync_it->signal.empty());
break;
}
}
}
for (auto &it : connections_) {
for (auto &it : module->connections_) {
log_assert(it.first.size() == it.second.size());
log_assert(!it.first.has_const());
it.first.check(this);
it.second.check(this);
it.first.check(module);
it.second.check(module);
}
for (auto &it : attributes)
for (auto &it : module->attributes)
log_assert(!it.first.empty());
#endif
}
void RTLIL::Module::check()
{
int pool_size = ThreadPool::work_pool_size(0, cells_size(), 1000);
ParallelDispatchThreadPool thread_pool(pool_size);
check_module(this, thread_pool);
}
void RTLIL::Module::optimize()
{
}
@ -4610,7 +4526,7 @@ bool RTLIL::Cell::is_mem_cell() const
}
bool RTLIL::Cell::is_builtin_ff() const {
return builtin_ff_cell_types_internal().count(type) > 0;
return StaticCellTypes::categories.is_ff(type);
}
RTLIL::SigChunk::SigChunk(const RTLIL::SigBit &bit)
@ -5074,31 +4990,35 @@ void RTLIL::SigSpec::remove2(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *othe
other->unpack();
}
bool modified = false;
bool other_modified = false;
for (int i = GetSize(bits_) - 1; i >= 0; i--)
{
if (bits_[i].wire == NULL) continue;
// Convert pattern to pool for O(1) lookup, avoiding O(n*m) chunk iteration
pool<SigBit> pattern_bits;
pattern_bits.reserve(pattern.size());
for (auto &bit : pattern)
if (bit.wire != NULL)
pattern_bits.insert(bit);
for (auto &pattern_chunk : pattern.chunks())
if (bits_[i].wire == pattern_chunk.wire &&
bits_[i].offset >= pattern_chunk.offset &&
bits_[i].offset < pattern_chunk.offset + pattern_chunk.width) {
modified = true;
bits_.erase(bits_.begin() + i);
if (other != NULL) {
other_modified = true;
other->bits_.erase(other->bits_.begin() + i);
}
break;
// Compact in-place to avoid O(n^2) erase operations
size_t write_idx = 0;
for (size_t read_idx = 0; read_idx < bits_.size(); read_idx++)
{
if (!(bits_[read_idx].wire != NULL && pattern_bits.count(bits_[read_idx]))) {
if (write_idx != read_idx) {
bits_[write_idx] = bits_[read_idx];
if (other != NULL)
other->bits_[write_idx] = other->bits_[read_idx];
}
write_idx++;
}
}
bool modified = (write_idx < bits_.size());
if (modified) {
bits_.resize(write_idx);
hash_.clear();
try_repack();
}
if (other_modified) {
if (other != NULL && modified) {
other->bits_.resize(write_idx);
other->hash_.clear();
other->try_repack();
}
@ -5125,24 +5045,27 @@ void RTLIL::SigSpec::remove2(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec
other->unpack();
}
bool modified = false;
bool other_modified = false;
for (int i = GetSize(bits_) - 1; i >= 0; i--) {
if (bits_[i].wire != NULL && pattern.count(bits_[i])) {
modified = true;
bits_.erase(bits_.begin() + i);
if (other != NULL) {
other_modified = true;
other->bits_.erase(other->bits_.begin() + i);
// Avoid O(n^2) complexity by compacting in-place
size_t write_idx = 0;
for (size_t read_idx = 0; read_idx < bits_.size(); read_idx++) {
if (!(bits_[read_idx].wire != NULL && pattern.count(bits_[read_idx]))) {
if (write_idx != read_idx) {
bits_[write_idx] = bits_[read_idx];
if (other != NULL)
other->bits_[write_idx] = other->bits_[read_idx];
}
write_idx++;
}
}
bool modified = (write_idx < bits_.size());
if (modified) {
bits_.resize(write_idx);
hash_.clear();
try_repack();
}
if (other_modified) {
if (other != NULL && modified) {
other->bits_.resize(write_idx);
other->hash_.clear();
other->try_repack();
}
@ -5158,24 +5081,27 @@ void RTLIL::SigSpec::remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigS
other->unpack();
}
bool modified = false;
bool other_modified = false;
for (int i = GetSize(bits_) - 1; i >= 0; i--) {
if (bits_[i].wire != NULL && pattern.count(bits_[i])) {
modified = true;
bits_.erase(bits_.begin() + i);
if (other != NULL) {
other_modified = true;
other->bits_.erase(other->bits_.begin() + i);
// Avoid O(n^2) complexity by compacting in-place
size_t write_idx = 0;
for (size_t read_idx = 0; read_idx < bits_.size(); read_idx++) {
if (!(bits_[read_idx].wire != NULL && pattern.count(bits_[read_idx]))) {
if (write_idx != read_idx) {
bits_[write_idx] = bits_[read_idx];
if (other != NULL)
other->bits_[write_idx] = other->bits_[read_idx];
}
write_idx++;
}
}
bool modified = (write_idx < bits_.size());
if (modified) {
bits_.resize(write_idx);
hash_.clear();
try_repack();
}
if (other_modified) {
if (other != NULL && modified) {
other->bits_.resize(write_idx);
other->hash_.clear();
other->try_repack();
}
@ -5470,7 +5396,7 @@ RTLIL::SigSpec RTLIL::SigSpec::repeat(int num) const
}
#ifndef NDEBUG
void RTLIL::SigSpec::check(Module *mod) const
void RTLIL::SigSpec::check(const Module *mod) const
{
if (rep_ == CHUNK)
{

View file

@ -275,6 +275,17 @@ struct RTLIL::IdString
*out += std::to_string(-index_);
}
std::string unescape() const {
if (index_ < 0) {
// Must start with "$auto$" so no unescaping required.
return str();
}
std::string_view str = global_id_storage_.at(index_).str_view();
if (str.size() < 2 || str[0] != '\\' || str[1] == '$' || str[1] == '\\' || (str[1] >= '0' && str[1] <= '9'))
return std::string(str);
return std::string(str.substr(1));
}
class Substrings {
std::string_view first_;
int suffix_number;
@ -737,6 +748,7 @@ template <> struct IDMacroHelper<-1> {
namespace RTLIL {
extern dict<std::string, std::string> constpad;
[[deprecated("use StaticCellTypes::categories.is_ff() instead")]]
const pool<IdString> &builtin_ff_cell_types();
static inline std::string escape_id(const std::string &str) {
@ -758,7 +770,7 @@ namespace RTLIL {
}
static inline std::string unescape_id(RTLIL::IdString str) {
return unescape_id(str.str());
return str.unescape();
}
static inline const char *id2cstr(RTLIL::IdString str) {
@ -1748,9 +1760,9 @@ public:
}
#ifndef NDEBUG
void check(Module *mod = nullptr) const;
void check(const Module *mod = nullptr) const;
#else
void check(Module *mod = nullptr) const { (void)mod; }
void check(const Module *mod = nullptr) const { (void)mod; }
#endif
};

View file

@ -279,7 +279,7 @@ static int tcl_get_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar
ERROR("object not found")
if (string_flag) {
Tcl_SetResult(interp, (char *) obj->get_string_attribute(attr_id).c_str(), TCL_VOLATILE);
Tcl_SetObjResult(interp, Tcl_NewStringObj(obj->get_string_attribute(attr_id).c_str(), -1));
} else if (int_flag || uint_flag || sint_flag) {
if (!obj->has_attribute(attr_id))
ERROR("attribute missing (required for -int)");
@ -295,7 +295,7 @@ static int tcl_get_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar
if (!obj->has_attribute(attr_id))
ERROR("attribute missing (required unless -bool or -string)")
Tcl_SetResult(interp, (char *) obj->attributes.at(attr_id).as_string().c_str(), TCL_VOLATILE);
Tcl_SetObjResult(interp, Tcl_NewStringObj(obj->attributes.at(attr_id).as_string().c_str(), -1));
}
return TCL_OK;
@ -341,7 +341,7 @@ static int tcl_has_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar
if (!obj)
ERROR("object not found")
Tcl_SetResult(interp, (char *) std::to_string(obj->has_attribute(attr_id)).c_str(), TCL_VOLATILE);
Tcl_SetObjResult(interp, Tcl_NewStringObj(std::to_string(obj->has_attribute(attr_id)).c_str(), -1));
return TCL_OK;
}
@ -465,14 +465,14 @@ static int tcl_get_param(ClientData, Tcl_Interp *interp, int argc, const char *a
const RTLIL::Const &value = cell->getParam(param_id);
if (string_flag) {
Tcl_SetResult(interp, (char *) value.decode_string().c_str(), TCL_VOLATILE);
Tcl_SetObjResult(interp, Tcl_NewStringObj(value.decode_string().c_str(), -1));
} else if (int_flag || uint_flag || sint_flag) {
mp_int value_mp;
if (!const_to_mp_int(value, &value_mp, sint_flag, uint_flag))
ERROR("bignum manipulation failed");
Tcl_SetObjResult(interp, Tcl_NewBignumObj(&value_mp));
} else {
Tcl_SetResult(interp, (char *) value.as_string().c_str(), TCL_VOLATILE);
Tcl_SetObjResult(interp, Tcl_NewStringObj(value.as_string().c_str(), -1));
}
return TCL_OK;
}

View file

@ -17,6 +17,20 @@ static int get_max_threads()
return max_threads;
}
static int init_work_units_per_thread_override()
{
const char *v = getenv("YOSYS_WORK_UNITS_PER_THREAD");
if (v == nullptr)
return 0;
return atoi(v);
}
static int get_work_units_per_thread_override()
{
static int work_units_per_thread = init_work_units_per_thread_override();
return work_units_per_thread;
}
void DeferredLogs::flush()
{
for (auto &m : logs)
@ -37,6 +51,14 @@ int ThreadPool::pool_size(int reserved_cores, int max_worker_threads)
#endif
}
int ThreadPool::work_pool_size(int reserved_cores, int work_units, int work_units_per_thread)
{
int work_units_per_thread_override = get_work_units_per_thread_override();
if (work_units_per_thread_override > 0)
work_units_per_thread = work_units_per_thread_override;
return pool_size(reserved_cores, work_units / work_units_per_thread);
}
ThreadPool::ThreadPool(int pool_size, std::function<void(int)> b)
: body(std::move(b))
{
@ -57,4 +79,74 @@ ThreadPool::~ThreadPool()
#endif
}
IntRange item_range_for_worker(int num_items, int thread_num, int num_threads)
{
if (num_threads <= 1) {
return {0, num_items};
}
int items_per_thread = num_items / num_threads;
int extra_items = num_items % num_threads;
// The first `extra_items` threads get one extra item.
int start = thread_num * items_per_thread + std::min(thread_num, extra_items);
int end = (thread_num + 1) * items_per_thread + std::min(thread_num + 1, extra_items);
return {start, end};
}
ParallelDispatchThreadPool::ParallelDispatchThreadPool(int pool_size)
: num_worker_threads_(std::max(1, pool_size) - 1)
{
#ifdef YOSYS_ENABLE_THREADS
main_to_workers_signal.resize(num_worker_threads_, 0);
#endif
// Don't start the threads until we've constructed all our data members.
thread_pool = std::make_unique<ThreadPool>(num_worker_threads_, [this](int thread_num){
run_worker(thread_num);
});
}
ParallelDispatchThreadPool::~ParallelDispatchThreadPool()
{
#ifdef YOSYS_ENABLE_THREADS
if (num_worker_threads_ == 0)
return;
current_work = nullptr;
num_active_worker_threads_.store(num_worker_threads_, std::memory_order_relaxed);
signal_workers_start();
wait_for_workers_done();
#endif
}
void ParallelDispatchThreadPool::run(std::function<void(const RunCtx &)> work, int max_threads)
{
Multithreading multithreading;
int num_active_worker_threads = num_threads(max_threads) - 1;
if (num_active_worker_threads == 0) {
work({{0}, 1});
return;
}
#ifdef YOSYS_ENABLE_THREADS
num_active_worker_threads_.store(num_active_worker_threads, std::memory_order_relaxed);
current_work = &work;
signal_workers_start();
work({{0}, num_active_worker_threads + 1});
wait_for_workers_done();
#endif
}
void ParallelDispatchThreadPool::run_worker(int thread_num)
{
#ifdef YOSYS_ENABLE_THREADS
while (true)
{
worker_wait_for_start(thread_num);
if (current_work == nullptr)
break;
int num_active_worker_threads = num_active_worker_threads_.load(std::memory_order_relaxed);
(*current_work)({{thread_num + 1}, num_active_worker_threads + 1});
signal_worker_done();
}
signal_worker_done();
#endif
}
YOSYS_NAMESPACE_END

View file

@ -8,6 +8,7 @@
#include "kernel/yosys_common.h"
#include "kernel/log.h"
#include "kernel/utils.h"
#ifndef YOSYS_THREADING_H
#define YOSYS_THREADING_H
@ -131,6 +132,11 @@ public:
// The result may be 0.
static int pool_size(int reserved_cores, int max_worker_threads);
// Computes the number of worker threads to use, by dividing work_units among threads.
// For testing purposes you can set YOSYS_WORK_UNITS_PER_THREAD to override `work_units_per_thread`.
// The result may be 0.
static int work_pool_size(int reserved_cores, int work_units, int work_units_per_thread);
// Create a pool of threads running the given closure (parameterized by thread number).
// `pool_size` must be the result of a `pool_size()` call.
ThreadPool(int pool_size, std::function<void(int)> b);
@ -154,6 +160,139 @@ private:
#endif
};
// Divides some number of items into `num_threads` subranges and returns the
// `thread_num`'th subrange. If `num_threads` is zero, returns the whole range.
IntRange item_range_for_worker(int num_items, int thread_num, int num_threads);
// A type that encapsulates the index of a thread in some list of threads. Useful for
// stronger typechecking and code readability.
struct ThreadIndex {
int thread_num;
};
// A set of threads with a `run()` API that runs a closure on all of the threads
// and wait for all those closures to complete. This is a convenient way to implement
// parallel algorithms that use barrier synchronization.
class ParallelDispatchThreadPool
{
public:
// Create a pool of threads running the given closure (parameterized by thread number).
// `pool_size` must be the result of a `pool_size()` call.
// `pool_size` can be zero, which we treat as 1.
ParallelDispatchThreadPool(int pool_size);
~ParallelDispatchThreadPool();
// For each thread running a closure, a `RunCtx` is passed to the closure. Currently
// it contains the thread index and the total number of threads. It can be passed
// directly to any APIs requiring a `ThreadIndex`.
struct RunCtx : public ThreadIndex {
int num_threads;
IntRange item_range(int num_items) const {
return item_range_for_worker(num_items, thread_num, num_threads);
}
};
// Sometimes we only want to activate a subset of the threads in the pool. This
// class provides a way to do that. It provides the same `num_threads()`
// and `run()` APIs as a `ParallelDispatchThreadPool`.
class Subpool {
public:
Subpool(ParallelDispatchThreadPool &parent, int max_threads)
: parent(parent), max_threads(max_threads) {}
// Returns the number of threads that will be used when calling `run()`.
int num_threads() const {
return parent.num_threads(max_threads);
}
void run(std::function<void(const RunCtx &)> work) {
parent.run(std::move(work), max_threads);
}
ParallelDispatchThreadPool &thread_pool() { return parent; }
private:
ParallelDispatchThreadPool &parent;
int max_threads;
};
// Run the `work` function in parallel on each thread in the pool (parameterized by
// thread number). Waits for all work functions to complete. Only one `run()` can be
// active at a time.
// Uses no more than `max_threads` threads (but at least one).
void run(std::function<void(const RunCtx &)> work) {
run(std::move(work), INT_MAX);
}
// Returns the number of threads that will be used when calling `run()`.
int num_threads() const {
return num_threads(INT_MAX);
}
private:
friend class Subpool;
void run(std::function<void(const RunCtx &)> work, int max_threads);
int num_threads(int max_threads) const {
return std::min(num_worker_threads_ + 1, std::max(1, max_threads));
}
void run_worker(int thread_num);
std::unique_ptr<ThreadPool> thread_pool;
std::function<void(const RunCtx &)> *current_work = nullptr;
// Keeps a correct count even when threads are exiting.
int num_worker_threads_;
// The count of active worker threads for the current `run()`.
// This is only written by the main thread, and only written when
// no other worker threads are running (i.e. all worker threads have
// passed the increment of `done_workers` in `signal_worker_done()`
// and not passed the release of the lock in `worker_wait_for_start()`.
// Although there can't be any races, we still need to make it atomic
// to prevent the compiler reordering accesses so the above invariant
// is maintained.
std::atomic<int> num_active_worker_threads_ = 0;
#ifdef YOSYS_ENABLE_THREADS
// Not especially efficient for large numbers of threads. Worker wakeup could scale
// better by conceptually organising workers into a tree and having workers wake
// up their children.
std::mutex main_to_workers_signal_mutex;
std::condition_variable main_to_workers_signal_cv;
std::vector<uint8_t> main_to_workers_signal;
void signal_workers_start() {
std::unique_lock lock(main_to_workers_signal_mutex);
int num_active_worker_threads = num_active_worker_threads_.load(std::memory_order_relaxed);
std::fill(main_to_workers_signal.begin(), main_to_workers_signal.begin() + num_active_worker_threads, 1);
// When `num_active_worker_threads_` is small compared to `num_worker_threads_`, we have a "thundering herd"
// problem here. Fixing that would add complexity so don't worry about it for now.
main_to_workers_signal_cv.notify_all();
}
void worker_wait_for_start(int thread_num) {
std::unique_lock lock(main_to_workers_signal_mutex);
main_to_workers_signal_cv.wait(lock, [this, thread_num] { return main_to_workers_signal[thread_num] > 0; });
main_to_workers_signal[thread_num] = 0;
}
std::atomic<int> done_workers = 0;
std::mutex workers_to_main_signal_mutex;
std::condition_variable workers_to_main_signal_cv;
void signal_worker_done() {
// Must read `num_active_worker_threads_` before we increment `d`! Otherwise
// it is possible we would increment `d`, and then another worker signals the
// main thread that all workers are done, and the main thread writes to
// `num_active_worker_threads_` before we check it.
int num_active_worker_threads = num_active_worker_threads_.load(std::memory_order_relaxed);
int d = done_workers.fetch_add(1, std::memory_order_release);
if (d + 1 == num_active_worker_threads) {
std::unique_lock lock(workers_to_main_signal_mutex);
workers_to_main_signal_cv.notify_all();
}
}
void wait_for_workers_done() {
std::unique_lock lock(workers_to_main_signal_mutex);
workers_to_main_signal_cv.wait(lock, [this] {
int num_active_worker_threads = num_active_worker_threads_.load(std::memory_order_relaxed);
return done_workers.load(std::memory_order_acquire) == num_active_worker_threads;
});
done_workers.store(0, std::memory_order_relaxed);
}
#endif
};
template <class T>
class ConcurrentStack
{
@ -181,6 +320,382 @@ private:
std::vector<T> contents;
};
// A vector that is sharded into buckets, one per thread. This lets multiple threads write
// efficiently to the vector without synchronization overhead. After all writers have
// finished writing, the vector can be iterated over. The iteration order is deterministic:
// all the elements written by thread 0 in the order it inserted them, followed by all elements
// written by thread 1, etc.
template <typename T>
class ShardedVector {
public:
ShardedVector(const ParallelDispatchThreadPool &thread_pool) {
init(thread_pool.num_threads());
}
ShardedVector(const ParallelDispatchThreadPool::Subpool &thread_pool) {
init(thread_pool.num_threads());
}
// Insert a value, passing the `ThreadIndex` of the writer thread.
// Parallel inserts with different `ThreadIndex` values are fine.
// Inserts must not run concurrently with any other methods (e.g.
// iteration or `empty()`.)
void insert(const ThreadIndex &thread, T value) {
buckets[thread.thread_num].emplace_back(std::move(value));
}
bool empty() const {
for (const std::vector<T> &bucket : buckets)
if (!bucket.empty())
return false;
return true;
}
using Buckets = std::vector<std::vector<T>>;
class iterator {
public:
iterator(typename Buckets::iterator bucket_it, typename Buckets::iterator bucket_end)
: bucket_it(std::move(bucket_it)), bucket_end(std::move(bucket_end)) {
if (bucket_it != bucket_end)
inner_it = bucket_it->begin();
normalize();
}
T& operator*() const { return *inner_it.value(); }
iterator &operator++() {
++*inner_it;
normalize();
return *this;
}
bool operator!=(const iterator &other) const {
return bucket_it != other.bucket_it || inner_it != other.inner_it;
}
private:
void normalize() {
if (bucket_it == bucket_end)
return;
while (inner_it == bucket_it->end()) {
++bucket_it;
if (bucket_it == bucket_end) {
inner_it.reset();
return;
}
inner_it = bucket_it->begin();
}
}
std::optional<typename std::vector<T>::iterator> inner_it;
typename Buckets::iterator bucket_it;
typename Buckets::iterator bucket_end;
};
iterator begin() { return iterator(buckets.begin(), buckets.end()); }
iterator end() { return iterator(buckets.end(), buckets.end()); }
private:
void init(int num_threads) {
buckets.resize(num_threads);
}
Buckets buckets;
};
// This collision handler for `ShardedHashtable` resolves collisions by keeping
// the current value and discarding the other. This is correct when all values with the
// same key are interchangeable, i.e. when the hashtable is being used as a set instead
// of a map.
template <typename V>
struct SetCollisionHandler {
void operator()(typename V::Accumulated &, typename V::Accumulated &) const {}
};
// A hashtable that can be efficiently built in parallel and then looked up concurrently.
// `V` is the type of elements that will be added to the hashtable. It must have a
// member type `Accumulated` representing the combination of multiple `V` elements. This
// can be the same as `V`, but for example `V` could contain a Wire* and `V::Accumulated`
// could contain a `pool<Wire*>`. `KeyEquality` is a class containing an `operator()` that
// returns true of two `V` elements have equal keys.
// `CollisionHandler` is used to reduce two `V::Accumulated` values into a single value.
//
// To use this, first construct a `Builder` and fill it in (in parallel), then construct
// a `ShardedHashtable` from the `Builder`.
template <typename V, typename KeyEquality, typename CollisionHandler>
class ShardedHashtable {
public:
// A combination of a `V` and its hash value.
struct Value {
Value(V value, unsigned int hash) : value(std::move(value)), hash(hash) {}
Value(Value &&) = default;
Value(const Value &) = delete;
Value &operator=(const Value &) = delete;
V value;
unsigned int hash;
};
// A combination of a `V::Accumulated` and its hash value.
struct AccumulatedValue {
AccumulatedValue(typename V::Accumulated value, unsigned int hash) : value(std::move(value)), hash(hash) {}
AccumulatedValue(AccumulatedValue &&) = default;
#if defined(_MSC_VER)
AccumulatedValue(const AccumulatedValue &) {
log_error("Copy constructor called on AccumulatedValue");
}
AccumulatedValue &operator=(const AccumulatedValue &) {
log_error("Copy assignment called on AccumulatedValue");
return *this;
}
#else
AccumulatedValue(const AccumulatedValue &) = delete;
AccumulatedValue &operator=(const AccumulatedValue &) = delete;
#endif
typename V::Accumulated value;
unsigned int hash;
};
// A class containing an `operator()` that returns true of two `AccumulatedValue`
// elements have equal keys.
// Required to insert `AccumulatedValue`s into an `std::unordered_set`.
struct AccumulatedValueEquality {
KeyEquality inner;
AccumulatedValueEquality(const KeyEquality &inner) : inner(inner) {}
bool operator()(const AccumulatedValue &v1, const AccumulatedValue &v2) const {
return inner(v1.value, v2.value);
}
};
// A class containing an `operator()` that returns the hash value of an `AccumulatedValue`.
// Required to insert `AccumulatedValue`s into an `std::unordered_set`.
struct AccumulatedValueHashOp {
size_t operator()(const AccumulatedValue &v) const {
return static_cast<size_t>(v.hash);
}
};
using Shard = std::unordered_set<AccumulatedValue, AccumulatedValueHashOp, AccumulatedValueEquality>;
// First construct one of these. Then populate it in parallel by calling `insert()` from many threads.
// Then do another parallel phase calling `process()` from many threads.
class Builder {
public:
Builder(const ParallelDispatchThreadPool &thread_pool, KeyEquality equality = KeyEquality(), CollisionHandler collision_handler = CollisionHandler())
: collision_handler(std::move(collision_handler)) {
init(thread_pool.num_threads(), std::move(equality));
}
Builder(const ParallelDispatchThreadPool::Subpool &thread_pool, KeyEquality equality = KeyEquality(), CollisionHandler collision_handler = CollisionHandler())
: collision_handler(std::move(collision_handler)) {
init(thread_pool.num_threads(), std::move(equality));
}
// First call `insert` to insert all elements. All inserts must finish
// before calling any `process()`.
void insert(const ThreadIndex &thread, Value v) {
// You might think that for the single-threaded case, we can optimize by
// inserting directly into the `std::unordered_set` here. But that slows things down
// a lot and I never got around to figuring out why.
std::vector<std::vector<Value>> &buckets = all_buckets[thread.thread_num];
size_t bucket = static_cast<size_t>(v.hash) % buckets.size();
buckets[bucket].emplace_back(std::move(v));
}
// Then call `process` for each thread. All `process()`s must finish before using
// the `Builder` to construct a `ShardedHashtable`.
void process(const ThreadIndex &thread) {
int size = 0;
for (std::vector<std::vector<Value>> &buckets : all_buckets)
size += GetSize(buckets[thread.thread_num]);
Shard &shard = shards[thread.thread_num];
shard.reserve(size);
for (std::vector<std::vector<Value>> &buckets : all_buckets) {
for (Value &value : buckets[thread.thread_num])
accumulate(value, shard);
// Free as much memory as we can during the parallel phase.
std::vector<Value>().swap(buckets[thread.thread_num]);
}
}
private:
friend class ShardedHashtable<V, KeyEquality, CollisionHandler>;
void accumulate(Value &value, Shard &shard) {
// With C++20 we could make this more efficient using heterogenous lookup
AccumulatedValue accumulated_value{std::move(value.value), value.hash};
auto [it, inserted] = shard.insert(std::move(accumulated_value));
if (!inserted)
collision_handler(const_cast<typename V::Accumulated &>(it->value), accumulated_value.value);
}
void init(int num_threads, KeyEquality equality) {
all_buckets.resize(num_threads);
for (std::vector<std::vector<Value>> &buckets : all_buckets)
buckets.resize(num_threads);
for (int i = 0; i < num_threads; ++i)
shards.emplace_back(0, AccumulatedValueHashOp(), AccumulatedValueEquality(equality));
}
const CollisionHandler collision_handler;
// A num_threads x num_threads matrix of buckets.
// In the first phase, each thread i gemerates elements and writes them to
// bucket [i][j] where j = hash(element) % num_threads.
// In the second phase, thread i reads from bucket [j][i] for all j, collecting
// all elements where i = hash(element) % num_threads.
std::vector<std::vector<std::vector<Value>>> all_buckets;
std::vector<Shard> shards;
};
// Then finally construct the hashtable:
ShardedHashtable(Builder &builder) : shards(std::move(builder.shards)) {
// Check that all necessary 'process()' calls were made.
for (std::vector<std::vector<Value>> &buckets : builder.all_buckets)
for (std::vector<Value> &bucket : buckets)
log_assert(bucket.empty());
// Free memory.
std::vector<std::vector<std::vector<Value>>>().swap(builder.all_buckets);
}
ShardedHashtable(ShardedHashtable &&other) = default;
ShardedHashtable() {}
ShardedHashtable &operator=(ShardedHashtable &&other) = default;
// Look up by `AccumulatedValue`. If we switch to C++20 then we could use
// heterogenous lookup to support looking up by `Value` here. Returns nullptr
// if the key is not found.
const typename V::Accumulated *find(const AccumulatedValue &v) const {
size_t num_shards = shards.size();
if (num_shards == 0)
return nullptr;
size_t shard = static_cast<size_t>(v.hash) % num_shards;
auto it = shards[shard].find(v);
if (it == shards[shard].end())
return nullptr;
return &it->value;
}
// Insert an element into the table. The caller is responsible for ensuring this does not
// happen concurrently with any other method calls.
void insert(AccumulatedValue v) {
size_t num_shards = shards.size();
if (num_shards == 0)
return;
size_t shard = static_cast<size_t>(v.hash) % num_shards;
shards[shard].insert(v);
}
// Call this for each shard to implement parallel destruction. For very large `ShardedHashtable`s,
// deleting all elements of all shards on a single thread can be a performance bottleneck.
void clear(const ThreadIndex &shard) {
AccumulatedValueEquality equality = shards[0].key_eq();
shards[shard.thread_num] = Shard(0, AccumulatedValueHashOp(), equality);
}
private:
std::vector<Shard> shards;
};
// A concurrent work-queue that can share batches of work across threads.
// Uses a naive implementation of work-stealing.
template <typename T>
class ConcurrentWorkQueue {
public:
// Create a queue that supports the given number of threads and
// groups work into `batch_size` units.
ConcurrentWorkQueue(int num_threads, int batch_size = 100)
: batch_size(batch_size), thread_states(num_threads) {}
int num_threads() const { return GetSize(thread_states); }
// Push some work to do. Pushes and pops with the same `thread` must
// not happen concurrently.
void push(const ThreadIndex &thread, T work) {
ThreadState &thread_state = thread_states[thread.thread_num];
thread_state.next_batch.emplace_back(std::move(work));
if (GetSize(thread_state.next_batch) < batch_size)
return;
bool was_empty;
{
std::unique_lock lock(thread_state.batches_lock);
was_empty = thread_state.batches.empty();
thread_state.batches.push_back(std::move(thread_state.next_batch));
}
if (was_empty) {
std::unique_lock lock(waiters_lock);
if (num_waiters > 0) {
waiters_cv.notify_one();
}
}
}
// Grab some work to do.
// If all threads enter `pop_batch()`, then instead of deadlocking the
// queue will return no work. That is the only case in which it will
// return no work.
std::vector<T> pop_batch(const ThreadIndex &thread) {
ThreadState &thread_state = thread_states[thread.thread_num];
if (!thread_state.next_batch.empty())
return std::move(thread_state.next_batch);
// Empty our own work queue first.
{
std::unique_lock lock(thread_state.batches_lock);
if (!thread_state.batches.empty()) {
std::vector<T> batch = std::move(thread_state.batches.back());
thread_state.batches.pop_back();
return batch;
}
}
// From here on in this function, our work queue is empty.
while (true) {
std::vector<T> batch = try_steal(thread);
if (!batch.empty()) {
return std::move(batch);
}
// Termination: if all threads run out of work, then all of
// them will eventually enter this loop and there will be no further
// notifications on waiters_cv, so all will eventually increment
// num_waiters and wait, so num_waiters == num_threads()
// will become true.
std::unique_lock lock(waiters_lock);
++num_waiters;
if (num_waiters == num_threads()) {
waiters_cv.notify_all();
return {};
}
// As above, it's possible that we'll wait here even when there
// are work batches posted by other threads. That's OK.
waiters_cv.wait(lock);
if (num_waiters == num_threads())
return {};
--num_waiters;
}
}
private:
std::vector<T> try_steal(const ThreadIndex &thread) {
for (int i = 1; i < num_threads(); i++) {
int other_thread_num = (thread.thread_num + i) % num_threads();
ThreadState &other_thread_state = thread_states[other_thread_num];
std::unique_lock lock(other_thread_state.batches_lock);
if (!other_thread_state.batches.empty()) {
std::vector<T> batch = std::move(other_thread_state.batches.front());
other_thread_state.batches.pop_front();
return batch;
}
}
return {};
}
int batch_size;
struct ThreadState {
// Entirely thread-local.
std::vector<T> next_batch;
std::mutex batches_lock;
// Only the associated thread ever adds to this, and only at the back.
// Other threads can remove elements from the front.
std::deque<std::vector<T>> batches;
};
std::vector<ThreadState> thread_states;
std::mutex waiters_lock;
std::condition_variable waiters_cv;
// Number of threads waiting for work. Their queues are empty.
int num_waiters = 0;
};
// A monotonic flag. Starts false, and can be set to true in a thread-safe way.
// Once `load()` returns true, it will always return true.
// Uses relaxed atomics so there are no memory ordering guarantees. Do not use this
// to guard access to shared memory.
class MonotonicFlag {
public:
MonotonicFlag() : value(false) {}
bool load() const { return value.load(std::memory_order_relaxed); }
void set() { value.store(true, std::memory_order_relaxed); }
bool set_and_return_old() {
return value.exchange(true, std::memory_order_relaxed);
}
private:
std::atomic<bool> value;
};
YOSYS_NAMESPACE_END
#endif // YOSYS_THREADING_H

View file

@ -125,20 +125,22 @@ public:
};
// ------------------------------------------------
// A simple class for topological sorting
// ------------------------------------------------
// ---------------------------------------------------
// Best-effort topological sorting with loop detection
// ---------------------------------------------------
template <typename T, typename C = std::less<T>> class TopoSort
{
public:
static_assert(!(std::is_pointer<T>::value && std::is_same<C, std::less<T>>::value),
"std::less is run-to-run unstable for pointers");
public:
// We use this ordering of the edges in the adjacency matrix for
// exact compatibility with an older implementation.
struct IndirectCmp {
IndirectCmp(const std::vector<T> &nodes) : node_cmp_(), nodes_(nodes) {}
IndirectCmp(const std::vector<T> &nodes) : node_cmp_(), nodes_(nodes) {}
bool operator()(int a, int b) const
{
log_assert(static_cast<size_t>(a) < nodes_.size());
log_assert(static_cast<size_t>(a) < nodes_.size());
log_assert(static_cast<size_t>(b) < nodes_.size());
return node_cmp_(nodes_[a], nodes_[b]);
}
@ -147,7 +149,9 @@ template <typename T, typename C = std::less<T>> class TopoSort
};
bool analyze_loops;
// The stability doesn't rely on std::less of T, so pointers are safe
std::map<T, int, C> node_to_index;
// edges[i] is the set of nodes with an edge into node i
std::vector<std::set<int, IndirectCmp>> edges;
std::vector<T> sorted;
std::set<std::vector<T>> loops;
@ -160,10 +164,10 @@ template <typename T, typename C = std::less<T>> class TopoSort
int node(T n)
{
auto rv = node_to_index.emplace(n, static_cast<int>(nodes.size()));
if (rv.second) {
nodes.push_back(n);
edges.push_back(std::set<int, IndirectCmp>(indirect_cmp));
auto rv = node_to_index.emplace(n, static_cast<int>(nodes.size()));
if (rv.second) {
nodes.push_back(n);
edges.push_back(std::set<int, IndirectCmp>(indirect_cmp));
}
return rv.first->second;
}
@ -183,13 +187,14 @@ template <typename T, typename C = std::less<T>> class TopoSort
sorted.clear();
found_loops = false;
std::vector<bool> marked_cells(edges.size(), false);
std::vector<bool> active_cells(edges.size(), false);
std::vector<int> active_stack;
std::vector<bool> node_is_sorted(edges.size(), false);
std::vector<bool> node_is_on_stack(edges.size(), false);
// Only used with analyze_loops
std::vector<int> stack;
sorted.reserve(edges.size());
for (const auto &it : node_to_index)
sort_worker(it.second, marked_cells, active_cells, active_stack);
sort_worker(it.second, node_is_sorted, node_is_on_stack, stack);
log_assert(GetSize(sorted) == GetSize(nodes));
@ -211,19 +216,20 @@ template <typename T, typename C = std::less<T>> class TopoSort
return database;
}
private:
private:
bool found_loops;
std::vector<T> nodes;
const IndirectCmp indirect_cmp;
void sort_worker(const int root_index, std::vector<bool> &marked_cells, std::vector<bool> &active_cells, std::vector<int> &active_stack)
void sort_worker(const int root_index, std::vector<bool> &node_is_sorted, std::vector<bool> &node_is_on_stack, std::vector<int> &stack)
{
if (active_cells[root_index]) {
if (node_is_on_stack[root_index]) {
// We've been here before, meaning we have a loop
found_loops = true;
if (analyze_loops) {
std::vector<T> loop;
for (int i = GetSize(active_stack) - 1; i >= 0; i--) {
const int index = active_stack[i];
for (int i = GetSize(stack) - 1; i >= 0; i--) {
const int index = stack[i];
loop.push_back(nodes[index]);
if (index == root_index)
break;
@ -233,23 +239,24 @@ template <typename T, typename C = std::less<T>> class TopoSort
return;
}
if (marked_cells[root_index])
// We're done if we've already sorted this subgraph
if (node_is_sorted[root_index])
return;
if (!edges[root_index].empty()) {
if (analyze_loops)
active_stack.push_back(root_index);
active_cells[root_index] = true;
stack.push_back(root_index);
node_is_on_stack[root_index] = true;
for (int left_n : edges[root_index])
sort_worker(left_n, marked_cells, active_cells, active_stack);
sort_worker(left_n, node_is_sorted, node_is_on_stack, stack);
if (analyze_loops)
active_stack.pop_back();
active_cells[root_index] = false;
stack.pop_back();
node_is_on_stack[root_index] = false;
}
marked_cells[root_index] = true;
node_is_sorted[root_index] = true;
sorted.push_back(nodes[root_index]);
}
};
@ -299,6 +306,24 @@ auto reversed(const T& container) {
return reverse_view{container};
}
// A range of integers [start_, end_) that can be iterated over with a
// C++ range-based for loop.
struct IntRange {
int start_;
int end_;
struct Int {
int v;
int operator*() const { return v; }
Int &operator++() { ++v; return *this; }
bool operator!=(const Int &other) const { return v != other.v; }
};
Int begin() const { return {start_}; }
Int end() const { return {end_}; }
bool operator==(const IntRange &other) const { return start_ == other.start_ && end_ == other.end_; }
bool operator!=(const IntRange &other) const { return !(*this == other); }
};
YOSYS_NAMESPACE_END
#endif

View file

@ -18,8 +18,8 @@
*/
#include "kernel/yosys.h"
#include "kernel/celltypes.h"
#include "kernel/log.h"
#include "kernel/newcelltypes.h"
#ifdef YOSYS_ENABLE_READLINE
# include <readline/readline.h>
@ -92,7 +92,7 @@ const char* yosys_maybe_version() {
}
RTLIL::Design *yosys_design = NULL;
CellTypes yosys_celltypes;
NewCellTypes yosys_celltypes;
#ifdef YOSYS_ENABLE_TCL
Tcl_Interp *yosys_tcl_interp = NULL;
@ -262,7 +262,7 @@ void yosys_setup()
Pass::init_register();
yosys_design = new RTLIL::Design;
yosys_celltypes.setup();
yosys_celltypes.static_cell_types = StaticCellTypes::categories.is_known;
log_push();
}
@ -291,8 +291,6 @@ void yosys_shutdown()
log_errfile = NULL;
log_files.clear();
yosys_celltypes.clear();
#ifdef YOSYS_ENABLE_TCL
if (yosys_tcl_interp != NULL) {
if (!Tcl_InterpDeleted(yosys_tcl_interp)) {

View file

@ -20,7 +20,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/celledges.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/utils.h"
#include "kernel/log_help.h"

View file

@ -1,5 +1,6 @@
#include "kernel/yosys.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/ff.h"
USING_YOSYS_NAMESPACE
@ -123,7 +124,7 @@ struct LibertyStubber {
return;
}
if (RTLIL::builtin_ff_cell_types().count(base_name))
if (StaticCellTypes::categories.is_ff(base_name))
return liberty_flop(base, derived, f);
auto& base_type = ct.cell_types[base_name];

View file

@ -18,7 +18,7 @@
*/
#include "kernel/yosys.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/sigtools.h"
#include "kernel/log_help.h"
@ -488,7 +488,7 @@ static int parse_comma_list(std::set<RTLIL::IdString> &tokens, const std::string
}
}
static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::vector<expand_rule_t> &rules, std::set<RTLIL::IdString> &limits, int max_objects, char mode, CellTypes &ct, bool eval_only)
static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::vector<expand_rule_t> &rules, std::set<RTLIL::IdString> &limits, int max_objects, char mode, NewCellTypes &ct, bool eval_only)
{
int sel_objects = 0;
bool is_input, is_output;
@ -564,7 +564,7 @@ static void select_op_expand(RTLIL::Design *design, const std::string &arg, char
std::vector<expand_rule_t> rules;
std::set<RTLIL::IdString> limits;
CellTypes ct;
NewCellTypes ct;
if (mode != 'x')
ct.setup(design);

View file

@ -310,6 +310,8 @@ struct SetundefPass : public Pass {
RTLIL::SigSpec sig = undriven_signals.export_all();
for (auto &c : sig.chunks()) {
if (!design->selected(module, c.wire))
continue;
RTLIL::Wire * wire;
if (c.wire->width == c.width) {
wire = c.wire;
@ -328,12 +330,12 @@ struct SetundefPass : public Pass {
SigMap sigmap(module);
SigPool undriven_signals;
for (auto &it : module->wires_)
undriven_signals.add(sigmap(it.second));
for (auto wire : module->selected_wires())
undriven_signals.add(sigmap(wire));
for (auto &it : module->wires_)
if (it.second->port_input)
undriven_signals.del(sigmap(it.second));
for (auto wire : module->selected_wires())
if (wire->port_input)
undriven_signals.del(sigmap(wire));
CellTypes ct(design);
for (auto &it : module->cells_)
@ -367,6 +369,14 @@ struct SetundefPass : public Pass {
if (!cell->is_builtin_ff())
continue;
bool cell_selected = design->selected(module, cell);
bool wire_selected = false;
for (auto bit : sigmap(cell->getPort(ID::Q)))
if (bit.wire && design->selected(module, bit.wire))
wire_selected = true;
if (!cell_selected && !wire_selected)
continue;
for (auto bit : sigmap(cell->getPort(ID::Q)))
ffbits.insert(bit);
}
@ -502,14 +512,21 @@ struct SetundefPass : public Pass {
}
}
for (auto &it : module->cells_)
if (!it.second->get_bool_attribute(ID::xprop_decoder))
it.second->rewrite_sigspecs(worker);
for (auto &it : module->processes)
it.second->rewrite_sigspecs(worker);
for (auto cell : module->selected_cells())
if (!cell->get_bool_attribute(ID::xprop_decoder))
cell->rewrite_sigspecs(worker);
for (auto proc : module->selected_processes())
proc->rewrite_sigspecs(worker);
for (auto &it : module->connections_) {
worker(it.first);
worker(it.second);
SigSpec lhs = it.first;
bool selected = false;
for (auto &chunk : lhs.chunks())
if (chunk.wire && module->design->selected(module, chunk.wire))
selected = true;
if (selected) {
worker(it.first);
worker(it.second);
}
}
if (worker.next_bit_mode == MODE_ANYSEQ || worker.next_bit_mode == MODE_ANYCONST)

View file

@ -561,7 +561,7 @@ struct statdata_t {
}
}
if (tech == "xilinx") {
if (tech == "xilinx" || tech == "analogdevices") {
log("\n");
log(" Estimated number of LCs: %10u\n", estimate_xilinx_lc());
}
@ -628,7 +628,7 @@ struct statdata_t {
first_line = false;
}
log("\n }\n");
if (tech == "xilinx") {
if (tech == "xilinx" || tech == "analogdevices") {
log(" \"estimated_num_lc\": %u,\n", estimate_xilinx_lc());
}
if (tech == "cmos") {
@ -710,7 +710,7 @@ struct statdata_t {
log("\n");
log(" }");
}
if (tech == "xilinx") {
if (tech == "xilinx" || tech == "analogdevices") {
log(",\n");
log(" \"estimated_num_lc\": %u", estimate_xilinx_lc());
}
@ -908,7 +908,7 @@ struct StatPass : public Pass {
log("\n");
log(" -tech <technology>\n");
log(" print area estimate for the specified technology. Currently supported\n");
log(" values for <technology>: xilinx, cmos\n");
log(" values for <technology>: xilinx, analogdevices, cmos\n");
log("\n");
log(" -width\n");
log(" annotate internal cell types with their word width.\n");
@ -968,7 +968,7 @@ struct StatPass : public Pass {
if (!json_mode)
log_header(design, "Printing statistics.\n");
if (techname != "" && techname != "xilinx" && techname != "cmos" && !json_mode)
if (techname != "" && techname != "xilinx" && techname != "analogdevices" && techname != "cmos" && !json_mode)
log_cmd_error("Unsupported technology: '%s'\n", techname);
if (json_mode) {

View file

@ -18,7 +18,7 @@
*/
#include "kernel/yosys.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/sigtools.h"
#include "kernel/utils.h"
#include "kernel/log_help.h"

View file

@ -5,6 +5,7 @@
#include "kernel/yosys_common.h"
#include "kernel/sigtools.h"
#include "kernel/satgen.h"
#include "kernel/newcelltypes.h"
YOSYS_NAMESPACE_BEGIN

View file

@ -9,7 +9,6 @@ OBJS += passes/opt/opt_muxtree.o
OBJS += passes/opt/opt_reduce.o
OBJS += passes/opt/opt_dff.o
OBJS += passes/opt/opt_share.o
OBJS += passes/opt/opt_clean.o
OBJS += passes/opt/opt_expr.o
OBJS += passes/opt/opt_hier.o
@ -40,3 +39,5 @@ PEEPOPT_PATTERN += passes/opt/peepopt_formal_clockgateff.pmg
passes/opt/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
$(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^)
endif
include $(YOSYS_SRC)/passes/opt/opt_clean/Makefile.inc

View file

@ -38,6 +38,9 @@ struct ExclusiveDatabase
pool<Cell*> reduce_or;
for (auto cell : module->cells()) {
if (cell->type == ID($eq)) {
SigSpec y_sig = sigmap(cell->getPort(ID::Y));
if (GetSize(y_sig) == 0)
continue;
nonconst_sig = sigmap(cell->getPort(ID::A));
const_sig = sigmap(cell->getPort(ID::B));
if (!const_sig.is_fully_const()) {
@ -45,12 +48,15 @@ struct ExclusiveDatabase
continue;
std::swap(nonconst_sig, const_sig);
}
y_port = sigmap(cell->getPort(ID::Y));
y_port = y_sig[0];
}
else if (cell->type == ID($logic_not)) {
SigSpec y_sig = sigmap(cell->getPort(ID::Y));
if (GetSize(y_sig) == 0)
continue;
nonconst_sig = sigmap(cell->getPort(ID::A));
const_sig = Const(State::S0, GetSize(nonconst_sig));
y_port = sigmap(cell->getPort(ID::Y));
y_port = y_sig[0];
}
else if (cell->type == ID($reduce_or)) {
reduce_or.insert(cell);
@ -84,7 +90,10 @@ struct ExclusiveDatabase
}
if (nonconst_sig.empty())
continue;
y_port = sigmap(cell->getPort(ID::Y));
SigSpec y_sig = sigmap(cell->getPort(ID::Y));
if (GetSize(y_sig) == 0)
continue;
y_port = y_sig[0];
sig_cmp_prev[y_port] = std::make_pair(nonconst_sig,std::move(values));
}
}

View file

@ -1,793 +0,0 @@
/*
* 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/register.h"
#include "kernel/sigtools.h"
#include "kernel/log.h"
#include "kernel/celltypes.h"
#include "kernel/ffinit.h"
#include <stdlib.h>
#include <stdio.h>
#include <set>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
using RTLIL::id2cstr;
struct keep_cache_t
{
Design *design;
dict<Module*, bool> cache;
bool purge_mode = false;
void reset(Design *design = nullptr, bool purge_mode = false)
{
this->design = design;
this->purge_mode = purge_mode;
cache.clear();
}
bool query(Module *module)
{
log_assert(design != nullptr);
if (module == nullptr)
return false;
if (cache.count(module))
return cache.at(module);
cache[module] = true;
if (!module->get_bool_attribute(ID::keep)) {
bool found_keep = false;
for (auto cell : module->cells())
if (query(cell, true /* ignore_specify */)) {
found_keep = true;
break;
}
for (auto wire : module->wires())
if (wire->get_bool_attribute(ID::keep)) {
found_keep = true;
break;
}
cache[module] = found_keep;
}
return cache[module];
}
bool query(Cell *cell, bool ignore_specify = false)
{
if (cell->type.in(ID($assert), ID($assume), ID($live), ID($fair), ID($cover)))
return true;
if (cell->type.in(ID($overwrite_tag)))
return true;
if (!ignore_specify && cell->type.in(ID($specify2), ID($specify3), ID($specrule)))
return true;
if (cell->type == ID($print) || cell->type == ID($check))
return true;
if (cell->has_keep_attr())
return true;
if (!purge_mode && cell->type == ID($scopeinfo))
return true;
if (cell->module && cell->module->design)
return query(cell->module->design->module(cell->type));
return false;
}
};
keep_cache_t keep_cache;
CellTypes ct_reg, ct_all;
int count_rm_cells, count_rm_wires;
void rmunused_module_cells(Module *module, bool verbose)
{
SigMap sigmap(module);
dict<IdString, pool<Cell*>> mem2cells;
pool<IdString> mem_unused;
pool<Cell*> queue, unused;
pool<SigBit> used_raw_bits;
dict<SigBit, pool<Cell*>> wire2driver;
dict<SigBit, vector<string>> driver_driver_logs;
FfInitVals ffinit(&sigmap, module);
SigMap raw_sigmap;
for (auto &it : module->connections_) {
for (int i = 0; i < GetSize(it.second); i++) {
if (it.second[i].wire != nullptr)
raw_sigmap.add(it.first[i], it.second[i]);
}
}
for (auto &it : module->memories) {
mem_unused.insert(it.first);
}
for (Cell *cell : module->cells()) {
if (cell->type.in(ID($memwr), ID($memwr_v2), ID($meminit), ID($meminit_v2))) {
IdString mem_id = cell->getParam(ID::MEMID).decode_string();
mem2cells[mem_id].insert(cell);
}
}
for (auto &it : module->cells_) {
Cell *cell = it.second;
for (auto &it2 : cell->connections()) {
if (ct_all.cell_known(cell->type) && !ct_all.cell_output(cell->type, it2.first))
continue;
for (auto raw_bit : it2.second) {
if (raw_bit.wire == nullptr)
continue;
auto bit = sigmap(raw_bit);
if (bit.wire == nullptr && ct_all.cell_known(cell->type))
driver_driver_logs[raw_sigmap(raw_bit)].push_back(stringf("Driver-driver conflict "
"for %s between cell %s.%s and constant %s in %s: Resolved using constant.",
log_signal(raw_bit), log_id(cell), log_id(it2.first), log_signal(bit), log_id(module)));
if (bit.wire != nullptr)
wire2driver[bit].insert(cell);
}
}
if (keep_cache.query(cell))
queue.insert(cell);
else
unused.insert(cell);
}
for (auto &it : module->wires_) {
Wire *wire = it.second;
if (wire->port_output || wire->get_bool_attribute(ID::keep)) {
for (auto bit : sigmap(wire))
for (auto c : wire2driver[bit])
queue.insert(c), unused.erase(c);
for (auto raw_bit : SigSpec(wire))
used_raw_bits.insert(raw_sigmap(raw_bit));
}
}
while (!queue.empty())
{
pool<SigBit> bits;
pool<IdString> mems;
for (auto cell : queue) {
for (auto &it : cell->connections())
if (!ct_all.cell_known(cell->type) || ct_all.cell_input(cell->type, it.first))
for (auto bit : sigmap(it.second))
bits.insert(bit);
if (cell->type.in(ID($memrd), ID($memrd_v2))) {
IdString mem_id = cell->getParam(ID::MEMID).decode_string();
if (mem_unused.count(mem_id)) {
mem_unused.erase(mem_id);
mems.insert(mem_id);
}
}
}
queue.clear();
for (auto bit : bits)
for (auto c : wire2driver[bit])
if (unused.count(c))
queue.insert(c), unused.erase(c);
for (auto mem : mems)
for (auto c : mem2cells[mem])
if (unused.count(c))
queue.insert(c), unused.erase(c);
}
unused.sort(RTLIL::sort_by_name_id<RTLIL::Cell>());
for (auto cell : unused) {
if (verbose)
log_debug(" removing unused `%s' cell `%s'.\n", cell->type, cell->name);
module->design->scratchpad_set_bool("opt.did_something", true);
if (cell->is_builtin_ff())
ffinit.remove_init(cell->getPort(ID::Q));
module->remove(cell);
count_rm_cells++;
}
for (auto it : mem_unused)
{
if (verbose)
log_debug(" removing unused memory `%s'.\n", it);
delete module->memories.at(it);
module->memories.erase(it);
}
for (auto &it : module->cells_) {
Cell *cell = it.second;
for (auto &it2 : cell->connections()) {
if (ct_all.cell_known(cell->type) && !ct_all.cell_input(cell->type, it2.first))
continue;
for (auto raw_bit : raw_sigmap(it2.second))
used_raw_bits.insert(raw_bit);
}
}
for (auto it : driver_driver_logs) {
if (used_raw_bits.count(it.first))
for (auto msg : it.second)
log_warning("%s\n", msg);
}
}
int count_nontrivial_wire_attrs(RTLIL::Wire *w)
{
int count = w->attributes.size();
count -= w->attributes.count(ID::src);
count -= w->attributes.count(ID::hdlname);
count -= w->attributes.count(ID(scopename));
count -= w->attributes.count(ID::unused_bits);
return count;
}
// Should we pick `s2` over `s1` to represent a signal?
bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool &regs, SigPool &conns, pool<RTLIL::Wire*> &direct_wires)
{
RTLIL::Wire *w1 = s1.wire;
RTLIL::Wire *w2 = s2.wire;
if (w1 == NULL || w2 == NULL)
return w2 == NULL;
if (w1->port_input != w2->port_input)
return w2->port_input;
if ((w1->port_input && w1->port_output) != (w2->port_input && w2->port_output))
return !(w2->port_input && w2->port_output);
if (w1->name.isPublic() && w2->name.isPublic()) {
if (regs.check(s1) != regs.check(s2))
return regs.check(s2);
if (direct_wires.count(w1) != direct_wires.count(w2))
return direct_wires.count(w2) != 0;
if (conns.check_any(s1) != conns.check_any(s2))
return conns.check_any(s2);
}
if (w1 == w2)
return s2.offset < s1.offset;
if (w1->port_output != w2->port_output)
return w2->port_output;
if (w1->name[0] != w2->name[0])
return w2->name.isPublic();
int attrs1 = count_nontrivial_wire_attrs(w1);
int attrs2 = count_nontrivial_wire_attrs(w2);
if (attrs1 != attrs2)
return attrs2 > attrs1;
return w2->name.lt_by_name(w1->name);
}
bool check_public_name(RTLIL::IdString id)
{
if (id.begins_with("$"))
return false;
const std::string &id_str = id.str();
if (id.begins_with("\\_") && (id.ends_with("_") || id_str.find("_[") != std::string::npos))
return false;
if (id_str.find(".$") != std::string::npos)
return false;
return true;
}
bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbose)
{
// `register_signals` and `connected_signals` will help us decide later on
// on picking representatives out of groups of connected signals
SigPool register_signals;
SigPool connected_signals;
if (!purge_mode)
for (auto &it : module->cells_) {
RTLIL::Cell *cell = it.second;
if (ct_reg.cell_known(cell->type)) {
bool clk2fflogic = cell->get_bool_attribute(ID(clk2fflogic));
for (auto &it2 : cell->connections())
if (clk2fflogic ? it2.first == ID::D : ct_reg.cell_output(cell->type, it2.first))
register_signals.add(it2.second);
}
for (auto &it2 : cell->connections())
connected_signals.add(it2.second);
}
SigMap assign_map(module);
// construct a pool of wires which are directly driven by a known celltype,
// this will influence our choice of representatives
pool<RTLIL::Wire*> direct_wires;
{
pool<RTLIL::SigSpec> direct_sigs;
for (auto &it : module->cells_) {
RTLIL::Cell *cell = it.second;
if (ct_all.cell_known(cell->type))
for (auto &it2 : cell->connections())
if (ct_all.cell_output(cell->type, it2.first))
direct_sigs.insert(assign_map(it2.second));
}
for (auto &it : module->wires_) {
if (direct_sigs.count(assign_map(it.second)) || it.second->port_input)
direct_wires.insert(it.second);
}
}
// weight all options for representatives with `compare_signals`,
// the one that wins will be what `assign_map` maps to
for (auto &it : module->wires_) {
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(s2, s1, register_signals, connected_signals, direct_wires))
assign_map.add(s1);
}
}
// we are removing all connections
module->connections_.clear();
// used signals sigmapped
SigPool used_signals;
// used signals pre-sigmapped
SigPool raw_used_signals;
// used signals sigmapped, ignoring drivers (we keep track of this to set `unused_bits`)
SigPool used_signals_nodrivers;
// gather the usage information for cells
for (auto &it : module->cells_) {
RTLIL::Cell *cell = it.second;
for (auto &it2 : cell->connections_) {
assign_map.apply(it2.second); // modify the cell connection in place
raw_used_signals.add(it2.second);
used_signals.add(it2.second);
if (!ct_all.cell_output(cell->type, it2.first))
used_signals_nodrivers.add(it2.second);
}
}
// gather the usage information for ports, wires with `keep`,
// also gather init bits
dict<RTLIL::SigBit, RTLIL::State> init_bits;
for (auto &it : module->wires_) {
RTLIL::Wire *wire = it.second;
if (wire->port_id > 0) {
RTLIL::SigSpec sig = RTLIL::SigSpec(wire);
raw_used_signals.add(sig);
assign_map.apply(sig);
used_signals.add(sig);
if (!wire->port_input)
used_signals_nodrivers.add(sig);
}
if (wire->get_bool_attribute(ID::keep)) {
RTLIL::SigSpec sig = RTLIL::SigSpec(wire);
assign_map.apply(sig);
used_signals.add(sig);
}
auto it2 = wire->attributes.find(ID::init);
if (it2 != wire->attributes.end()) {
RTLIL::Const &val = it2->second;
SigSpec sig = assign_map(wire);
for (int i = 0; i < GetSize(val) && i < GetSize(sig); i++)
if (val[i] != State::Sx)
init_bits[sig[i]] = val[i];
wire->attributes.erase(it2);
}
}
// set init attributes on all wires of a connected group
for (auto wire : module->wires()) {
bool found = false;
Const val(State::Sx, wire->width);
for (int i = 0; i < wire->width; i++) {
auto it = init_bits.find(RTLIL::SigBit(wire, i));
if (it != init_bits.end()) {
val.set(i, it->second);
found = true;
}
}
if (found)
wire->attributes[ID::init] = val;
}
// now decide for each wire if we should be deleting it
pool<RTLIL::Wire*> del_wires_queue;
for (auto wire : module->wires())
{
SigSpec s1 = SigSpec(wire), s2 = assign_map(s1);
log_assert(GetSize(s1) == GetSize(s2));
Const initval;
if (wire->attributes.count(ID::init))
initval = wire->attributes.at(ID::init);
if (GetSize(initval) != GetSize(wire))
initval.resize(GetSize(wire), State::Sx);
if (initval.is_fully_undef())
wire->attributes.erase(ID::init);
if (GetSize(wire) == 0) {
// delete zero-width wires, unless they are module ports
if (wire->port_id == 0)
goto delete_this_wire;
} else
if (wire->port_id != 0 || wire->get_bool_attribute(ID::keep) || !initval.is_fully_undef()) {
// do not delete anything with "keep" or module ports or initialized wires
} else
if (!purge_mode && check_public_name(wire->name) && (raw_used_signals.check_any(s1) || used_signals.check_any(s2) || s1 != s2)) {
// do not get rid of public names unless in purge mode or if the wire is entirely unused, not even aliased
} else
if (!raw_used_signals.check_any(s1)) {
// delete wires that aren't used by anything directly
goto delete_this_wire;
}
if (0)
{
delete_this_wire:
del_wires_queue.insert(wire);
}
else
{
RTLIL::SigSig new_conn;
for (int i = 0; i < GetSize(s1); i++)
if (s1[i] != s2[i]) {
if (s2[i] == State::Sx && (initval[i] == State::S0 || initval[i] == State::S1)) {
s2[i] = initval[i];
initval.set(i, State::Sx);
}
new_conn.first.append(s1[i]);
new_conn.second.append(s2[i]);
}
if (new_conn.first.size() > 0) {
if (initval.is_fully_undef())
wire->attributes.erase(ID::init);
else
wire->attributes.at(ID::init) = initval;
module->connect(new_conn);
}
if (!used_signals_nodrivers.check_all(s2)) {
std::string unused_bits;
for (int i = 0; i < GetSize(s2); i++) {
if (s2[i].wire == NULL)
continue;
if (!used_signals_nodrivers.check(s2[i])) {
if (!unused_bits.empty())
unused_bits += " ";
unused_bits += stringf("%d", i);
}
}
if (unused_bits.empty() || wire->port_id != 0)
wire->attributes.erase(ID::unused_bits);
else
wire->attributes[ID::unused_bits] = RTLIL::Const(unused_bits);
} else {
wire->attributes.erase(ID::unused_bits);
}
}
}
int del_temp_wires_count = 0;
for (auto wire : del_wires_queue) {
if (ys_debug() || (check_public_name(wire->name) && verbose))
log_debug(" removing unused non-port wire %s.\n", wire->name);
else
del_temp_wires_count++;
}
module->remove(del_wires_queue);
count_rm_wires += GetSize(del_wires_queue);
if (verbose && del_temp_wires_count)
log_debug(" removed %d unused temporary wires.\n", del_temp_wires_count);
if (!del_wires_queue.empty())
module->design->scratchpad_set_bool("opt.did_something", true);
return !del_wires_queue.empty();
}
bool rmunused_module_init(RTLIL::Module *module, bool verbose)
{
bool did_something = false;
CellTypes fftypes;
fftypes.setup_internals_mem();
SigMap sigmap(module);
dict<SigBit, State> qbits;
for (auto cell : module->cells())
if (fftypes.cell_known(cell->type) && cell->hasPort(ID::Q))
{
SigSpec sig = cell->getPort(ID::Q);
for (int i = 0; i < GetSize(sig); i++)
{
SigBit bit = sig[i];
if (bit.wire == nullptr || bit.wire->attributes.count(ID::init) == 0)
continue;
Const init = bit.wire->attributes.at(ID::init);
if (i >= GetSize(init) || init[i] == State::Sx || init[i] == State::Sz)
continue;
sigmap.add(bit);
qbits[bit] = init[i];
}
}
for (auto wire : module->wires())
{
if (wire->attributes.count(ID::init) == 0)
continue;
Const init = wire->attributes.at(ID::init);
for (int i = 0; i < GetSize(wire) && i < GetSize(init); i++)
{
if (init[i] == State::Sx || init[i] == State::Sz)
continue;
SigBit wire_bit = SigBit(wire, i);
SigBit mapped_wire_bit = sigmap(wire_bit);
if (wire_bit == mapped_wire_bit)
goto next_wire;
if (mapped_wire_bit.wire) {
if (qbits.count(mapped_wire_bit) == 0)
goto next_wire;
if (qbits.at(mapped_wire_bit) != init[i])
goto next_wire;
}
else {
if (mapped_wire_bit == State::Sx || mapped_wire_bit == State::Sz)
goto next_wire;
if (mapped_wire_bit != init[i]) {
log_warning("Initial value conflict for %s resolving to %s but with init %s.\n", log_signal(wire_bit), log_signal(mapped_wire_bit), log_signal(init[i]));
goto next_wire;
}
}
}
if (verbose)
log_debug(" removing redundant init attribute on %s.\n", log_id(wire));
wire->attributes.erase(ID::init);
did_something = true;
next_wire:;
}
if (did_something)
module->design->scratchpad_set_bool("opt.did_something", true);
return did_something;
}
void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool rminit)
{
if (verbose)
log("Finding unused cells or wires in module %s..\n", module->name);
std::vector<RTLIL::Cell*> delcells;
for (auto cell : module->cells()) {
if (cell->type.in(ID($pos), ID($_BUF_), ID($buf)) && !cell->has_keep_attr()) {
bool is_signed = cell->type == ID($pos) && cell->getParam(ID::A_SIGNED).as_bool();
RTLIL::SigSpec a = cell->getPort(ID::A);
RTLIL::SigSpec y = cell->getPort(ID::Y);
a.extend_u0(GetSize(y), is_signed);
if (a.has_const(State::Sz)) {
SigSpec new_a;
SigSpec new_y;
for (int i = 0; i < GetSize(a); ++i) {
SigBit b = a[i];
if (b == State::Sz)
continue;
new_a.append(b);
new_y.append(y[i]);
}
a = std::move(new_a);
y = std::move(new_y);
}
if (!y.empty())
module->connect(y, a);
delcells.push_back(cell);
} else if (cell->type.in(ID($connect)) && !cell->has_keep_attr()) {
RTLIL::SigSpec a = cell->getPort(ID::A);
RTLIL::SigSpec b = cell->getPort(ID::B);
if (a.has_const() && !b.has_const())
std::swap(a, b);
module->connect(a, b);
delcells.push_back(cell);
} else if (cell->type.in(ID($input_port)) && !cell->has_keep_attr()) {
delcells.push_back(cell);
}
}
for (auto cell : delcells) {
if (verbose) {
if (cell->type == ID($connect))
log_debug(" removing connect cell `%s': %s <-> %s\n", cell->name,
log_signal(cell->getPort(ID::A)), log_signal(cell->getPort(ID::B)));
else if (cell->type == ID($input_port))
log_debug(" removing input port marker cell `%s': %s\n", cell->name,
log_signal(cell->getPort(ID::Y)));
else
log_debug(" removing buffer cell `%s': %s = %s\n", cell->name,
log_signal(cell->getPort(ID::Y)), log_signal(cell->getPort(ID::A)));
}
module->remove(cell);
}
if (!delcells.empty())
module->design->scratchpad_set_bool("opt.did_something", true);
rmunused_module_cells(module, verbose);
while (rmunused_module_signals(module, purge_mode, verbose)) { }
if (rminit && rmunused_module_init(module, verbose))
while (rmunused_module_signals(module, purge_mode, verbose)) { }
}
struct OptCleanPass : public Pass {
OptCleanPass() : Pass("opt_clean", "remove unused cells and wires") { }
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" opt_clean [options] [selection]\n");
log("\n");
log("This pass identifies wires and cells that are unused and removes them. Other\n");
log("passes often remove cells but leave the wires in the design or reconnect the\n");
log("wires but leave the old cells in the design. This pass can be used to clean up\n");
log("after the passes that do the actual work.\n");
log("\n");
log("This pass only operates on completely selected modules without processes.\n");
log("\n");
log(" -purge\n");
log(" also remove internal nets if they have a public name\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
bool purge_mode = false;
log_header(design, "Executing OPT_CLEAN pass (remove unused cells and wires).\n");
log_push();
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-purge") {
purge_mode = true;
continue;
}
break;
}
extra_args(args, argidx, design);
keep_cache.reset(design, purge_mode);
ct_reg.setup_internals_mem();
ct_reg.setup_internals_anyinit();
ct_reg.setup_stdcells_mem();
ct_all.setup(design);
count_rm_cells = 0;
count_rm_wires = 0;
for (auto module : design->selected_whole_modules_warn()) {
if (module->has_processes_warn())
continue;
rmunused_module(module, purge_mode, true, true);
}
if (count_rm_cells > 0 || count_rm_wires > 0)
log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires);
design->optimize();
design->check();
keep_cache.reset();
ct_reg.clear();
ct_all.clear();
log_pop();
request_garbage_collection();
}
} OptCleanPass;
struct CleanPass : public Pass {
CleanPass() : Pass("clean", "remove unused cells and wires") { }
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" clean [options] [selection]\n");
log("\n");
log("This is identical to 'opt_clean', but less verbose.\n");
log("\n");
log("When commands are separated using the ';;' token, this command will be executed\n");
log("between the commands.\n");
log("\n");
log("When commands are separated using the ';;;' token, this command will be executed\n");
log("in -purge mode between the commands.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
bool purge_mode = false;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-purge") {
purge_mode = true;
continue;
}
break;
}
extra_args(args, argidx, design);
keep_cache.reset(design);
ct_reg.setup_internals_mem();
ct_reg.setup_internals_anyinit();
ct_reg.setup_stdcells_mem();
ct_all.setup(design);
count_rm_cells = 0;
count_rm_wires = 0;
for (auto module : design->selected_unboxed_whole_modules()) {
if (module->has_processes())
continue;
rmunused_module(module, purge_mode, ys_debug(), true);
}
log_suppressed();
if (count_rm_cells > 0 || count_rm_wires > 0)
log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires);
design->optimize();
design->check();
keep_cache.reset();
ct_reg.clear();
ct_all.clear();
request_garbage_collection();
}
} CleanPass;
PRIVATE_NAMESPACE_END

View file

@ -0,0 +1,10 @@
OPT_CLEAN_OBJS =
OPT_CLEAN_OBJS += passes/opt/opt_clean/cells_all.o
OPT_CLEAN_OBJS += passes/opt/opt_clean/cells_temp.o
OPT_CLEAN_OBJS += passes/opt/opt_clean/wires.o
OPT_CLEAN_OBJS += passes/opt/opt_clean/inits.o
OPT_CLEAN_OBJS += passes/opt/opt_clean/opt_clean.o
$(OPT_CLEAN_OBJS): passes/opt/opt_clean/opt_clean.h
OBJS += $(OPT_CLEAN_OBJS)

View file

@ -0,0 +1,373 @@
/*
* 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/ffinit.h"
#include "kernel/yosys_common.h"
#include "passes/opt/opt_clean/opt_clean.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
unsigned int hash_bit(const SigBit &bit) {
return static_cast<unsigned int>(hash_ops<SigBit>::hash(bit).yield());
}
SigMap wire_sigmap(const RTLIL::Module* mod) {
SigMap map;
for (auto &it : mod->connections_) {
for (int i = 0; i < GetSize(it.second); i++) {
if (it.second[i].wire != nullptr)
map.add(it.first[i], it.second[i]);
}
}
return map;
}
struct WireDrivers;
// Maps from a SigBit to a unique driver cell.
struct WireDriver {
using Accumulated = WireDrivers;
SigBit bit;
int driver_cell;
};
// Maps from a SigBit to one or more driver cells.
struct WireDrivers {
WireDrivers() : driver_cell(0) {}
WireDrivers(WireDriver driver) : bit(driver.bit), driver_cell(driver.driver_cell) {}
WireDrivers(SigBit bit) : bit(bit), driver_cell(0) {}
WireDrivers(WireDrivers &&other) = default;
class const_iterator {
public:
const_iterator(const WireDrivers &drivers, bool end)
: driver_cell(drivers.driver_cell), in_extra_cells(end) {
if (drivers.extra_driver_cells) {
if (end) {
extra_it = drivers.extra_driver_cells->end();
} else {
extra_it = drivers.extra_driver_cells->begin();
}
}
}
int operator*() const {
if (in_extra_cells)
return **extra_it;
return driver_cell;
}
const_iterator& operator++() {
if (in_extra_cells)
++*extra_it;
else
in_extra_cells = true;
return *this;
}
bool operator!=(const const_iterator &other) const {
return !(*this == other);
}
bool operator==(const const_iterator &other) const {
return in_extra_cells == other.in_extra_cells &&
extra_it == other.extra_it;
}
private:
std::optional<pool<int>::iterator> extra_it;
int driver_cell;
bool in_extra_cells;
};
const_iterator begin() const { return const_iterator(*this, false); }
const_iterator end() const { return const_iterator(*this, true); }
SigBit bit;
int driver_cell;
std::unique_ptr<pool<int>> extra_driver_cells;
};
struct WireDriversKeyEquality {
bool operator()(const WireDrivers &a, const WireDrivers &b) const {
return a.bit == b.bit;
}
};
struct WireDriversCollisionHandler {
void operator()(WireDrivers &incumbent, WireDrivers &new_value) const {
log_assert(new_value.extra_driver_cells == nullptr);
if (!incumbent.extra_driver_cells)
incumbent.extra_driver_cells.reset(new pool<int>());
incumbent.extra_driver_cells->insert(new_value.driver_cell);
}
};
using Wire2Drivers = ShardedHashtable<WireDriver, WireDriversKeyEquality, WireDriversCollisionHandler>;
struct ConflictLogs {
ShardedVector<std::pair<SigBit, std::string>> logs;
ConflictLogs(ParallelDispatchThreadPool::Subpool &subpool) : logs(subpool) {}
void print_warnings(pool<SigBit>& used_raw_bits, const SigMap& wire_map, const RTLIL::Module* mod, CleanRunContext &clean_ctx) {
if (!logs.empty()) {
// We could do this in parallel but hopefully this is rare.
for (auto [_, cell] : mod->cells_) {
for (auto &[port, sig] : cell->connections()) {
if (clean_ctx.ct_all.cell_known(cell->type) && !clean_ctx.ct_all.cell_input(cell->type, port))
continue;
for (auto raw_bit : wire_map(sig))
used_raw_bits.insert(raw_bit);
}
}
for (std::pair<SigBit, std::string> &it : logs) {
if (used_raw_bits.count(it.first))
log_warning("%s\n", it.second);
}
}
}
};
struct CellTraversal {
ConcurrentWorkQueue<int> queue;
Wire2Drivers wire2driver;
dict<std::string, pool<int>> mem2cells;
CellTraversal(int num_threads) : queue(num_threads), wire2driver(), mem2cells() {}
};
struct CellAnalysis {
ShardedVector<Wire*> keep_wires;
std::vector<std::atomic<bool>> unused;
CellAnalysis(AnalysisContext& actx)
: keep_wires(actx.subpool), unused(actx.mod->cells_size()) {}
pool<SigBit> analyze_kept_wires(CellTraversal& traversal, const SigMap& sigmap, const SigMap& wire_map, int num_threads) {
// Also enqueue cells that drive kept wires into cell_queue
// and mark those cells as used
// and mark all bits of those wires as used
pool<SigBit> used_raw_bits;
int i = 0;
for (Wire *wire : keep_wires) {
for (auto bit : sigmap(wire)) {
const WireDrivers *drivers = traversal.wire2driver.find({{bit}, hash_bit(bit)});
if (drivers != nullptr)
for (int cell_index : *drivers)
if (unused[cell_index].exchange(false, std::memory_order_relaxed)) {
ThreadIndex fake_thread_index = {i++ % num_threads};
traversal.queue.push(fake_thread_index, cell_index);
}
}
for (auto raw_bit : SigSpec(wire))
used_raw_bits.insert(wire_map(raw_bit));
}
return used_raw_bits;
}
void mark_used_and_enqueue(int cell_idx, ConcurrentWorkQueue<int>& queue, const ParallelDispatchThreadPool::RunCtx &ctx) {
if (unused[cell_idx].exchange(false, std::memory_order_relaxed))
queue.push(ctx, cell_idx);
}
};
ConflictLogs explore(CellAnalysis& analysis, CellTraversal& traversal, const SigMap& wire_map, AnalysisContext& actx, CleanRunContext &clean_ctx) {
ConflictLogs logs(actx.subpool);
Wire2Drivers::Builder wire2driver_builder(actx.subpool);
ShardedVector<std::pair<std::string, int>> mem2cells_vector(actx.subpool);
// Enqueue kept cells into traversal.queue
// Prepare input cone traversal into traversal.wire2driver
// Prepare "input cone" traversal from memory to write port or meminit as analysis.mem2cells
// Also check driver conflicts
// Also mark cells unused to true unless keep (we override this later)
actx.subpool.run([&analysis, &traversal, &logs, &wire_map, &mem2cells_vector, &wire2driver_builder, &actx, &clean_ctx](const ParallelDispatchThreadPool::RunCtx &ctx) {
for (int i : ctx.item_range(actx.mod->cells_size())) {
Cell *cell = actx.mod->cell_at(i);
if (cell->type.in(ID($memwr), ID($memwr_v2), ID($meminit), ID($meminit_v2)))
mem2cells_vector.insert(ctx, {cell->getParam(ID::MEMID).decode_string(), i});
for (auto &it2 : cell->connections()) {
if (clean_ctx.ct_all.cell_known(cell->type) && !clean_ctx.ct_all.cell_output(cell->type, it2.first))
continue;
for (auto raw_bit : it2.second) {
if (raw_bit.wire == nullptr)
continue;
auto bit = actx.assign_map(raw_bit);
if (bit.wire == nullptr && clean_ctx.ct_all.cell_known(cell->type)) {
std::string msg = stringf("Driver-driver conflict "
"for %s between cell %s.%s and constant %s in %s: Resolved using constant.",
log_signal(raw_bit), cell->name.unescape(), it2.first.unescape(), log_signal(bit), actx.mod->name.unescape());
logs.logs.insert(ctx, {wire_map(raw_bit), msg});
}
if (bit.wire != nullptr)
wire2driver_builder.insert(ctx, {{bit, i}, hash_bit(bit)});
}
}
bool keep = clean_ctx.keep_cache.query(cell);
analysis.unused[i].store(!keep, std::memory_order_relaxed);
if (keep)
traversal.queue.push(ctx, i);
}
for (int i : ctx.item_range(actx.mod->wires_size())) {
Wire *wire = actx.mod->wire_at(i);
if (wire->port_output || wire->get_bool_attribute(ID::keep))
analysis.keep_wires.insert(ctx, wire);
}
});
// Finish by merging per-thread collected data
actx.subpool.run([&wire2driver_builder](const ParallelDispatchThreadPool::RunCtx &ctx) {
wire2driver_builder.process(ctx);
});
traversal.wire2driver = wire2driver_builder;
for (std::pair<std::string, int> &mem2cell : mem2cells_vector)
traversal.mem2cells[mem2cell.first].insert(mem2cell.second);
return logs;
}
struct MemAnalysis {
std::vector<std::atomic<bool>> unused;
dict<std::string, int> indices;
MemAnalysis(const RTLIL::Module* mod) : unused(mod->memories.size()), indices() {
for (int i = 0; i < GetSize(mod->memories); ++i) {
indices[mod->memories.element(i)->first.str()] = i;
unused[i].store(true, std::memory_order_relaxed);
}
}
};
void fixup_unused_cells_and_mems(CellAnalysis& analysis, MemAnalysis& mem_analysis, CellTraversal& traversal, AnalysisContext& actx, CleanRunContext &clean_ctx) {
// Processes the cell queue in batches, traversing input cones by enqueuing more cells
// Discover and mark used memories and cells
actx.subpool.run([&analysis, &mem_analysis, &traversal, &actx, &clean_ctx](const ParallelDispatchThreadPool::RunCtx &ctx) {
pool<SigBit> bits;
pool<std::string> mems;
while (true) {
std::vector<int> cell_indices = traversal.queue.pop_batch(ctx);
if (cell_indices.empty())
return;
for (auto cell_index : cell_indices) {
Cell *cell = actx.mod->cell_at(cell_index);
for (auto &it : cell->connections())
if (!clean_ctx.ct_all.cell_known(cell->type) || clean_ctx.ct_all.cell_input(cell->type, it.first))
for (auto bit : actx.assign_map(it.second))
bits.insert(bit);
if (cell->type.in(ID($memrd), ID($memrd_v2))) {
std::string mem_id = cell->getParam(ID::MEMID).decode_string();
if (mem_analysis.indices.count(mem_id)) {
int mem_index = mem_analysis.indices[mem_id];
// Memory fixup
if (mem_analysis.unused[mem_index].exchange(false, std::memory_order_relaxed))
mems.insert(mem_id);
}
}
}
for (auto bit : bits) {
// Cells fixup
const WireDrivers *drivers = traversal.wire2driver.find({{bit}, hash_bit(bit)});
if (drivers != nullptr)
for (int cell_idx : *drivers)
analysis.mark_used_and_enqueue(cell_idx, traversal.queue, ctx);
}
bits.clear();
for (auto mem : mems) {
if (traversal.mem2cells.count(mem) == 0)
continue;
// Cells fixup
for (int cell_idx : traversal.mem2cells.at(mem))
analysis.mark_used_and_enqueue(cell_idx, traversal.queue, ctx);
}
mems.clear();
}
});
}
pool<Cell*> all_unused_cells(const Module *mod, const CellAnalysis& analysis, Wire2Drivers& wire2driver, ParallelDispatchThreadPool::Subpool &subpool) {
pool<Cell*> unused_cells;
ShardedVector<int> sharded_unused_cells(subpool);
subpool.run([mod, &analysis, &wire2driver, &sharded_unused_cells](const ParallelDispatchThreadPool::RunCtx &ctx) {
// Parallel destruction of `wire2driver`
wire2driver.clear(ctx);
for (int i : ctx.item_range(mod->cells_size()))
if (analysis.unused[i].load(std::memory_order_relaxed))
sharded_unused_cells.insert(ctx, i);
});
for (int cell_index : sharded_unused_cells)
unused_cells.insert(mod->cell_at(cell_index));
unused_cells.sort(RTLIL::sort_by_name_id<RTLIL::Cell>());
return unused_cells;
}
void remove_cells(RTLIL::Module* mod, FfInitVals& ffinit, const pool<Cell*>& cells, bool verbose, RmStats& stats) {
for (auto cell : cells) {
if (verbose)
log_debug(" removing unused `%s' cell `%s'.\n", cell->type, cell->name);
mod->design->scratchpad_set_bool("opt.did_something", true);
if (cell->is_builtin_ff())
ffinit.remove_init(cell->getPort(ID::Q));
mod->remove(cell);
stats.count_rm_cells++;
}
}
void remove_mems(RTLIL::Module* mod, const MemAnalysis& mem_analysis, bool verbose) {
for (const auto &it : mem_analysis.indices) {
if (!mem_analysis.unused[it.second].load(std::memory_order_relaxed))
continue;
RTLIL::IdString id(it.first);
if (verbose)
log_debug(" removing unused memory `%s'.\n", id.unescape());
delete mod->memories.at(id);
mod->memories.erase(id);
}
}
PRIVATE_NAMESPACE_END
YOSYS_NAMESPACE_BEGIN
void rmunused_module_cells(Module *module, ParallelDispatchThreadPool::Subpool &subpool, CleanRunContext &clean_ctx)
{
AnalysisContext actx(module, subpool);
// Used for logging warnings only
SigMap wire_map = wire_sigmap(module);
CellAnalysis analysis(actx);
CellTraversal traversal(subpool.num_threads());
// Mark all unkept cells as unused initially
// and queue up cell traversal from those cells
auto logs = explore(analysis, traversal, wire_map, actx, clean_ctx);
// Mark cells that drive kept wires into cell_queue and those bits as used
// and queue up cell traversal from those cells
pool<SigBit> used_raw_bits = analysis.analyze_kept_wires(traversal, actx.assign_map, wire_map, subpool.num_threads());
// Mark all memories as unused initially
MemAnalysis mem_analysis(module);
// Marked all used cells and mems as used by traversing with cell queue
fixup_unused_cells_and_mems(analysis, mem_analysis, traversal, actx, clean_ctx);
// Analyses are now fully correct
// unused_cells.contains(foo) iff analysis.used[foo] == true
// wire2driver is passed in only to destroy it
pool<Cell*> unused_cells = all_unused_cells(module, analysis, traversal.wire2driver, subpool);
FfInitVals ffinit;
ffinit.set_parallel(&actx.assign_map, subpool.thread_pool(), module);
// Now we know what to kill
remove_cells(module, ffinit, unused_cells, clean_ctx.flags.verbose, clean_ctx.stats);
remove_mems(module, mem_analysis, clean_ctx.flags.verbose);
logs.print_warnings(used_raw_bits, wire_map, module, clean_ctx);
}
YOSYS_NAMESPACE_END

View file

@ -0,0 +1,104 @@
/*
* 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 "passes/opt/opt_clean/opt_clean.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
bool is_signed(RTLIL::Cell* cell) {
return cell->type == ID($pos) && cell->getParam(ID::A_SIGNED).as_bool();
}
bool trim_buf(RTLIL::Cell* cell, ShardedVector<RTLIL::SigSig>& new_connections, const ParallelDispatchThreadPool::RunCtx &ctx) {
RTLIL::SigSpec a = cell->getPort(ID::A);
RTLIL::SigSpec y = cell->getPort(ID::Y);
a.extend_u0(GetSize(y), is_signed(cell));
if (a.has_const(State::Sz)) {
RTLIL::SigSpec new_a;
RTLIL::SigSpec new_y;
for (int i = 0; i < GetSize(a); ++i) {
RTLIL::SigBit b = a[i];
if (b == State::Sz)
return false;
new_a.append(b);
new_y.append(y[i]);
}
a = std::move(new_a);
y = std::move(new_y);
}
if (!y.empty())
new_connections.insert(ctx, {y, a});
return true;
}
bool remove(ShardedVector<RTLIL::Cell*>& cells, RTLIL::Module* mod, bool verbose) {
bool did_something = false;
for (RTLIL::Cell *cell : cells) {
if (verbose) {
if (cell->type == ID($connect))
log_debug(" removing connect cell `%s': %s <-> %s\n", cell->name,
log_signal(cell->getPort(ID::A)), log_signal(cell->getPort(ID::B)));
else if (cell->type == ID($input_port))
log_debug(" removing input port marker cell `%s': %s\n", cell->name,
log_signal(cell->getPort(ID::Y)));
else
log_debug(" removing buffer cell `%s': %s = %s\n", cell->name,
log_signal(cell->getPort(ID::Y)), log_signal(cell->getPort(ID::A)));
}
mod->remove(cell);
did_something = true;
}
return did_something;
}
PRIVATE_NAMESPACE_END
YOSYS_NAMESPACE_BEGIN
void remove_temporary_cells(RTLIL::Module *module, ParallelDispatchThreadPool::Subpool &subpool, bool verbose)
{
ShardedVector<RTLIL::Cell*> delcells(subpool);
ShardedVector<RTLIL::SigSig> new_connections(subpool);
const RTLIL::Module *const_module = module;
subpool.run([const_module, &delcells, &new_connections](const ParallelDispatchThreadPool::RunCtx &ctx) {
for (int i : ctx.item_range(const_module->cells_size())) {
RTLIL::Cell *cell = const_module->cell_at(i);
if (cell->type.in(ID($pos), ID($_BUF_), ID($buf)) && !cell->has_keep_attr()) {
if (trim_buf(cell, new_connections, ctx))
delcells.insert(ctx, cell);
} else if (cell->type.in(ID($connect)) && !cell->has_keep_attr()) {
RTLIL::SigSpec a = cell->getPort(ID::A);
RTLIL::SigSpec b = cell->getPort(ID::B);
if (a.has_const() && !b.has_const())
std::swap(a, b);
new_connections.insert(ctx, {a, b});
delcells.insert(ctx, cell);
} else if (cell->type.in(ID($input_port)) && !cell->has_keep_attr()) {
delcells.insert(ctx, cell);
}
}
});
for (RTLIL::SigSig &connection : new_connections) {
module->connect(connection);
}
if (remove(delcells, module, verbose))
module->design->scratchpad_set_bool("opt.did_something", true);
}
YOSYS_NAMESPACE_END

View file

@ -0,0 +1,137 @@
/*
* 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 "passes/opt/opt_clean/opt_clean.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
ShardedVector<std::pair<SigBit, State>> build_inits(AnalysisContext& actx) {
ShardedVector<std::pair<SigBit, State>> results(actx.subpool);
actx.subpool.run([&results, &actx](const ParallelDispatchThreadPool::RunCtx &ctx) {
for (int i : ctx.item_range(actx.mod->cells_size())) {
RTLIL::Cell *cell = actx.mod->cell_at(i);
if (StaticCellTypes::Compat::internals_mem_ff(cell->type) && cell->hasPort(ID::Q))
{
SigSpec sig = cell->getPort(ID::Q);
for (int i = 0; i < GetSize(sig); i++)
{
SigBit bit = sig[i];
if (bit.wire == nullptr || bit.wire->attributes.count(ID::init) == 0)
continue;
Const init = bit.wire->attributes.at(ID::init);
if (i >= GetSize(init) || init[i] == State::Sx || init[i] == State::Sz)
continue;
results.insert(ctx, {bit, init[i]});
}
}
}
});
return results;
}
dict<SigBit, State> qbits_from_inits(ShardedVector<std::pair<SigBit, State>>& inits, SigMap& assign_map) {
dict<SigBit, State> qbits;
for (std::pair<SigBit, State> &p : inits) {
assign_map.add(p.first);
qbits[p.first] = p.second;
}
return qbits;
}
ShardedVector<RTLIL::Wire*> deferred_init_transfer(const dict<SigBit, State>& qbits, AnalysisContext& actx) {
ShardedVector<RTLIL::Wire*> wire_results(actx.subpool);
actx.subpool.run([&actx, &qbits, &wire_results](const ParallelDispatchThreadPool::RunCtx &ctx) {
for (int j : ctx.item_range(actx.mod->wires_size())) {
RTLIL::Wire *wire = actx.mod->wire_at(j);
if (wire->attributes.count(ID::init) == 0)
continue;
Const init = wire->attributes.at(ID::init);
for (int i = 0; i < GetSize(wire) && i < GetSize(init); i++)
{
if (init[i] == State::Sx || init[i] == State::Sz)
continue;
SigBit wire_bit = SigBit(wire, i);
SigBit mapped_wire_bit = actx.assign_map(wire_bit);
if (wire_bit == mapped_wire_bit)
goto next_wire;
if (mapped_wire_bit.wire) {
if (qbits.count(mapped_wire_bit) == 0)
goto next_wire;
if (qbits.at(mapped_wire_bit) != init[i])
goto next_wire;
}
else {
if (mapped_wire_bit == State::Sx || mapped_wire_bit == State::Sz)
goto next_wire;
if (mapped_wire_bit != init[i]) {
log_warning("Initial value conflict for %s resolving to %s but with init %s.\n", log_signal(wire_bit), log_signal(mapped_wire_bit), log_signal(init[i]));
goto next_wire;
}
}
}
wire_results.insert(ctx, wire);
next_wire:;
}
});
return wire_results;
}
bool remove_redundant_inits(ShardedVector<RTLIL::Wire*> wires, bool verbose) {
bool did_something = false;
for (RTLIL::Wire *wire : wires) {
if (verbose)
log_debug(" removing redundant init attribute on %s.\n", log_id(wire));
wire->attributes.erase(ID::init);
did_something = true;
}
return did_something;
}
PRIVATE_NAMESPACE_END
YOSYS_NAMESPACE_BEGIN
bool rmunused_module_init(RTLIL::Module *module, ParallelDispatchThreadPool::Subpool &subpool, bool verbose)
{
AnalysisContext actx(module, subpool);
ShardedVector<std::pair<SigBit, State>> inits = build_inits(actx);
dict<SigBit, State> qbits = qbits_from_inits(inits, actx.assign_map);
ShardedVector<RTLIL::Wire*> inits_to_transfer = deferred_init_transfer(qbits, actx);
bool did_something = remove_redundant_inits(inits_to_transfer, verbose);
if (did_something)
module->design->scratchpad_set_bool("opt.did_something", true);
return did_something;
}
YOSYS_NAMESPACE_END

View file

@ -0,0 +1,167 @@
/*
* 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/rtlil.h"
#include "kernel/sigtools.h"
#include "kernel/threading.h"
#include "kernel/celltypes.h"
#include "kernel/yosys_common.h"
#ifndef OPT_CLEAN_KEEP_CACHE_H
#define OPT_CLEAN_KEEP_CACHE_H
YOSYS_NAMESPACE_BEGIN
struct KeepCache
{
dict<Module*, bool> keep_modules;
bool purge_mode;
KeepCache(bool purge_mode, ParallelDispatchThreadPool &thread_pool, const std::vector<RTLIL::Module *> &selected_modules)
: purge_mode(purge_mode) {
std::vector<RTLIL::Module *> scan_modules_worklist;
dict<RTLIL::Module *, std::vector<RTLIL::Module*>> dependents;
std::vector<RTLIL::Module *> propagate_kept_modules_worklist;
for (RTLIL::Module *module : selected_modules) {
if (keep_modules.count(module))
continue;
bool keep = scan_module(module, thread_pool, dependents, ALL_CELLS, scan_modules_worklist);
keep_modules[module] = keep;
if (keep)
propagate_kept_modules_worklist.push_back(module);
}
while (!scan_modules_worklist.empty()) {
RTLIL::Module *module = scan_modules_worklist.back();
scan_modules_worklist.pop_back();
if (keep_modules.count(module))
continue;
bool keep = scan_module(module, thread_pool, dependents, MINIMUM_CELLS, scan_modules_worklist);
keep_modules[module] = keep;
if (keep)
propagate_kept_modules_worklist.push_back(module);
}
while (!propagate_kept_modules_worklist.empty()) {
RTLIL::Module *module = propagate_kept_modules_worklist.back();
propagate_kept_modules_worklist.pop_back();
for (RTLIL::Module *dependent : dependents[module]) {
if (keep_modules[dependent])
continue;
keep_modules[dependent] = true;
propagate_kept_modules_worklist.push_back(dependent);
}
}
}
bool query(Cell *cell) const
{
if (keep_cell(cell, purge_mode))
return true;
if (cell->type.in(ID($specify2), ID($specify3), ID($specrule)))
return true;
if (cell->module && cell->module->design) {
RTLIL::Module *cell_module = cell->module->design->module(cell->type);
return cell_module != nullptr && keep_modules.at(cell_module);
}
return false;
}
private:
enum ScanCells {
// Scan every cell to see if it uses a module that is kept.
ALL_CELLS,
// Stop scanning cells if we determine early that this module is kept.
MINIMUM_CELLS,
};
bool scan_module(Module *module, ParallelDispatchThreadPool &thread_pool, dict<RTLIL::Module *, std::vector<RTLIL::Module*>> &dependents,
ScanCells scan_cells, std::vector<Module*> &worklist) const
{
MonotonicFlag keep_module;
if (module->get_bool_attribute(ID::keep)) {
if (scan_cells == MINIMUM_CELLS)
return true;
keep_module.set();
}
ParallelDispatchThreadPool::Subpool subpool(thread_pool, ThreadPool::work_pool_size(0, module->cells_size(), 1000));
ShardedVector<Module*> deps(subpool);
const RTLIL::Module *const_module = module;
bool purge_mode = this->purge_mode;
subpool.run([purge_mode, const_module, scan_cells, &deps, &keep_module](const ParallelDispatchThreadPool::RunCtx &ctx) {
bool keep = false;
for (int i : ctx.item_range(const_module->cells_size())) {
Cell *cell = const_module->cell_at(i);
if (keep_cell(cell, purge_mode)) {
if (scan_cells == MINIMUM_CELLS) {
keep_module.set();
return;
}
keep = true;
}
if (const_module->design) {
RTLIL::Module *cell_module = const_module->design->module(cell->type);
if (cell_module != nullptr)
deps.insert(ctx, cell_module);
}
}
if (keep) {
keep_module.set();
return;
}
for (int i : ctx.item_range(const_module->wires_size())) {
Wire *wire = const_module->wire_at(i);
if (wire->get_bool_attribute(ID::keep)) {
keep_module.set();
return;
}
}
});
if (scan_cells == MINIMUM_CELLS && keep_module.load())
return true;
for (Module *dep : deps) {
dependents[dep].push_back(module);
worklist.push_back(dep);
}
return keep_module.load();
}
static bool keep_cell(Cell *cell, bool purge_mode)
{
if (cell->type.in(ID($assert), ID($assume), ID($live), ID($fair), ID($cover)))
return true;
if (cell->type.in(ID($overwrite_tag)))
return true;
if (cell->type == ID($print) || cell->type == ID($check))
return true;
if (cell->has_keep_attr())
return true;
if (!purge_mode && cell->type == ID($scopeinfo))
return true;
return false;
}
};
YOSYS_NAMESPACE_END
#endif /* OPT_CLEAN_KEEP_CACHE_H */

View file

@ -0,0 +1,151 @@
/*
* 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/register.h"
#include "kernel/log.h"
#include "passes/opt/opt_clean/opt_clean.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
void rmunused_module(RTLIL::Module *module, bool rminit, CleanRunContext &clean_ctx)
{
if (clean_ctx.flags.verbose)
log("Finding unused cells or wires in module %s..\n", module->name);
// 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::work_pool_size(0, module->cells_size(), 10000);
ParallelDispatchThreadPool::Subpool subpool(clean_ctx.thread_pool, num_worker_threads);
remove_temporary_cells(module, subpool, clean_ctx.flags.verbose);
rmunused_module_cells(module, subpool, clean_ctx);
while (rmunused_module_signals(module, subpool, clean_ctx)) { }
if (rminit && rmunused_module_init(module, subpool, clean_ctx.flags.verbose))
while (rmunused_module_signals(module, subpool, clean_ctx)) { }
}
struct OptCleanPass : public Pass {
OptCleanPass() : Pass("opt_clean", "remove unused cells and wires") { }
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" opt_clean [options] [selection]\n");
log("\n");
log("This pass identifies wires and cells that are unused and removes them. Other\n");
log("passes often remove cells but leave the wires in the design or reconnect the\n");
log("wires but leave the old cells in the design. This pass can be used to clean up\n");
log("after the passes that do the actual work.\n");
log("\n");
log("This pass only operates on completely selected modules without processes.\n");
log("\n");
log(" -purge\n");
log(" also remove internal nets if they have a public name\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
bool purge_mode = false;
log_header(design, "Executing OPT_CLEAN pass (remove unused cells and wires).\n");
log_push();
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-purge") {
purge_mode = true;
continue;
}
break;
}
extra_args(args, argidx, design);
{
std::vector<RTLIL::Module*> selected_modules;
for (auto module : design->selected_whole_modules_warn())
if (!module->has_processes_warn())
selected_modules.push_back(module);
CleanRunContext clean_ctx(design, selected_modules, {purge_mode, true});
for (auto module : selected_modules)
rmunused_module(module, true, clean_ctx);
clean_ctx.stats.log();
design->optimize();
design->check();
}
log_pop();
request_garbage_collection();
}
} OptCleanPass;
struct CleanPass : public Pass {
CleanPass() : Pass("clean", "remove unused cells and wires") { }
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" clean [options] [selection]\n");
log("\n");
log("This is identical to 'opt_clean', but less verbose.\n");
log("\n");
log("When commands are separated using the ';;' token, this command will be executed\n");
log("between the commands.\n");
log("\n");
log("When commands are separated using the ';;;' token, this command will be executed\n");
log("in -purge mode between the commands.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
bool purge_mode = false;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-purge") {
purge_mode = true;
continue;
}
break;
}
extra_args(args, argidx, design);
{
std::vector<RTLIL::Module*> selected_modules;
for (auto module : design->selected_unboxed_whole_modules())
if (!module->has_processes())
selected_modules.push_back(module);
CleanRunContext clean_ctx(design, selected_modules, {purge_mode, ys_debug()});
for (auto module : selected_modules)
rmunused_module(module, true, clean_ctx);
log_suppressed();
clean_ctx.stats.log();
design->optimize();
design->check();
}
request_garbage_collection();
}
} CleanPass;
PRIVATE_NAMESPACE_END

View file

@ -0,0 +1,92 @@
/*
* 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/rtlil.h"
#include "kernel/threading.h"
#include "passes/opt/opt_clean/keep_cache.h"
#ifndef OPT_CLEAN_SHARED_H
#define OPT_CLEAN_SHARED_H
YOSYS_NAMESPACE_BEGIN
struct AnalysisContext {
SigMap assign_map;
const RTLIL::Module *mod;
ParallelDispatchThreadPool::Subpool &subpool;
AnalysisContext(RTLIL::Module* m, ParallelDispatchThreadPool::Subpool &p) : assign_map(m), mod(m), subpool(p) {}
};
struct RmStats {
int count_rm_cells = 0;
int count_rm_wires = 0;
void log()
{
if (count_rm_cells > 0 || count_rm_wires > 0)
YOSYS_NAMESPACE_PREFIX log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires);
}
};
struct Flags {
bool purge = false;
bool verbose = false;
};
struct CleanRunContext {
static constexpr auto ct_reg = StaticCellTypes::Categories::join(
StaticCellTypes::Compat::mem_ff,
StaticCellTypes::categories.is_anyinit);
NewCellTypes ct_all;
RmStats stats;
ParallelDispatchThreadPool thread_pool;
KeepCache keep_cache;
Flags flags;
private:
// Helper to compute thread pool size
static int compute_thread_pool_size(const std::vector<RTLIL::Module*>& selected_modules) {
int thread_pool_size = 0;
for (auto module : selected_modules)
thread_pool_size = std::max(thread_pool_size,
ThreadPool::work_pool_size(0, module->cells_size(), 10000));
return thread_pool_size;
}
public:
CleanRunContext(RTLIL::Design* design, const std::vector<RTLIL::Module*>& selected_modules, Flags f)
: thread_pool(compute_thread_pool_size(selected_modules)),
keep_cache(f.purge, thread_pool, selected_modules),
flags(f)
{
ct_all.setup(design);
}
~CleanRunContext() {
ct_all.clear();
}
};
void remove_temporary_cells(RTLIL::Module *module, ParallelDispatchThreadPool::Subpool &subpool, bool verbose);
void rmunused_module_cells(Module *module, ParallelDispatchThreadPool::Subpool &subpool, CleanRunContext &clean_ctx);
bool rmunused_module_signals(RTLIL::Module *module, ParallelDispatchThreadPool::Subpool &subpool, CleanRunContext &clean_ctx);
bool rmunused_module_init(RTLIL::Module *module, ParallelDispatchThreadPool::Subpool &subpool, bool verbose);
YOSYS_NAMESPACE_END
#endif /* OPT_CLEAN_SHARED_H */

View file

@ -0,0 +1,585 @@
/*
* 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 "passes/opt/opt_clean/opt_clean.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
// No collision handler for these, since we will use them such that collisions don't happen
struct ShardedSigBit {
using Accumulated = ShardedSigBit;
RTLIL::SigBit bit;
ShardedSigBit() = default;
ShardedSigBit(const RTLIL::SigBit &bit) : bit(bit) {}
};
struct ShardedSigBitEquality {
bool operator()(const ShardedSigBit &b1, const ShardedSigBit &b2) const {
return b1.bit == b2.bit;
}
};
using ShardedSigPool = ShardedHashtable<ShardedSigBit, ShardedSigBitEquality, SetCollisionHandler<ShardedSigBit>>;
struct ShardedSigSpec {
using Accumulated = ShardedSigSpec;
RTLIL::SigSpec spec;
ShardedSigSpec() = default;
ShardedSigSpec(RTLIL::SigSpec spec) : spec(std::move(spec)) {}
ShardedSigSpec(ShardedSigSpec &&) = default;
};
struct ShardedSigSpecEquality {
bool operator()(const ShardedSigSpec &s1, const ShardedSigSpec &s2) const {
return s1.spec == s2.spec;
}
};
using ShardedSigSpecPool = ShardedHashtable<ShardedSigSpec, ShardedSigSpecEquality, SetCollisionHandler<ShardedSigSpec>>;
struct ExactCellWires {
const ShardedSigSpecPool &exact_cells;
const SigMap &assign_map;
dict<RTLIL::Wire *, bool> cache;
ExactCellWires(const ShardedSigSpecPool &exact_cells, const SigMap &assign_map) : exact_cells(exact_cells), assign_map(assign_map) {}
void cache_result_for_bit(const SigBit &bit) {
if (bit.wire != nullptr)
(void)is_exactly_cell_driven(bit.wire);
}
bool is_exactly_cell_driven(RTLIL::Wire *wire) {
if (wire->port_input)
return true;
auto it = cache.find(wire);
if (it != cache.end())
return it->second;
SigSpec sig = assign_map(wire);
bool direct = exact_cells.find({sig, sig.hash_into(Hasher()).yield()}) != nullptr;
cache.insert({wire, direct});
return direct;
}
void cache_all(ShardedVector<RTLIL::SigBit> &bits) {
for (RTLIL::SigBit candidate : bits) {
cache_result_for_bit(candidate);
cache_result_for_bit(assign_map(candidate));
}
}
};
int count_nontrivial_wire_attrs(RTLIL::Wire *w)
{
int count = w->attributes.size();
count -= w->attributes.count(ID::src);
count -= w->attributes.count(ID::hdlname);
count -= w->attributes.count(ID::scopename);
count -= w->attributes.count(ID::unused_bits);
return count;
}
// Should we pick `s2` over `s1` to represent a signal?
bool compare_signals(const RTLIL::SigBit &s1, const RTLIL::SigBit &s2, const ShardedSigPool &regs, const ShardedSigPool &conns, ExactCellWires &cell_wires)
{
if (s1 == s2)
return false;
RTLIL::Wire *w1 = s1.wire;
RTLIL::Wire *w2 = s2.wire;
if (w1 == NULL || w2 == NULL)
return w2 == NULL;
if (w1->port_input != w2->port_input)
return w2->port_input;
if ((w1->port_input && w1->port_output) != (w2->port_input && w2->port_output))
return !(w2->port_input && w2->port_output);
if (w1->name.isPublic() && w2->name.isPublic()) {
ShardedSigPool::AccumulatedValue s1_val = {s1, s1.hash_top().yield()};
ShardedSigPool::AccumulatedValue s2_val = {s2, s2.hash_top().yield()};
bool regs1 = regs.find(s1_val) != nullptr;
bool regs2 = regs.find(s2_val) != nullptr;
if (regs1 != regs2)
return regs2;
bool w1_exact = cell_wires.is_exactly_cell_driven(w1);
bool w2_exact = cell_wires.is_exactly_cell_driven(w2);
if (w1_exact != w2_exact)
return w2_exact;
bool conns1 = conns.find(s1_val) != nullptr;
bool conns2 = conns.find(s2_val) != nullptr;
if (conns1 != conns2)
return conns2;
}
if (w1 == w2)
return s2.offset < s1.offset;
if (w1->port_output != w2->port_output)
return w2->port_output;
if (w1->name[0] != w2->name[0])
return w2->name.isPublic();
int attrs1 = count_nontrivial_wire_attrs(w1);
int attrs2 = count_nontrivial_wire_attrs(w2);
if (attrs1 != attrs2)
return attrs2 > attrs1;
return w2->name.lt_by_name(w1->name);
}
bool check_public_name(RTLIL::IdString id)
{
if (id.begins_with("$"))
return false;
const std::string &id_str = id.str();
if (id.begins_with("\\_") && (id.ends_with("_") || id_str.find("_[") != std::string::npos))
return false;
if (id_str.find(".$") != std::string::npos)
return false;
return true;
}
void add_spec(ShardedSigPool::Builder &builder, const ThreadIndex &thread, const RTLIL::SigSpec &spec) {
for (SigBit bit : spec)
if (bit.wire != nullptr)
builder.insert(thread, {bit, bit.hash_top().yield()});
}
bool check_any(const ShardedSigPool &sigs, const RTLIL::SigSpec &spec) {
for (SigBit b : spec)
if (sigs.find({b, b.hash_top().yield()}) != nullptr)
return true;
return false;
}
bool check_all(const ShardedSigPool &sigs, const RTLIL::SigSpec &spec) {
for (SigBit b : spec)
if (sigs.find({b, b.hash_top().yield()}) == nullptr)
return false;
return true;
}
struct UpdateConnection {
RTLIL::Cell *cell;
RTLIL::IdString port;
RTLIL::SigSpec spec;
};
void fixup_cell_ports(ShardedVector<UpdateConnection> &update_connections)
{
for (UpdateConnection &update : update_connections)
update.cell->connections_.at(update.port) = std::move(update.spec);
}
struct InitBits {
dict<SigBit, RTLIL::State> values;
// Wires that appear in the keys of the `values` dict
pool<Wire*> wires;
// Set init attributes on all wires of a connected group
void apply_normalised_inits() {
for (RTLIL::Wire *wire : wires) {
bool found = false;
Const val(State::Sx, wire->width);
for (int i = 0; i < wire->width; i++) {
auto it = values.find(RTLIL::SigBit(wire, i));
if (it != values.end()) {
val.set(i, it->second);
found = true;
}
}
if (found)
wire->attributes[ID::init] = val;
}
}
};
static InitBits consume_inits(ShardedVector<RTLIL::Wire*> &initialized_wires, const SigMap &assign_map)
{
InitBits init_bits;
for (RTLIL::Wire *initialized_wire : initialized_wires) {
auto it = initialized_wire->attributes.find(ID::init);
RTLIL::Const &val = it->second;
SigSpec sig = assign_map(initialized_wire);
for (int i = 0; i < GetSize(val) && i < GetSize(sig); i++)
if (val[i] != State::Sx && sig[i].wire != nullptr) {
init_bits.values[sig[i]] = val[i];
init_bits.wires.insert(sig[i].wire);
}
initialized_wire->attributes.erase(it);
}
return init_bits;
}
/**
* What kinds of things are signals connected to?
* Helps pick representatives out of groups of connected signals */
struct SigConnKinds {
// Wire bits directly driven by registers (with clk2fflogic exception)
ShardedSigPool raw_registers;
// Wire bits directly connected to any cell port
ShardedSigPool raw_cell_connected;
// Signals exactly driven by a known cell output,
// this will influence only our choice of representatives.
// A signal is exactly driven by a cell output iff all its bits are driven by this output
// and all bits of this output drive a bit of this signal.
// Additionally, all signals that sigmap to this signal are exactly driven by the port, too
ShardedSigSpecPool exact_cells;
SigConnKinds(bool purge_mode, const AnalysisContext& actx, CleanRunContext& clean_ctx) {
ShardedSigPool::Builder raw_register_builder(actx.subpool);
ShardedSigPool::Builder raw_cell_connected_builder(actx.subpool);
ShardedSigSpecPool::Builder exact_cell_output_builder(actx.subpool);
actx.subpool.run([&exact_cell_output_builder, &raw_register_builder, &raw_cell_connected_builder, purge_mode, &actx, &clean_ctx](const ParallelDispatchThreadPool::RunCtx &ctx) {
for (int i : ctx.item_range(actx.mod->cells_size())) {
RTLIL::Cell *cell = actx.mod->cell_at(i);
if (!purge_mode) {
if (clean_ctx.ct_reg(cell->type)) {
// Improve witness signal naming when clk2fflogic used
// see commit message e36c71b5
bool clk2fflogic = cell->get_bool_attribute(ID::clk2fflogic);
for (auto &[port, sig] : cell->connections())
if (clk2fflogic ? port == ID::D : clean_ctx.ct_all.cell_output(cell->type, port))
add_spec(raw_register_builder, ctx, sig);
}
for (auto &[_, sig] : cell->connections())
add_spec(raw_cell_connected_builder, ctx, sig);
}
if (clean_ctx.ct_all.cell_known(cell->type))
for (auto &[port, sig] : cell->connections())
if (clean_ctx.ct_all.cell_output(cell->type, port)) {
RTLIL::SigSpec spec = actx.assign_map(sig);
unsigned int hash = spec.hash_into(Hasher()).yield();
exact_cell_output_builder.insert(ctx, {std::move(spec), hash});
}
}
});
actx.subpool.run([&raw_register_builder, &raw_cell_connected_builder, &exact_cell_output_builder](const ParallelDispatchThreadPool::RunCtx &ctx) {
raw_register_builder.process(ctx);
raw_cell_connected_builder.process(ctx);
exact_cell_output_builder.process(ctx);
});
raw_registers = raw_register_builder;
raw_cell_connected = raw_cell_connected_builder;
exact_cells = exact_cell_output_builder;
}
void clear(const ParallelDispatchThreadPool::RunCtx &ctx) {
raw_registers.clear(ctx);
raw_cell_connected.clear(ctx);
exact_cells.clear(ctx);
}
};
ShardedVector<RTLIL::SigBit> build_candidates(ExactCellWires& cell_wires, const SigConnKinds& sig_analysis, const AnalysisContext& actx) {
ShardedVector<RTLIL::SigBit> candidates(actx.subpool);
actx.subpool.run([&actx, &sig_analysis, &candidates, &cell_wires](const ParallelDispatchThreadPool::RunCtx &ctx) {
std::optional<ExactCellWires> local_cell_wires;
ExactCellWires *this_thread_cell_wires = &cell_wires;
if (ctx.thread_num > 0) {
local_cell_wires.emplace(sig_analysis.exact_cells, actx.assign_map);
this_thread_cell_wires = &local_cell_wires.value();
}
for (int i : ctx.item_range(actx.mod->wires_size())) {
RTLIL::Wire *wire = actx.mod->wire_at(i);
for (int j = 0; j < wire->width; ++j) {
RTLIL::SigBit s1(wire, j);
RTLIL::SigBit s2 = actx.assign_map(s1);
if (compare_signals(s2, s1, sig_analysis.raw_registers, sig_analysis.raw_cell_connected, *this_thread_cell_wires))
candidates.insert(ctx, s1);
}
}
});
return candidates;
}
void update_assign_map(SigMap& assign_map, ShardedVector<RTLIL::SigBit>& sigmap_canonical_candidates, ExactCellWires& cell_wires, const SigConnKinds& sig_analysis) {
for (RTLIL::SigBit candidate : sigmap_canonical_candidates) {
RTLIL::SigBit current_canonical = assign_map(candidate);
// Resolves if two threads in build_candidates found different candidates
// for the same set
// TODO adds effort for single-threaded?
if (compare_signals(current_canonical, candidate, sig_analysis.raw_registers, sig_analysis.raw_cell_connected, cell_wires))
assign_map.add(candidate);
}
}
struct DeferredUpdates {
// Deferred updates to the assign_map
ShardedVector<UpdateConnection> update_connections;
// Wires we should remove init from
ShardedVector<RTLIL::Wire*> initialized_wires;
DeferredUpdates(ParallelDispatchThreadPool::Subpool &subpool) : update_connections(subpool), initialized_wires(subpool) {}
};
struct UsedSignals {
// here, "connected" means "driven or driving something"
// meanwhile, "used" means "driving something"
// sigmapped
ShardedSigPool connected;
// pre-sigmapped
ShardedSigPool raw_connected;
// sigmapped
ShardedSigPool used;
void clear(ParallelDispatchThreadPool::Subpool &subpool) {
subpool.run([this](const ParallelDispatchThreadPool::RunCtx &ctx) {
connected.clear(ctx);
raw_connected.clear(ctx);
used.clear(ctx);
});
}
};
DeferredUpdates analyse_connectivity(UsedSignals& used, SigConnKinds& sig_analysis, const AnalysisContext& actx, CleanRunContext &clean_ctx) {
DeferredUpdates deferred(actx.subpool);
ShardedSigPool::Builder conn_builder(actx.subpool);
ShardedSigPool::Builder raw_conn_builder(actx.subpool);
ShardedSigPool::Builder used_builder(actx.subpool);
// gather the usage information for cells and update cell connections with the altered sigmap
// also gather the usage information for ports, wires with `keep`
// also gather init bits
actx.subpool.run([&deferred, &conn_builder, &raw_conn_builder, &used_builder, &sig_analysis, &actx, &clean_ctx](const ParallelDispatchThreadPool::RunCtx &ctx) {
// Parallel destruction of these sharded structures
sig_analysis.clear(ctx);
for (int i : ctx.item_range(actx.mod->cells_size())) {
RTLIL::Cell *cell = actx.mod->cell_at(i);
for (const auto &[port, sig] : cell->connections_) {
SigSpec spec = actx.assign_map(sig);
if (spec != sig)
deferred.update_connections.insert(ctx, {cell, port, spec});
add_spec(raw_conn_builder, ctx, spec);
add_spec(conn_builder, ctx, spec);
if (!clean_ctx.ct_all.cell_output(cell->type, port))
add_spec(used_builder, ctx, spec);
}
}
for (int i : ctx.item_range(actx.mod->wires_size())) {
RTLIL::Wire *wire = actx.mod->wire_at(i);
if (wire->port_id > 0) {
RTLIL::SigSpec sig = RTLIL::SigSpec(wire);
add_spec(raw_conn_builder, ctx, sig);
actx.assign_map.apply(sig);
add_spec(conn_builder, ctx, sig);
if (!wire->port_input)
add_spec(used_builder, ctx, sig);
}
if (wire->get_bool_attribute(ID::keep)) {
RTLIL::SigSpec sig = RTLIL::SigSpec(wire);
actx.assign_map.apply(sig);
add_spec(conn_builder, ctx, sig);
}
auto it = wire->attributes.find(ID::init);
if (it != wire->attributes.end())
deferred.initialized_wires.insert(ctx, wire);
}
});
actx.subpool.run([&conn_builder, &raw_conn_builder, &used_builder](const ParallelDispatchThreadPool::RunCtx &ctx) {
conn_builder.process(ctx);
raw_conn_builder.process(ctx);
used_builder.process(ctx);
});
used = {conn_builder, raw_conn_builder, used_builder};
return deferred;
}
struct WireDeleter {
pool<RTLIL::Wire*> del_wires_queue;
ShardedVector<RTLIL::Wire*> remove_init;
ShardedVector<std::pair<RTLIL::Wire*, RTLIL::Const>> set_init;
ShardedVector<RTLIL::SigSig> new_connections;
ShardedVector<RTLIL::Wire*> remove_unused_bits;
ShardedVector<std::pair<RTLIL::Wire*, RTLIL::Const>> set_unused_bits;
WireDeleter(UsedSignals& used_sig_analysis, bool purge_mode, const AnalysisContext& actx) :
remove_init(actx.subpool),
set_init(actx.subpool),
new_connections(actx.subpool),
remove_unused_bits(actx.subpool),
set_unused_bits(actx.subpool) {
ShardedVector<RTLIL::Wire*> del_wires(actx.subpool);
actx.subpool.run([&actx, purge_mode, &del_wires, &used_sig_analysis, this](const ParallelDispatchThreadPool::RunCtx &ctx) {
for (int i : ctx.item_range(actx.mod->wires_size())) {
RTLIL::Wire *wire = actx.mod->wire_at(i);
SigSpec s1 = SigSpec(wire), s2 = actx.assign_map(s1);
log_assert(GetSize(s1) == GetSize(s2));
Const initval;
bool has_init_attribute = wire->attributes.count(ID::init);
bool init_changed = false;
if (has_init_attribute)
initval = wire->attributes.at(ID::init);
if (GetSize(initval) != GetSize(wire)) {
initval.resize(GetSize(wire), State::Sx);
init_changed = true;
}
if (GetSize(wire) == 0) {
// delete zero-width wires, unless they are module ports
if (wire->port_id == 0)
goto delete_this_wire;
} else
if (wire->port_id != 0 || wire->get_bool_attribute(ID::keep) || !initval.is_fully_undef()) {
// do not delete anything with "keep" or module ports or initialized wires
} else
if (!purge_mode && check_public_name(wire->name) && (check_any(used_sig_analysis.raw_connected, s1) || check_any(used_sig_analysis.connected, s2) || s1 != s2)) {
// do not get rid of public names unless in purge mode or if the wire is entirely unused, not even aliased
} else
if (!check_any(used_sig_analysis.raw_connected, s1)) {
// delete wires that aren't used by anything directly
goto delete_this_wire;
}
if (0)
{
delete_this_wire:
del_wires.insert(ctx, wire);
}
else
{
RTLIL::SigSig new_conn;
for (int i = 0; i < GetSize(s1); i++)
if (s1[i] != s2[i]) {
if (s2[i] == State::Sx && (initval[i] == State::S0 || initval[i] == State::S1)) {
s2[i] = initval[i];
initval.set(i, State::Sx);
init_changed = true;
}
new_conn.first.append(s1[i]);
new_conn.second.append(s2[i]);
}
if (new_conn.first.size() > 0)
new_connections.insert(ctx, std::move(new_conn));
if (initval.is_fully_undef()) {
if (has_init_attribute)
remove_init.insert(ctx, wire);
} else
if (init_changed)
set_init.insert(ctx, {wire, std::move(initval)});
std::string unused_bits;
if (!check_all(used_sig_analysis.used, s2)) {
for (int i = 0; i < GetSize(s2); i++) {
if (s2[i].wire == NULL)
continue;
SigBit b = s2[i];
if (used_sig_analysis.used.find({b, b.hash_top().yield()}) == nullptr) {
if (!unused_bits.empty())
unused_bits += " ";
unused_bits += stringf("%d", i);
}
}
}
if (unused_bits.empty() || wire->port_id != 0) {
if (wire->attributes.count(ID::unused_bits))
remove_unused_bits.insert(ctx, wire);
} else {
RTLIL::Const unused_bits_const(std::move(unused_bits));
if (wire->attributes.count(ID::unused_bits)) {
RTLIL::Const &unused_bits_attr = wire->attributes.at(ID::unused_bits);
if (unused_bits_attr != unused_bits_const)
set_unused_bits.insert(ctx, {wire, std::move(unused_bits_const)});
} else
set_unused_bits.insert(ctx, {wire, std::move(unused_bits_const)});
}
}
}
});
del_wires_queue.insert(del_wires.begin(), del_wires.end());
}
// Decide for each wire if we should be deleting it
// and fix up attributes
void commit_changes(RTLIL::Module* mod) {
for (RTLIL::Wire *wire : remove_init)
wire->attributes.erase(ID::init);
for (auto &p : set_init)
p.first->attributes[ID::init] = std::move(p.second);
for (auto &conn : new_connections)
mod->connect(std::move(conn));
for (RTLIL::Wire *wire : remove_unused_bits)
wire->attributes.erase(ID::unused_bits);
for (auto &p : set_unused_bits)
p.first->attributes[ID::unused_bits] = std::move(p.second);
}
int delete_wires(RTLIL::Module* mod, bool verbose) {
int deleted_and_unreported = 0;
for (auto wire : del_wires_queue) {
if (ys_debug() || (check_public_name(wire->name) && verbose))
log_debug(" removing unused non-port wire %s.\n", wire->name);
else
deleted_and_unreported++;
}
mod->remove(del_wires_queue);
return deleted_and_unreported;
}
};
PRIVATE_NAMESPACE_END
YOSYS_NAMESPACE_BEGIN
bool rmunused_module_signals(RTLIL::Module *module, ParallelDispatchThreadPool::Subpool &subpool, CleanRunContext &clean_ctx)
{
// Passing actx to function == function does parallel work
// Not passing module as function argument == function does not modify module
// The above sentence signals intent; it's not enforced due to constness laundering in wire_at / cell_at
AnalysisContext actx(module, subpool);
SigConnKinds conn_kinds(clean_ctx.flags.purge, actx, clean_ctx);
ExactCellWires cell_wires(conn_kinds.exact_cells, actx.assign_map);
// Collect sigmap representative candidates as built in parallel
// With parallel runs, this creates redundant candidates that have to resolve in update_assign_map
ShardedVector<RTLIL::SigBit> new_sigmap_rep_candidates = build_candidates(cell_wires, conn_kinds, actx);
// Cache all the cell_wires results that we might possible need. This avoids the results
// changing when we update `assign_map` below.
cell_wires.cache_all(new_sigmap_rep_candidates);
// Modify assign_map to reflect the connectivity we want, not the one we have
// this changes representative selection in assign_map
update_assign_map(actx.assign_map, new_sigmap_rep_candidates, cell_wires, conn_kinds);
// Remove all wire-wire connections
module->connections_.clear();
UsedSignals used;
DeferredUpdates deferred = analyse_connectivity(used, conn_kinds, actx, clean_ctx);
fixup_cell_ports(deferred.update_connections);
// Rip up and re-apply init attributes onto representative wires with x-bits
// in place of unset init bits
consume_inits(deferred.initialized_wires, actx.assign_map).apply_normalised_inits();
WireDeleter deleter(used, clean_ctx.flags.purge, actx);
used.clear(subpool);
deleter.commit_changes(module);
int deleted_and_unreported = deleter.delete_wires(module, clean_ctx.flags.verbose);
int deleted_total = GetSize(deleter.del_wires_queue);
clean_ctx.stats.count_rm_wires += deleted_total;
if (clean_ctx.flags.verbose && deleted_and_unreported)
log_debug(" removed %d unused temporary wires.\n", deleted_and_unreported);
if (deleted_total)
module->design->scratchpad_set_bool("opt.did_something", true);
return deleted_total != 0;
}
YOSYS_NAMESPACE_END

View file

@ -20,6 +20,7 @@
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/utils.h"
#include "kernel/log.h"
#include <stdlib.h>
@ -31,7 +32,7 @@ PRIVATE_NAMESPACE_BEGIN
bool did_something;
void replace_undriven(RTLIL::Module *module, const CellTypes &ct)
void replace_undriven(RTLIL::Module *module, const NewCellTypes &ct)
{
SigMap sigmap(module);
SigPool driven_signals;
@ -407,9 +408,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
}
CellTypes ct_memcells;
ct_memcells.setup_stdcells_mem();
if (!noclkinv)
for (auto cell : module->cells())
if (design->selected(module, cell)) {
@ -433,7 +431,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (cell->type.in(ID($dffe), ID($adffe), ID($aldffe), ID($sdffe), ID($sdffce), ID($dffsre), ID($dlatch), ID($adlatch), ID($dlatchsr)))
handle_polarity_inv(cell, ID::EN, ID::EN_POLARITY, assign_map, invert_map);
if (!ct_memcells.cell_known(cell->type))
if (!StaticCellTypes::Compat::stdcells_mem(cell->type))
continue;
handle_clkpol_celltype_swap(cell, "$_SR_N?_", "$_SR_P?_", ID::S, assign_map, invert_map);
@ -2294,7 +2292,7 @@ struct OptExprPass : public Pass {
}
extra_args(args, argidx, design);
CellTypes ct(design);
NewCellTypes ct(design);
for (auto module : design->selected_modules())
{
log("Optimizing module %s.\n", log_id(module));

View file

@ -39,7 +39,8 @@ struct OptLutInsPass : public Pass {
log("\n");
log(" -tech <technology>\n");
log(" Instead of generic $lut cells, operate on LUT cells specific\n");
log(" to the given technology. Valid values are: xilinx, lattice, gowin.\n");
log(" to the given technology. Valid values are: xilinx, lattice,\n");
log(" gowin, analogdevices.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
@ -58,7 +59,7 @@ struct OptLutInsPass : public Pass {
}
extra_args(args, argidx, design);
if (techname != "" && techname != "xilinx" && techname != "lattice" && techname != "ecp5" && techname != "gowin")
if (techname != "" && techname != "xilinx" && techname != "lattice" && techname != "analogdevices" && techname != "gowin")
log_cmd_error("Unsupported technology: '%s'\n", techname);
for (auto module : design->selected_modules())
@ -81,7 +82,7 @@ struct OptLutInsPass : public Pass {
inputs = cell->getPort(ID::A);
output = cell->getPort(ID::Y);
lut = cell->getParam(ID::LUT);
} else if (techname == "xilinx" || techname == "gowin") {
} else if (techname == "xilinx" || techname == "gowin" || techname == "analogdevices") {
if (cell->type == ID(LUT1)) {
inputs = {
cell->getPort(ID(I0)),
@ -126,11 +127,11 @@ struct OptLutInsPass : public Pass {
continue;
}
lut = cell->getParam(ID::INIT);
if (techname == "xilinx")
if (techname == "xilinx" || techname == "analogdevices")
output = cell->getPort(ID::O);
else
output = cell->getPort(ID::F);
} else if (techname == "lattice" || techname == "ecp5") {
} else if (techname == "lattice") {
if (cell->type == ID(LUT4)) {
inputs = {
cell->getPort(ID::A),
@ -236,7 +237,7 @@ struct OptLutInsPass : public Pass {
} else {
// xilinx, gowin
cell->setParam(ID::INIT, new_lut);
if (techname == "xilinx")
if (techname == "xilinx" || techname == "analogdevices")
log_assert(GetSize(new_inputs) <= 6);
else
log_assert(GetSize(new_inputs) <= 4);

View file

@ -23,6 +23,7 @@
#include "kernel/modtools.h"
#include "kernel/utils.h"
#include "kernel/macc.h"
#include "kernel/newcelltypes.h"
#include <iterator>
USING_YOSYS_NAMESPACE
@ -38,19 +39,18 @@ struct ShareWorkerConfig
bool opt_force;
bool opt_aggressive;
bool opt_fast;
pool<RTLIL::IdString> generic_uni_ops, generic_bin_ops, generic_cbin_ops, generic_other_ops;
StaticCellTypes::Categories::Category generic_uni_ops, generic_bin_ops, generic_cbin_ops, generic_other_ops;
};
struct ShareWorker
{
const ShareWorkerConfig config;
int limit;
pool<RTLIL::IdString> generic_ops;
StaticCellTypes::Categories::Category generic_ops;
RTLIL::Design *design;
RTLIL::Module *module;
CellTypes fwd_ct, cone_ct;
ModWalker modwalker;
pool<RTLIL::Cell*> cells_to_remove;
@ -75,7 +75,7 @@ struct ShareWorker
queue_bits.insert(modwalker.signal_outputs.begin(), modwalker.signal_outputs.end());
for (auto &it : module->cells_)
if (!fwd_ct.cell_known(it.second->type)) {
if (!StaticCellTypes::Compat::internals_nomem_noff(it.second->type)) {
pool<RTLIL::SigBit> &bits = modwalker.cell_inputs[it.second];
queue_bits.insert(bits.begin(), bits.end());
}
@ -95,7 +95,7 @@ struct ShareWorker
queue_bits.insert(bits.begin(), bits.end());
visited_cells.insert(pbit.cell);
}
if (fwd_ct.cell_known(pbit.cell->type) && visited_cells.count(pbit.cell) == 0) {
if (StaticCellTypes::Compat::internals_nomem_noff(pbit.cell->type) && visited_cells.count(pbit.cell) == 0) {
pool<RTLIL::SigBit> &bits = modwalker.cell_inputs[pbit.cell];
terminal_bits.insert(bits.begin(), bits.end());
queue_bits.insert(bits.begin(), bits.end());
@ -388,7 +388,7 @@ struct ShareWorker
continue;
}
if (generic_ops.count(cell->type)) {
if (generic_ops(cell->type)) {
if (config.opt_aggressive)
shareable_cells.insert(cell);
continue;
@ -412,7 +412,7 @@ struct ShareWorker
return true;
}
if (config.generic_uni_ops.count(c1->type))
if (config.generic_uni_ops(c1->type))
{
if (!config.opt_aggressive)
{
@ -429,7 +429,7 @@ struct ShareWorker
return true;
}
if (config.generic_bin_ops.count(c1->type) || c1->type == ID($alu))
if (config.generic_bin_ops(c1->type) || c1->type == ID($alu))
{
if (!config.opt_aggressive)
{
@ -449,7 +449,7 @@ struct ShareWorker
return true;
}
if (config.generic_cbin_ops.count(c1->type))
if (config.generic_cbin_ops(c1->type))
{
if (!config.opt_aggressive)
{
@ -511,7 +511,7 @@ struct ShareWorker
{
log_assert(c1->type == c2->type);
if (config.generic_uni_ops.count(c1->type))
if (config.generic_uni_ops(c1->type))
{
if (c1->parameters.at(ID::A_SIGNED).as_bool() != c2->parameters.at(ID::A_SIGNED).as_bool())
{
@ -560,11 +560,11 @@ struct ShareWorker
return supercell;
}
if (config.generic_bin_ops.count(c1->type) || config.generic_cbin_ops.count(c1->type) || c1->type == ID($alu))
if (config.generic_bin_ops(c1->type) || config.generic_cbin_ops(c1->type) || c1->type == ID($alu))
{
bool modified_src_cells = false;
if (config.generic_cbin_ops.count(c1->type))
if (config.generic_cbin_ops(c1->type))
{
int score_unflipped = max(c1->parameters.at(ID::A_WIDTH).as_int(), c2->parameters.at(ID::A_WIDTH).as_int()) +
max(c1->parameters.at(ID::B_WIDTH).as_int(), c2->parameters.at(ID::B_WIDTH).as_int());
@ -758,7 +758,7 @@ struct ShareWorker
recursion_state.insert(cell);
for (auto c : consumer_cells)
if (fwd_ct.cell_known(c->type)) {
if (StaticCellTypes::Compat::internals_nomem_noff(c->type)) {
const pool<RTLIL::SigBit> &bits = find_forbidden_controls(c);
forbidden_controls_cache[cell].insert(bits.begin(), bits.end());
}
@ -897,7 +897,7 @@ struct ShareWorker
return activation_patterns_cache.at(cell);
}
for (auto &pbit : modwalker.signal_consumers[bit]) {
log_assert(fwd_ct.cell_known(pbit.cell->type));
log_assert(StaticCellTypes::Compat::internals_nomem_noff(pbit.cell->type));
if ((pbit.cell->type == ID($mux) || pbit.cell->type == ID($pmux)) && (pbit.port == ID::A || pbit.port == ID::B))
driven_data_muxes.insert(pbit.cell);
else
@ -1214,24 +1214,10 @@ struct ShareWorker
ShareWorker(ShareWorkerConfig config, RTLIL::Design* design) :
config(config), design(design), modwalker(design)
{
generic_ops.insert(config.generic_uni_ops.begin(), config.generic_uni_ops.end());
generic_ops.insert(config.generic_bin_ops.begin(), config.generic_bin_ops.end());
generic_ops.insert(config.generic_cbin_ops.begin(), config.generic_cbin_ops.end());
generic_ops.insert(config.generic_other_ops.begin(), config.generic_other_ops.end());
fwd_ct.setup_internals();
cone_ct.setup_internals();
cone_ct.cell_types.erase(ID($mul));
cone_ct.cell_types.erase(ID($mod));
cone_ct.cell_types.erase(ID($div));
cone_ct.cell_types.erase(ID($modfloor));
cone_ct.cell_types.erase(ID($divfloor));
cone_ct.cell_types.erase(ID($pow));
cone_ct.cell_types.erase(ID($shl));
cone_ct.cell_types.erase(ID($shr));
cone_ct.cell_types.erase(ID($sshl));
cone_ct.cell_types.erase(ID($sshr));
generic_ops = StaticCellTypes::Categories::join(generic_ops, config.generic_uni_ops);
generic_ops = StaticCellTypes::Categories::join(generic_ops, config.generic_bin_ops);
generic_ops = StaticCellTypes::Categories::join(generic_ops, config.generic_cbin_ops);
generic_ops = StaticCellTypes::Categories::join(generic_ops, config.generic_other_ops);
}
void operator()(RTLIL::Module *module) {
@ -1561,45 +1547,45 @@ struct SharePass : public Pass {
config.opt_aggressive = false;
config.opt_fast = false;
config.generic_uni_ops.insert(ID($not));
// config.generic_uni_ops.insert(ID($pos));
config.generic_uni_ops.insert(ID($neg));
config.generic_uni_ops.set_id(ID($not));
// config.generic_uni_ops.set_id(ID($pos));
config.generic_uni_ops.set_id(ID($neg));
config.generic_cbin_ops.insert(ID($and));
config.generic_cbin_ops.insert(ID($or));
config.generic_cbin_ops.insert(ID($xor));
config.generic_cbin_ops.insert(ID($xnor));
config.generic_cbin_ops.set_id(ID($and));
config.generic_cbin_ops.set_id(ID($or));
config.generic_cbin_ops.set_id(ID($xor));
config.generic_cbin_ops.set_id(ID($xnor));
config.generic_bin_ops.insert(ID($shl));
config.generic_bin_ops.insert(ID($shr));
config.generic_bin_ops.insert(ID($sshl));
config.generic_bin_ops.insert(ID($sshr));
config.generic_bin_ops.set_id(ID($shl));
config.generic_bin_ops.set_id(ID($shr));
config.generic_bin_ops.set_id(ID($sshl));
config.generic_bin_ops.set_id(ID($sshr));
config.generic_bin_ops.insert(ID($lt));
config.generic_bin_ops.insert(ID($le));
config.generic_bin_ops.insert(ID($eq));
config.generic_bin_ops.insert(ID($ne));
config.generic_bin_ops.insert(ID($eqx));
config.generic_bin_ops.insert(ID($nex));
config.generic_bin_ops.insert(ID($ge));
config.generic_bin_ops.insert(ID($gt));
config.generic_bin_ops.set_id(ID($lt));
config.generic_bin_ops.set_id(ID($le));
config.generic_bin_ops.set_id(ID($eq));
config.generic_bin_ops.set_id(ID($ne));
config.generic_bin_ops.set_id(ID($eqx));
config.generic_bin_ops.set_id(ID($nex));
config.generic_bin_ops.set_id(ID($ge));
config.generic_bin_ops.set_id(ID($gt));
config.generic_cbin_ops.insert(ID($add));
config.generic_cbin_ops.insert(ID($mul));
config.generic_cbin_ops.set_id(ID($add));
config.generic_cbin_ops.set_id(ID($mul));
config.generic_bin_ops.insert(ID($sub));
config.generic_bin_ops.insert(ID($div));
config.generic_bin_ops.insert(ID($mod));
config.generic_bin_ops.insert(ID($divfloor));
config.generic_bin_ops.insert(ID($modfloor));
// config.generic_bin_ops.insert(ID($pow));
config.generic_bin_ops.set_id(ID($sub));
config.generic_bin_ops.set_id(ID($div));
config.generic_bin_ops.set_id(ID($mod));
config.generic_bin_ops.set_id(ID($divfloor));
config.generic_bin_ops.set_id(ID($modfloor));
// config.generic_bin_ops.set_id(ID($pow));
config.generic_uni_ops.insert(ID($logic_not));
config.generic_cbin_ops.insert(ID($logic_and));
config.generic_cbin_ops.insert(ID($logic_or));
config.generic_uni_ops.set_id(ID($logic_not));
config.generic_cbin_ops.set_id(ID($logic_and));
config.generic_cbin_ops.set_id(ID($logic_or));
config.generic_other_ops.insert(ID($alu));
config.generic_other_ops.insert(ID($macc));
config.generic_other_ops.set_id(ID($alu));
config.generic_other_ops.set_id(ID($macc));
log_header(design, "Executing SHARE pass (SAT-based resource sharing).\n");

View file

@ -20,6 +20,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/mem.h"
#include "kernel/fstdata.h"
#include "kernel/ff.h"
@ -215,7 +216,13 @@ struct SimInstance
std::vector<Mem> memories;
dict<Wire*, pair<int, Const>> signal_database;
struct signal_entry_t {
int id;
Const last_value;
SigSpec mapped_sig;
};
dict<Wire*, signal_entry_t> signal_database;
dict<IdString, std::map<int, pair<int, Const>>> trace_mem_database;
dict<std::pair<IdString, int>, Const> trace_mem_init_database;
dict<Wire*, fstHandle> fst_handles;
@ -412,11 +419,11 @@ struct SimInstance
return result;
}
Const get_state(SigSpec sig)
Const get_state_mapped(const SigSpec &mapped_sig)
{
Const::Builder builder(GetSize(sig));
Const::Builder builder(GetSize(mapped_sig));
for (auto bit : sigmap(sig))
for (auto bit : mapped_sig)
if (bit.wire == nullptr)
builder.push_back(bit.data);
else if (state_nets.count(bit))
@ -424,7 +431,12 @@ struct SimInstance
else
builder.push_back(State::Sz);
Const value = builder.build();
return builder.build();
}
Const get_state(SigSpec sig)
{
Const value = get_state_mapped(sigmap(sig));
if (shared->debug)
log("[%s] get %s: %s\n", hiername(), log_signal(sig), log_signal(value));
return value;
@ -990,7 +1002,7 @@ struct SimInstance
if (shared->hide_internal && wire->name[0] == '$')
continue;
signal_database[wire] = make_pair(id, Const());
signal_database[wire] = {id, Const(), sigmap(wire)};
id++;
}
@ -1031,11 +1043,11 @@ struct SimInstance
hdlname.pop_back();
for (auto name : hdlname)
enter_scope("\\" + name);
register_signal(signal_name.c_str(), GetSize(signal.first), signal.first, signal.second.first, registers.count(signal.first)!=0);
register_signal(signal_name.c_str(), GetSize(signal.first), signal.first, signal.second.id, registers.count(signal.first)!=0);
for (auto name : hdlname)
exit_scope();
} else
register_signal(log_id(signal.first->name), GetSize(signal.first), signal.first, signal.second.first, registers.count(signal.first)!=0);
register_signal(log_id(signal.first->name), GetSize(signal.first), signal.first, signal.second.id, registers.count(signal.first)!=0);
}
for (auto &trace_mem : trace_mem_database)
@ -1107,15 +1119,14 @@ struct SimInstance
{
for (auto &it : signal_database)
{
Wire *wire = it.first;
Const value = get_state(wire);
int id = it.second.first;
signal_entry_t &entry = it.second;
Const value = get_state_mapped(entry.mapped_sig);
if (it.second.second == value)
if (entry.last_value == value)
continue;
it.second.second = value;
data->emplace(id, value);
entry.last_value = value;
data->emplace(entry.id, value);
}
for (auto &trace_mem : trace_mem_database)
@ -1234,6 +1245,10 @@ struct SimInstance
bool checkSignals()
{
// No checks performed when using stimulus
if (shared->sim_mode == SimulationMode::sim)
return false;
bool retVal = false;
for(auto &item : fst_handles) {
if (item.second==0) continue; // Ignore signals not found
@ -1243,9 +1258,7 @@ struct SimInstance
log_warning("Signal '%s.%s' size is different in gold and gate.\n", scope, log_id(item.first));
continue;
}
if (shared->sim_mode == SimulationMode::sim) {
// No checks performed when using stimulus
} else if (shared->sim_mode == SimulationMode::gate && !fst_val.is_fully_def()) { // FST data contains X
if (shared->sim_mode == SimulationMode::gate && !fst_val.is_fully_def()) { // FST data contains X
for(int i=0;i<fst_val.size();i++) {
if (fst_val[i]!=State::Sx && fst_val[i]!=sim_val[i]) {
log_warning("Signal '%s.%s' in file %s in simulation %s\n", scope, log_id(item.first), log_signal(fst_val), log_signal(sim_val));

View file

@ -43,7 +43,7 @@
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/ffinit.h"
#include "kernel/ff.h"
#include "kernel/cost.h"
@ -129,7 +129,6 @@ struct AbcConfig
std::string delay_target;
std::string sop_inputs;
std::string sop_products;
std::string lutin_shared;
std::vector<std::string> dont_use_cells;
bool cleanup = true;
bool keepff = false;
@ -1056,7 +1055,7 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module
all_luts_cost_same = false;
abc_script += config.fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT;
if (all_luts_cost_same && !config.fast_mode)
abc_script += "; lutpack {S}";
abc_script += "; lutpack -S 1";
} else if (!config.liberty_files.empty() || !config.genlib_files.empty())
abc_script += config.constr_file.empty() ?
(config.fast_mode ? ABC_FAST_COMMAND_LIB : ABC_COMMAND_LIB) : (config.fast_mode ? ABC_FAST_COMMAND_CTR : ABC_COMMAND_CTR);
@ -1078,8 +1077,6 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module
for (size_t pos = abc_script.find("{P}"); pos != std::string::npos; pos = abc_script.find("{P}", pos))
abc_script = abc_script.substr(0, pos) + config.sop_products + abc_script.substr(pos+3);
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.per_run_tempdir_name);
abc_script += stringf("; write_blif %s/output.blif", run_abc.per_run_tempdir_name);
@ -1879,7 +1876,7 @@ struct AbcPass : public Pass {
log("%s\n", fold_abc_cmd(ABC_COMMAND_CTR));
log("\n");
log(" for -lut/-luts (only one LUT size):\n");
log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT "; lutpack {S}"));
log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT "; lutpack -S 1"));
log("\n");
log(" for -lut/-luts (different LUT sizes):\n");
log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT));
@ -1947,10 +1944,6 @@ struct AbcPass : public Pass {
log(" maximum number of SOP products.\n");
log(" (replaces {P} in the default scripts above)\n");
log("\n");
log(" -S <num>\n");
log(" maximum number of LUT inputs shared.\n");
log(" (replaces {S} in the default scripts above, default: -S 1)\n");
log("\n");
log(" -lut <width>\n");
log(" generate netlist using luts of (max) the specified width.\n");
log("\n");
@ -2061,11 +2054,6 @@ struct AbcPass : public Pass {
if (design->scratchpad.count("abc.P")) {
config.sop_products = "-P " + design->scratchpad_get_string("abc.P");
}
if (design->scratchpad.count("abc.S")) {
config.lutin_shared = "-S " + design->scratchpad_get_string("abc.S");
} else {
config.lutin_shared = "-S 1";
}
lut_arg = design->scratchpad_get_string("abc.lut", lut_arg);
luts_arg = design->scratchpad_get_string("abc.luts", luts_arg);
config.sop_mode = design->scratchpad_get_bool("abc.sop", false);
@ -2148,10 +2136,6 @@ struct AbcPass : public Pass {
config.sop_products = "-P " + args[++argidx];
continue;
}
if (arg == "-S" && argidx+1 < args.size()) {
config.lutin_shared = "-S " + args[++argidx];
continue;
}
if (arg == "-lut" && argidx+1 < args.size()) {
lut_arg = args[++argidx];
continue;
@ -2455,7 +2439,7 @@ struct AbcPass : public Pass {
continue;
}
CellTypes ct(design);
NewCellTypes ct(design);
std::vector<RTLIL::Cell*> all_cells = mod->selected_cells();
pool<RTLIL::Cell*> unassigned_cells(all_cells.begin(), all_cells.end());

View file

@ -38,53 +38,53 @@ struct Abc9Pass : public ScriptPass
Abc9Pass() : ScriptPass("abc9", "use ABC9 for technology mapping") { }
void on_register() override
{
RTLIL::constpad["abc9.script.default"] = "+&scorr; &sweep; &dc2; &dch -f -r; &ps; &if {C} {W} {D} {R} -v; &mfs";
RTLIL::constpad["abc9.script.default.area"] = "+&scorr; &sweep; &dc2; &dch -f -r; &ps; &if {C} {W} {D} {R} -a -v; &mfs";
RTLIL::constpad["abc9.script.default.fast"] = "+&if {C} {W} {D} {R} -v";
RTLIL::constpad["abc9.script.default"] = "+&scorr; &sweep; &dc2; &dch -f -r; &ps; &if {W} {D} {R} -v; &mfs";
RTLIL::constpad["abc9.script.default.area"] = "+&scorr; &sweep; &dc2; &dch -f -r; &ps; &if {W} {D} {R} -a -v; &mfs";
RTLIL::constpad["abc9.script.default.fast"] = "+&if {W} {D} {R} -v";
// Based on ABC's &flow
RTLIL::constpad["abc9.script.flow"] = "+&scorr; &sweep;" \
"&dch -C 500;" \
/* Round 1 */ \
/* Map 1 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
/* Map 1 */ "&unmap; &if {W} {D} {R} -v; &save; &load; &mfs;" \
"&st; &dsdb;" \
/* Map 2 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
/* Map 2 */ "&unmap; &if {W} {D} {R} -v; &save; &load; &mfs;" \
"&st; &syn2 -m -R 10; &dsdb;" \
"&blut -a -K 6;" \
/* Map 3 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
/* Map 3 */ "&unmap; &if {W} {D} {R} -v; &save; &load; &mfs;" \
/* Round 2 */ \
"&st; &sopb;" \
/* Map 1 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
/* Map 1 */ "&unmap; &if {W} {D} {R} -v; &save; &load; &mfs;" \
"&st; &dsdb;" \
/* Map 2 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
/* Map 2 */ "&unmap; &if {W} {D} {R} -v; &save; &load; &mfs;" \
"&st; &syn2 -m -R 10; &dsdb;" \
"&blut -a -K 6;" \
/* Map 3 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
/* Map 3 */ "&unmap; &if {W} {D} {R} -v; &save; &load; &mfs;" \
/* Round 3 */ \
/* Map 1 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
/* Map 1 */ "&unmap; &if {W} {D} {R} -v; &save; &load; &mfs;" \
"&st; &dsdb;" \
/* Map 2 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
/* Map 2 */ "&unmap; &if {W} {D} {R} -v; &save; &load; &mfs;" \
"&st; &syn2 -m -R 10; &dsdb;" \
"&blut -a -K 6;" \
/* Map 3 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;";
/* Map 3 */ "&unmap; &if {W} {D} {R} -v; &save; &load; &mfs;";
// Based on ABC's &flow2
RTLIL::constpad["abc9.script.flow2"] = "+&scorr; &sweep;" \
/* Comm1 */ "&synch2 -K 6 -C 500; &if -m {C} {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save;"\
/* Comm2 */ "&dch -C 500; &if -m {C} {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save;"\
/* Comm1 */ "&synch2 -K 6 -C 500; &if -m {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save;"\
/* Comm2 */ "&dch -C 500; &if -m {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save;"\
"&load; &st; &sopb -R 10 -C 4; " \
/* Comm3 */ "&synch2 -K 6 -C 500; &if -m "/*"-E 5"*/" {C} {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save;"\
/* Comm2 */ "&dch -C 500; &if -m {C} {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save; "\
/* Comm3 */ "&synch2 -K 6 -C 500; &if -m "/*"-E 5"*/" {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save;"\
/* Comm2 */ "&dch -C 500; &if -m {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save; "\
"&load";
// Based on ABC's &flow3 -m
RTLIL::constpad["abc9.script.flow3"] = "+&scorr; &sweep;" \
"&if {C} {W} {D}; &save; &st; &syn2; &if {C} {W} {D} {R} -v; &save; &load;"\
"&st; &if {C} -g -K 6; &dch -f; &if {C} {W} {D} {R} -v; &save; &load;"\
"&st; &if {C} -g -K 6; &synch2; &if {C} {W} {D} {R} -v; &save; &load;"\
"&if {W} {D}; &save; &st; &syn2; &if {W} {D} {R} -v; &save; &load;"\
"&st; &if -g -K 6; &dch -f; &if {W} {D} {R} -v; &save; &load;"\
"&st; &if -g -K 6; &synch2; &if {W} {D} {R} -v; &save; &load;"\
"&mfs";
// As above, but with &mfs calls as in the original &flow3
RTLIL::constpad["abc9.script.flow3mfs"] = "+&scorr; &sweep;" \
"&if {C} {W} {D}; &save; &st; &syn2; &if {C} {W} {D} {R} -v; &save; &load;"\
"&st; &if {C} -g -K 6; &dch -f; &if {C} {W} {D} {R} -v; &mfs; &save; &load;"\
"&st; &if {C} -g -K 6; &synch2; &if {C} {W} {D} {R} -v; &mfs; &save; &load;"\
"&if {W} {D}; &save; &st; &syn2; &if {W} {D} {R} -v; &save; &load;"\
"&st; &if -g -K 6; &dch -f; &if {W} {D} {R} -v; &mfs; &save; &load;"\
"&st; &if -g -K 6; &synch2; &if {W} {D} {R} -v; &mfs; &save; &load;"\
"&mfs";
}
void help() override
@ -121,20 +121,11 @@ struct Abc9Pass : public ScriptPass
log(" if no -script parameter is given, the following scripts are used:\n");
log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos)));
log("\n");
log(" -fast\n");
log(" use different default scripts that are slightly faster (at the cost\n");
log(" of output quality):\n");
log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)));
log("\n");
log(" -D <picoseconds>\n");
log(" set delay target. the string {D} in the default scripts above is\n");
log(" replaced by this option when used, and an empty string otherwise\n");
log(" (indicating best possible delay).\n");
log("\n");
// log(" -S <num>\n");
// log(" maximum number of LUT inputs shared.\n");
// log(" (replaces {S} in the default scripts above, default: -S 1)\n");
// log("\n");
log(" -lut <width>\n");
log(" generate netlist using luts of (max) the specified width.\n");
log("\n");
@ -226,8 +217,7 @@ struct Abc9Pass : public ScriptPass
exe_cmd << " " << arg << " " << args[++argidx];
continue;
}
if (arg == "-fast" || /* arg == "-dff" || */
/* arg == "-nocleanup" || */ arg == "-showtmp") {
if (arg == "-showtmp") {
exe_cmd << " " << arg;
continue;
}

View file

@ -165,7 +165,7 @@ struct abc9_output_filter
};
void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe_file,
vector<int> lut_costs, bool dff_mode, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode,
vector<int> lut_costs, bool dff_mode, std::string delay_target,
bool show_tempdir, std::string box_file, std::string lut_file,
std::vector<std::string> liberty_files, std::string wire_delay, std::string tempdir_name,
std::string constr_file, std::vector<std::string> dont_use_cells, std::vector<std::string> genlib_files)
@ -210,26 +210,16 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe
} else
abc9_script += stringf("source %s", script_file);
} else if (!lut_costs.empty() || !lut_file.empty()) {
abc9_script += fast_mode ? RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)
: RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos);
abc9_script += RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos);
} else
log_abort();
for (size_t pos = abc9_script.find("{D}"); pos != std::string::npos; pos = abc9_script.find("{D}", pos))
abc9_script = abc9_script.substr(0, pos) + delay_target + abc9_script.substr(pos+3);
//for (size_t pos = abc9_script.find("{S}"); pos != std::string::npos; pos = abc9_script.find("{S}", pos))
// abc9_script = abc9_script.substr(0, pos) + lutin_shared + abc9_script.substr(pos+3);
for (size_t pos = abc9_script.find("{W}"); pos != std::string::npos; pos = abc9_script.find("{W}", pos))
abc9_script = abc9_script.substr(0, pos) + wire_delay + abc9_script.substr(pos+3);
std::string C;
if (design->scratchpad.count("abc9.if.C"))
C = "-C " + design->scratchpad_get_string("abc9.if.C");
for (size_t pos = abc9_script.find("{C}"); pos != std::string::npos; pos = abc9_script.find("{C}", pos))
abc9_script = abc9_script.substr(0, pos) + C + abc9_script.substr(pos+3);
std::string R;
if (design->scratchpad.count("abc9.if.R"))
R = "-R " + design->scratchpad_get_string("abc9.if.R");
@ -369,11 +359,6 @@ struct Abc9ExePass : public Pass {
log(" if no -script parameter is given, the following scripts are used:\n");
log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos)));
log("\n");
log(" -fast\n");
log(" use different default scripts that are slightly faster (at the cost\n");
log(" of output quality):\n");
log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)));
log("\n");
log(" -constr <file>\n");
log(" pass this file with timing constraints to ABC.\n");
log(" use with -liberty.\n");
@ -453,9 +438,9 @@ struct Abc9ExePass : public Pass {
std::string exe_file = yosys_abc_executable;
std::string script_file, clk_str, box_file, lut_file, constr_file;
std::vector<std::string> liberty_files, genlib_files, dont_use_cells;
std::string delay_target, lutin_shared = "-S 1", wire_delay;
std::string delay_target, wire_delay;
std::string tempdir_name;
bool fast_mode = false, dff_mode = false;
bool dff_mode = false;
bool show_tempdir = false;
vector<int> lut_costs;
@ -472,7 +457,6 @@ struct Abc9ExePass : public Pass {
}
lut_arg = design->scratchpad_get_string("abc9.lut", lut_arg);
luts_arg = design->scratchpad_get_string("abc9.luts", luts_arg);
fast_mode = design->scratchpad_get_bool("abc9.fast", fast_mode);
dff_mode = design->scratchpad_get_bool("abc9.dff", dff_mode);
show_tempdir = design->scratchpad_get_bool("abc9.showtmp", show_tempdir);
box_file = design->scratchpad_get_string("abc9.box", box_file);
@ -504,20 +488,12 @@ struct Abc9ExePass : public Pass {
delay_target = "-D " + args[++argidx];
continue;
}
//if (arg == "-S" && argidx+1 < args.size()) {
// lutin_shared = "-S " + args[++argidx];
// continue;
//}
if (arg == "-lut" && argidx+1 < args.size()) {
lut_arg = args[++argidx];
continue;
}
if (arg == "-luts" && argidx+1 < args.size()) {
lut_arg = args[++argidx];
continue;
}
if (arg == "-fast") {
fast_mode = true;
luts_arg = args[++argidx];
continue;
}
if (arg == "-dff") {
@ -622,7 +598,7 @@ struct Abc9ExePass : public Pass {
log_cmd_error("abc9_exe '-genlib' is incompatible with '-dont_use'.\n");
abc9_module(design, script_file, exe_file, lut_costs, dff_mode,
delay_target, lutin_shared, fast_mode, show_tempdir,
delay_target, show_tempdir,
box_file, lut_file, liberty_files, wire_delay, tempdir_name,
constr_file, dont_use_cells, genlib_files);
}

View file

@ -21,7 +21,7 @@
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/utils.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/timinginfo.h"
#include <optional>
@ -1620,7 +1620,6 @@ clone_lut:
}
}
//log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires);
log("ABC RESULTS: input signals: %8d\n", in_wires);
log("ABC RESULTS: output signals: %8d\n", out_wires);

View file

@ -18,7 +18,7 @@
*/
#include "kernel/register.h"
#include "kernel/rtlil.h"
#include "kernel/yosys_common.h"
#include "kernel/utils.h"
USING_YOSYS_NAMESPACE
@ -27,7 +27,8 @@ PRIVATE_NAMESPACE_BEGIN
std::vector<Module*> order_modules(Design *design, std::vector<Module *> modules)
{
std::set<Module *> modules_set(modules.begin(), modules.end());
TopoSort<Module*> sort;
using Order = IdString::compare_ptr_by_name<RTLIL::NamedObject>;
TopoSort<Module*, Order> sort;
for (auto m : modules) {
sort.node(m);

View file

@ -71,6 +71,8 @@ struct ConstmapPass : public Pass {
}
extra_args(args, argidx, design);
if (celltype.empty())
log_cmd_error("Missing required option -cell.\n");
if (design->has(celltype)) {
Module *existing = design->module(celltype);

View file

@ -0,0 +1,21 @@
OBJS += techlibs/analogdevices/synth_analogdevices.o
$(eval $(call add_share_file,share/analogdevices,techlibs/analogdevices/cells_map.v))
$(eval $(call add_share_file,share/analogdevices,techlibs/analogdevices/cells_sim.v))
$(eval $(call add_share_file,share/analogdevices,techlibs/analogdevices/lutrams.txt))
$(eval $(call add_share_file,share/analogdevices,techlibs/analogdevices/lutrams_map.v))
$(eval $(call add_share_file,share/analogdevices,techlibs/analogdevices/brams_defs.vh))
$(eval $(call add_share_file,share/analogdevices,techlibs/analogdevices/brams.txt))
$(eval $(call add_share_file,share/analogdevices,techlibs/analogdevices/brams_map.v))
$(eval $(call add_share_file,share/analogdevices,techlibs/analogdevices/arith_map.v))
$(eval $(call add_share_file,share/analogdevices,techlibs/analogdevices/ff_map.v))
$(eval $(call add_share_file,share/analogdevices,techlibs/analogdevices/lut_map.v))
$(eval $(call add_share_file,share/analogdevices,techlibs/analogdevices/mux_map.v))
$(eval $(call add_share_file,share/analogdevices,techlibs/analogdevices/dsp_map.v))
$(eval $(call add_share_file,share/analogdevices,techlibs/analogdevices/abc9_model.v))

View file

@ -0,0 +1,39 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* 2019 Eddie Hung <eddie@fpgeh.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.
*
*/
// ============================================================================
// Box containing MUXF7.[AB] + MUXF8,
// Necessary to make these an atomic unit so that
// ABC cannot optimise just one of the MUXF7 away
// and expect to save on its delay
(* abc9_box, lib_whitebox *)
module \$__ANALOGDEVICES_MUXF78 (output O, input I0, I1, I2, I3, S0, S1);
assign O = S1 ? (S0 ? I3 : I2)
: (S0 ? I1 : I0);
specify
(I0 => O) = 294;
(I1 => O) = 297;
(I2 => O) = 311;
(I3 => O) = 317;
(S0 => O) = 390;
(S1 => O) = 273;
endspecify
endmodule

View file

@ -0,0 +1,173 @@
/*
* 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.
*
*/
// ============================================================================
// LCU
(* techmap_celltype = "$lcu" *)
module _80_analogdevices_lcu (P, G, CI, CO);
parameter WIDTH = 2;
(* force_downto *)
input [WIDTH-1:0] P, G;
input CI;
(* force_downto *)
output [WIDTH-1:0] CO;
wire _TECHMAP_FAIL_ = WIDTH <= 2;
genvar i;
generate
localparam CARRY4_COUNT = (WIDTH + 3) / 4;
localparam MAX_WIDTH = CARRY4_COUNT * 4;
localparam PAD_WIDTH = MAX_WIDTH - WIDTH;
(* force_downto *)
wire [MAX_WIDTH-1:0] S = {{PAD_WIDTH{1'b0}}, P & ~G};
(* force_downto *)
wire [MAX_WIDTH-1:0] GG = {{PAD_WIDTH{1'b0}}, G};
(* force_downto *)
wire [MAX_WIDTH-1:0] C;
assign CO = C;
generate for (i = 0; i < CARRY4_COUNT; i = i + 1) begin:slice
if (i == 0) begin
wire INITCO;
CRY4INIT init
(
.CYINIT(CI),
.CO (INITCO)
);
CRY4 carry4
(
.CYINIT(1'd0),
.CI (INITCO),
.DI (GG[i*4 +: 4]),
.S (S [i*4 +: 4]),
.CO (C [i*4 +: 4]),
);
end else begin
CRY4 carry4
(
.CYINIT(1'd0),
.CI (C [i*4 - 1]),
.DI (GG[i*4 +: 4]),
.S (S [i*4 +: 4]),
.CO (C [i*4 +: 4]),
);
end
end endgenerate
endgenerate
endmodule
// ============================================================================
// ALU
(* techmap_celltype = "$alu" *)
module _80_analogdevices_alu (A, B, CI, BI, X, Y, CO);
parameter A_SIGNED = 0;
parameter B_SIGNED = 0;
parameter A_WIDTH = 1;
parameter B_WIDTH = 1;
parameter Y_WIDTH = 1;
parameter _TECHMAP_CONSTVAL_CI_ = 0;
parameter _TECHMAP_CONSTMSK_CI_ = 0;
(* force_downto *)
input [A_WIDTH-1:0] A;
(* force_downto *)
input [B_WIDTH-1:0] B;
(* force_downto *)
output [Y_WIDTH-1:0] X, Y;
input CI, BI;
(* force_downto *)
output [Y_WIDTH-1:0] CO;
wire _TECHMAP_FAIL_ = Y_WIDTH <= 2;
(* force_downto *)
wire [Y_WIDTH-1:0] A_buf, B_buf;
\$pos #(.A_SIGNED(A_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(Y_WIDTH)) A_conv (.A(A), .Y(A_buf));
\$pos #(.A_SIGNED(B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) B_conv (.A(B), .Y(B_buf));
(* force_downto *)
wire [Y_WIDTH-1:0] AA = A_buf;
(* force_downto *)
wire [Y_WIDTH-1:0] BB = BI ? ~B_buf : B_buf;
genvar i;
localparam CARRY4_COUNT = (Y_WIDTH + 3) / 4;
localparam MAX_WIDTH = CARRY4_COUNT * 4;
localparam PAD_WIDTH = MAX_WIDTH - Y_WIDTH;
(* force_downto *)
wire [MAX_WIDTH-1:0] S = {{PAD_WIDTH{1'b0}}, AA ^ BB};
(* force_downto *)
wire [MAX_WIDTH-1:0] DI = {{PAD_WIDTH{1'b0}}, AA};
(* force_downto *)
wire [MAX_WIDTH-1:0] O;
(* force_downto *)
wire [MAX_WIDTH-1:0] C;
assign Y = O, CO = C;
genvar i;
generate for (i = 0; i < CARRY4_COUNT; i = i + 1) begin:slice
if (i == 0) begin
wire INITCO;
CRY4INIT init
(
.CYINIT(CI),
.CO (INITCO)
);
CRY4 carry4
(
.CYINIT(1'd0),
.CI (INITCO),
.DI (DI[i*4 +: 4]),
.S (S [i*4 +: 4]),
.O (O [i*4 +: 4]),
.CO (C [i*4 +: 4])
);
end else begin
CRY4 carry4
(
.CYINIT(1'd0),
.CI (C [i*4 - 1]),
.DI (DI[i*4 +: 4]),
.S (S [i*4 +: 4]),
.O (O [i*4 +: 4]),
.CO (C [i*4 +: 4])
);
end
end endgenerate
assign X = S;
endmodule

View file

@ -0,0 +1,285 @@
ifdef IS_T16FFC {
ram block $__ANALOGDEVICES_BLOCKRAM_FULL_ {
option "ERR" "ECC" {
style "ECC";
option "SIZE" "2048x32" {
abits 11;
width 32;
byte 32;
option "MODE" "TDP" cost 4502;
option "MODE" "SDP" forbid;
option "MODE" "SP" forbid;
}
option "SIZE" "1024x32" {
abits 10;
width 32;
byte 32;
option "MODE" "TDP" forbid;
option "MODE" "SDP" cost 2402;
option "MODE" "SP" forbid;
}
}
option "ERR" "BP" {
style "BP";
option "SIZE" "2048x36" {
abits 11;
width 36;
byte 9;
option "MODE" "TDP" cost 4504;
option "MODE" "SDP" forbid;
option "MODE" "SP" forbid;
}
option "SIZE" "1024x36" {
abits 10;
width 36;
byte 9;
option "MODE" "TDP" forbid;
option "MODE" "SDP" cost 2404;
option "MODE" "SP" forbid;
}
}
option "ERR" "FP" {
style "FP";
option "SIZE" "2048x18" {
abits 11;
width 18;
byte 18;
option "MODE" "TDP" cost 2501;
option "MODE" "SDP" cost 2401;
option "MODE" "SP" forbid;
}
}
option "ERR" "NONE" {
option "SIZE" "8192x05" {
abits 13;
width 5;
byte 1;
option "MODE" "TDP" cost 2505;
option "MODE" "SDP" forbid;
option "MODE" "SP" forbid;
}
option "SIZE" "4096x09" {
abits 12;
width 9;
byte 1;
option "MODE" "TDP" cost 2509;
option "MODE" "SDP" forbid;
option "MODE" "SP" forbid;
}
option "SIZE" "4096x10" {
abits 12;
width 10;
byte 1;
option "MODE" "TDP" forbid;
option "MODE" "SDP" cost 2410;
option "MODE" "SP" forbid;
}
option "SIZE" "2048x20" {
abits 11;
width 20;
byte 1;
option "MODE" "TDP" forbid;
option "MODE" "SDP" forbid;
option "MODE" "SP" cost 2320;
}
option "SIZE" "2048x40" {
abits 11;
width 40;
byte 8;
option "MODE" "TDP" cost 4505;
option "MODE" "SDP" forbid;
option "MODE" "SP" forbid;
}
}
# supports any initialization value, but need to export memory files
init any;
option "MODE" "TDP" {
port srsw "A" {
clock anyedge;
clken;
rdwr no_change;
}
port srsw "B" {
clock anyedge;
clken;
rdwr no_change;
}
}
option "MODE" "SDP" {
port sw "A" {
clock anyedge;
clken;
}
port sr "B" {
clock anyedge;
clken;
}
}
option "MODE" "SP" {
port srsw "A" {
clock anyedge;
clken;
rdwr no_change;
}
}
}
}
ram block $__ANALOGDEVICES_BLOCKRAM_HALF_ {
option "ERR" "ECC" {
style "ECC";
option "SIZE" "1024x32" {
abits 10;
width 32;
byte 32;
option "MODE" "SDP" cost 2402;
option "MODE" "SP" forbid;
option "MODE" "SP2" forbid;
}
option "SIZE" "512x32" {
abits 9;
width 32;
byte 32;
option "MODE" "SDP" forbid;
option "MODE" "SP" cost 2302;
option "MODE" "SP2" forbid;
}
}
option "ERR" "BP" {
style "BP";
option "SIZE" "1024x36" {
abits 10;
width 36;
byte 9;
option "MODE" "SDP" cost 2404;
option "MODE" "SP" forbid;
option "MODE" "SP2" forbid;
}
option "SIZE" "512x36" {
abits 9;
width 36;
byte 9;
option "MODE" "SDP" forbid;
option "MODE" "SP" cost 2304;
option "MODE" "SP2" forbid;
}
}
option "ERR" "FP" {
style "FP";
option "SIZE" "1024x18" {
abits 10;
width 18;
byte 18;
option "MODE" "SDP" forbid;
option "MODE" "SP" forbid;
option "MODE" "SP2" cost 2301;
}
}
option "ERR" "NONE" {
option "SIZE" "4096x05" {
abits 12;
width 5;
byte 1;
option "MODE" "SDP" cost 2405;
option "MODE" "SP" cost 2305;
option "MODE" "SP2" forbid;
}
option "SIZE" "2048x09" {
abits 11;
width 9;
byte 1;
option "MODE" "SDP" cost 2409;
option "MODE" "SP" forbid;
option "MODE" "SP2" cost 2309;
}
option "SIZE" "2048x10" {
abits 11;
width 10;
byte 1;
option "MODE" "SDP" cost 2410;
option "MODE" "SP" forbid;
option "MODE" "SP2" forbid;
}
option "SIZE" "1024x20" {
abits 10;
width 20;
byte 1;
option "MODE" "SDP" forbid;
option "MODE" "SP" cost 2320;
option "MODE" "SP2" forbid;
}
option "SIZE" "1024x40" {
abits 10;
width 40;
byte 8;
option "MODE" "SDP" cost 2405;
option "MODE" "SP" forbid;
option "MODE" "SP2" forbid;
}
}
option "MODE" "SDP" {
ifdef IS_T16FFC forbid;
port sw "A" {
clock anyedge;
clken;
}
port sr "B" {
clock anyedge;
clken;
}
}
option "MODE" "SP" {
ifdef IS_T16FFC forbid;
port srsw "A" {
clock anyedge;
clken;
rdwr no_change;
}
}
option "MODE" "SP2" {
ifdef IS_T40LP forbid;
port srsw "A" {
clock anyedge;
clken;
rdwr no_change;
}
}
}
ifdef IS_T40LP {
ram block $__ANALOGDEVICES_BLOCKRAM_QUARTER_ {
option "ERR" "BP" {
style "BP";
option "SIZE" "512x18" {
abits 9;
width 18;
byte 9;
option "MODE" "SP2" cost 2202;
}
}
option "ERR" "NONE" {
option "SIZE" "2048x05" {
abits 11;
width 5;
byte 1;
option "MODE" "SP2" cost 2205;
}
option "SIZE" "1024x09" {
abits 10;
width 9;
byte 1;
option "MODE" "SP2" cost 2209;
}
}
option "MODE" "SP2" {
port srsw "A" {
clock anyedge;
clken;
rdwr no_change;
}
}
}
}

View file

@ -0,0 +1,561 @@
`define PARAMS_INIT_9 \
.INIT_00(slice_init('h00)), \
.INIT_01(slice_init('h01)), \
.INIT_02(slice_init('h02)), \
.INIT_03(slice_init('h03)), \
.INIT_04(slice_init('h04)), \
.INIT_05(slice_init('h05)), \
.INIT_06(slice_init('h06)), \
.INIT_07(slice_init('h07)), \
.INIT_08(slice_init('h08)), \
.INIT_09(slice_init('h09)), \
.INIT_0A(slice_init('h0a)), \
.INIT_0B(slice_init('h0b)), \
.INIT_0C(slice_init('h0c)), \
.INIT_0D(slice_init('h0d)), \
.INIT_0E(slice_init('h0e)), \
.INIT_0F(slice_init('h0f)), \
.INIT_10(slice_init('h10)), \
.INIT_11(slice_init('h11)), \
.INIT_12(slice_init('h12)), \
.INIT_13(slice_init('h13)), \
.INIT_14(slice_init('h14)), \
.INIT_15(slice_init('h15)), \
.INIT_16(slice_init('h16)), \
.INIT_17(slice_init('h17)), \
.INIT_18(slice_init('h18)), \
.INIT_19(slice_init('h19)), \
.INIT_1A(slice_init('h1a)), \
.INIT_1B(slice_init('h1b)), \
.INIT_1C(slice_init('h1c)), \
.INIT_1D(slice_init('h1d)), \
.INIT_1E(slice_init('h1e)), \
.INIT_1F(slice_init('h1f)),
`define PARAMS_INITP_9 \
.INITP_00(slice_initp('h00)), \
.INITP_01(slice_initp('h01)), \
.INITP_02(slice_initp('h02)), \
.INITP_03(slice_initp('h03)),
`define PARAMS_INIT_18 \
.INIT_00(slice_init('h00)), \
.INIT_01(slice_init('h01)), \
.INIT_02(slice_init('h02)), \
.INIT_03(slice_init('h03)), \
.INIT_04(slice_init('h04)), \
.INIT_05(slice_init('h05)), \
.INIT_06(slice_init('h06)), \
.INIT_07(slice_init('h07)), \
.INIT_08(slice_init('h08)), \
.INIT_09(slice_init('h09)), \
.INIT_0A(slice_init('h0a)), \
.INIT_0B(slice_init('h0b)), \
.INIT_0C(slice_init('h0c)), \
.INIT_0D(slice_init('h0d)), \
.INIT_0E(slice_init('h0e)), \
.INIT_0F(slice_init('h0f)), \
.INIT_10(slice_init('h10)), \
.INIT_11(slice_init('h11)), \
.INIT_12(slice_init('h12)), \
.INIT_13(slice_init('h13)), \
.INIT_14(slice_init('h14)), \
.INIT_15(slice_init('h15)), \
.INIT_16(slice_init('h16)), \
.INIT_17(slice_init('h17)), \
.INIT_18(slice_init('h18)), \
.INIT_19(slice_init('h19)), \
.INIT_1A(slice_init('h1a)), \
.INIT_1B(slice_init('h1b)), \
.INIT_1C(slice_init('h1c)), \
.INIT_1D(slice_init('h1d)), \
.INIT_1E(slice_init('h1e)), \
.INIT_1F(slice_init('h1f)), \
.INIT_20(slice_init('h20)), \
.INIT_21(slice_init('h21)), \
.INIT_22(slice_init('h22)), \
.INIT_23(slice_init('h23)), \
.INIT_24(slice_init('h24)), \
.INIT_25(slice_init('h25)), \
.INIT_26(slice_init('h26)), \
.INIT_27(slice_init('h27)), \
.INIT_28(slice_init('h28)), \
.INIT_29(slice_init('h29)), \
.INIT_2A(slice_init('h2a)), \
.INIT_2B(slice_init('h2b)), \
.INIT_2C(slice_init('h2c)), \
.INIT_2D(slice_init('h2d)), \
.INIT_2E(slice_init('h2e)), \
.INIT_2F(slice_init('h2f)), \
.INIT_30(slice_init('h30)), \
.INIT_31(slice_init('h31)), \
.INIT_32(slice_init('h32)), \
.INIT_33(slice_init('h33)), \
.INIT_34(slice_init('h34)), \
.INIT_35(slice_init('h35)), \
.INIT_36(slice_init('h36)), \
.INIT_37(slice_init('h37)), \
.INIT_38(slice_init('h38)), \
.INIT_39(slice_init('h39)), \
.INIT_3A(slice_init('h3a)), \
.INIT_3B(slice_init('h3b)), \
.INIT_3C(slice_init('h3c)), \
.INIT_3D(slice_init('h3d)), \
.INIT_3E(slice_init('h3e)), \
.INIT_3F(slice_init('h3f)),
`define PARAMS_INIT_18_U \
.INIT_00(slice_init('h40)), \
.INIT_01(slice_init('h41)), \
.INIT_02(slice_init('h42)), \
.INIT_03(slice_init('h43)), \
.INIT_04(slice_init('h44)), \
.INIT_05(slice_init('h45)), \
.INIT_06(slice_init('h46)), \
.INIT_07(slice_init('h47)), \
.INIT_08(slice_init('h48)), \
.INIT_09(slice_init('h49)), \
.INIT_0A(slice_init('h4a)), \
.INIT_0B(slice_init('h4b)), \
.INIT_0C(slice_init('h4c)), \
.INIT_0D(slice_init('h4d)), \
.INIT_0E(slice_init('h4e)), \
.INIT_0F(slice_init('h4f)), \
.INIT_10(slice_init('h50)), \
.INIT_11(slice_init('h51)), \
.INIT_12(slice_init('h52)), \
.INIT_13(slice_init('h53)), \
.INIT_14(slice_init('h54)), \
.INIT_15(slice_init('h55)), \
.INIT_16(slice_init('h56)), \
.INIT_17(slice_init('h57)), \
.INIT_18(slice_init('h58)), \
.INIT_19(slice_init('h59)), \
.INIT_1A(slice_init('h5a)), \
.INIT_1B(slice_init('h5b)), \
.INIT_1C(slice_init('h5c)), \
.INIT_1D(slice_init('h5d)), \
.INIT_1E(slice_init('h5e)), \
.INIT_1F(slice_init('h5f)), \
.INIT_20(slice_init('h60)), \
.INIT_21(slice_init('h61)), \
.INIT_22(slice_init('h62)), \
.INIT_23(slice_init('h63)), \
.INIT_24(slice_init('h64)), \
.INIT_25(slice_init('h65)), \
.INIT_26(slice_init('h66)), \
.INIT_27(slice_init('h67)), \
.INIT_28(slice_init('h68)), \
.INIT_29(slice_init('h69)), \
.INIT_2A(slice_init('h6a)), \
.INIT_2B(slice_init('h6b)), \
.INIT_2C(slice_init('h6c)), \
.INIT_2D(slice_init('h6d)), \
.INIT_2E(slice_init('h6e)), \
.INIT_2F(slice_init('h6f)), \
.INIT_30(slice_init('h70)), \
.INIT_31(slice_init('h71)), \
.INIT_32(slice_init('h72)), \
.INIT_33(slice_init('h73)), \
.INIT_34(slice_init('h74)), \
.INIT_35(slice_init('h75)), \
.INIT_36(slice_init('h76)), \
.INIT_37(slice_init('h77)), \
.INIT_38(slice_init('h78)), \
.INIT_39(slice_init('h79)), \
.INIT_3A(slice_init('h7a)), \
.INIT_3B(slice_init('h7b)), \
.INIT_3C(slice_init('h7c)), \
.INIT_3D(slice_init('h7d)), \
.INIT_3E(slice_init('h7e)), \
.INIT_3F(slice_init('h7f)),
`define PARAMS_INITP_18 \
.INITP_00(slice_initp('h00)), \
.INITP_01(slice_initp('h01)), \
.INITP_02(slice_initp('h02)), \
.INITP_03(slice_initp('h03)), \
.INITP_04(slice_initp('h04)), \
.INITP_05(slice_initp('h05)), \
.INITP_06(slice_initp('h06)), \
.INITP_07(slice_initp('h07)),
`define PARAMS_INIT_36 \
.INIT_00(slice_init('h00)), \
.INIT_01(slice_init('h01)), \
.INIT_02(slice_init('h02)), \
.INIT_03(slice_init('h03)), \
.INIT_04(slice_init('h04)), \
.INIT_05(slice_init('h05)), \
.INIT_06(slice_init('h06)), \
.INIT_07(slice_init('h07)), \
.INIT_08(slice_init('h08)), \
.INIT_09(slice_init('h09)), \
.INIT_0A(slice_init('h0a)), \
.INIT_0B(slice_init('h0b)), \
.INIT_0C(slice_init('h0c)), \
.INIT_0D(slice_init('h0d)), \
.INIT_0E(slice_init('h0e)), \
.INIT_0F(slice_init('h0f)), \
.INIT_10(slice_init('h10)), \
.INIT_11(slice_init('h11)), \
.INIT_12(slice_init('h12)), \
.INIT_13(slice_init('h13)), \
.INIT_14(slice_init('h14)), \
.INIT_15(slice_init('h15)), \
.INIT_16(slice_init('h16)), \
.INIT_17(slice_init('h17)), \
.INIT_18(slice_init('h18)), \
.INIT_19(slice_init('h19)), \
.INIT_1A(slice_init('h1a)), \
.INIT_1B(slice_init('h1b)), \
.INIT_1C(slice_init('h1c)), \
.INIT_1D(slice_init('h1d)), \
.INIT_1E(slice_init('h1e)), \
.INIT_1F(slice_init('h1f)), \
.INIT_20(slice_init('h20)), \
.INIT_21(slice_init('h21)), \
.INIT_22(slice_init('h22)), \
.INIT_23(slice_init('h23)), \
.INIT_24(slice_init('h24)), \
.INIT_25(slice_init('h25)), \
.INIT_26(slice_init('h26)), \
.INIT_27(slice_init('h27)), \
.INIT_28(slice_init('h28)), \
.INIT_29(slice_init('h29)), \
.INIT_2A(slice_init('h2a)), \
.INIT_2B(slice_init('h2b)), \
.INIT_2C(slice_init('h2c)), \
.INIT_2D(slice_init('h2d)), \
.INIT_2E(slice_init('h2e)), \
.INIT_2F(slice_init('h2f)), \
.INIT_30(slice_init('h30)), \
.INIT_31(slice_init('h31)), \
.INIT_32(slice_init('h32)), \
.INIT_33(slice_init('h33)), \
.INIT_34(slice_init('h34)), \
.INIT_35(slice_init('h35)), \
.INIT_36(slice_init('h36)), \
.INIT_37(slice_init('h37)), \
.INIT_38(slice_init('h38)), \
.INIT_39(slice_init('h39)), \
.INIT_3A(slice_init('h3a)), \
.INIT_3B(slice_init('h3b)), \
.INIT_3C(slice_init('h3c)), \
.INIT_3D(slice_init('h3d)), \
.INIT_3E(slice_init('h3e)), \
.INIT_3F(slice_init('h3f)), \
.INIT_40(slice_init('h40)), \
.INIT_41(slice_init('h41)), \
.INIT_42(slice_init('h42)), \
.INIT_43(slice_init('h43)), \
.INIT_44(slice_init('h44)), \
.INIT_45(slice_init('h45)), \
.INIT_46(slice_init('h46)), \
.INIT_47(slice_init('h47)), \
.INIT_48(slice_init('h48)), \
.INIT_49(slice_init('h49)), \
.INIT_4A(slice_init('h4a)), \
.INIT_4B(slice_init('h4b)), \
.INIT_4C(slice_init('h4c)), \
.INIT_4D(slice_init('h4d)), \
.INIT_4E(slice_init('h4e)), \
.INIT_4F(slice_init('h4f)), \
.INIT_50(slice_init('h50)), \
.INIT_51(slice_init('h51)), \
.INIT_52(slice_init('h52)), \
.INIT_53(slice_init('h53)), \
.INIT_54(slice_init('h54)), \
.INIT_55(slice_init('h55)), \
.INIT_56(slice_init('h56)), \
.INIT_57(slice_init('h57)), \
.INIT_58(slice_init('h58)), \
.INIT_59(slice_init('h59)), \
.INIT_5A(slice_init('h5a)), \
.INIT_5B(slice_init('h5b)), \
.INIT_5C(slice_init('h5c)), \
.INIT_5D(slice_init('h5d)), \
.INIT_5E(slice_init('h5e)), \
.INIT_5F(slice_init('h5f)), \
.INIT_60(slice_init('h60)), \
.INIT_61(slice_init('h61)), \
.INIT_62(slice_init('h62)), \
.INIT_63(slice_init('h63)), \
.INIT_64(slice_init('h64)), \
.INIT_65(slice_init('h65)), \
.INIT_66(slice_init('h66)), \
.INIT_67(slice_init('h67)), \
.INIT_68(slice_init('h68)), \
.INIT_69(slice_init('h69)), \
.INIT_6A(slice_init('h6a)), \
.INIT_6B(slice_init('h6b)), \
.INIT_6C(slice_init('h6c)), \
.INIT_6D(slice_init('h6d)), \
.INIT_6E(slice_init('h6e)), \
.INIT_6F(slice_init('h6f)), \
.INIT_70(slice_init('h70)), \
.INIT_71(slice_init('h71)), \
.INIT_72(slice_init('h72)), \
.INIT_73(slice_init('h73)), \
.INIT_74(slice_init('h74)), \
.INIT_75(slice_init('h75)), \
.INIT_76(slice_init('h76)), \
.INIT_77(slice_init('h77)), \
.INIT_78(slice_init('h78)), \
.INIT_79(slice_init('h79)), \
.INIT_7A(slice_init('h7a)), \
.INIT_7B(slice_init('h7b)), \
.INIT_7C(slice_init('h7c)), \
.INIT_7D(slice_init('h7d)), \
.INIT_7E(slice_init('h7e)), \
.INIT_7F(slice_init('h7f)),
`define PARAMS_INIT_36_U \
.INIT_00(slice_init('h80)), \
.INIT_01(slice_init('h81)), \
.INIT_02(slice_init('h82)), \
.INIT_03(slice_init('h83)), \
.INIT_04(slice_init('h84)), \
.INIT_05(slice_init('h85)), \
.INIT_06(slice_init('h86)), \
.INIT_07(slice_init('h87)), \
.INIT_08(slice_init('h88)), \
.INIT_09(slice_init('h89)), \
.INIT_0A(slice_init('h8a)), \
.INIT_0B(slice_init('h8b)), \
.INIT_0C(slice_init('h8c)), \
.INIT_0D(slice_init('h8d)), \
.INIT_0E(slice_init('h8e)), \
.INIT_0F(slice_init('h8f)), \
.INIT_10(slice_init('h90)), \
.INIT_11(slice_init('h91)), \
.INIT_12(slice_init('h92)), \
.INIT_13(slice_init('h93)), \
.INIT_14(slice_init('h94)), \
.INIT_15(slice_init('h95)), \
.INIT_16(slice_init('h96)), \
.INIT_17(slice_init('h97)), \
.INIT_18(slice_init('h98)), \
.INIT_19(slice_init('h99)), \
.INIT_1A(slice_init('h9a)), \
.INIT_1B(slice_init('h9b)), \
.INIT_1C(slice_init('h9c)), \
.INIT_1D(slice_init('h9d)), \
.INIT_1E(slice_init('h9e)), \
.INIT_1F(slice_init('h9f)), \
.INIT_20(slice_init('ha0)), \
.INIT_21(slice_init('ha1)), \
.INIT_22(slice_init('ha2)), \
.INIT_23(slice_init('ha3)), \
.INIT_24(slice_init('ha4)), \
.INIT_25(slice_init('ha5)), \
.INIT_26(slice_init('ha6)), \
.INIT_27(slice_init('ha7)), \
.INIT_28(slice_init('ha8)), \
.INIT_29(slice_init('ha9)), \
.INIT_2A(slice_init('haa)), \
.INIT_2B(slice_init('hab)), \
.INIT_2C(slice_init('hac)), \
.INIT_2D(slice_init('had)), \
.INIT_2E(slice_init('hae)), \
.INIT_2F(slice_init('haf)), \
.INIT_30(slice_init('hb0)), \
.INIT_31(slice_init('hb1)), \
.INIT_32(slice_init('hb2)), \
.INIT_33(slice_init('hb3)), \
.INIT_34(slice_init('hb4)), \
.INIT_35(slice_init('hb5)), \
.INIT_36(slice_init('hb6)), \
.INIT_37(slice_init('hb7)), \
.INIT_38(slice_init('hb8)), \
.INIT_39(slice_init('hb9)), \
.INIT_3A(slice_init('hba)), \
.INIT_3B(slice_init('hbb)), \
.INIT_3C(slice_init('hbc)), \
.INIT_3D(slice_init('hbd)), \
.INIT_3E(slice_init('hbe)), \
.INIT_3F(slice_init('hbf)), \
.INIT_40(slice_init('hc0)), \
.INIT_41(slice_init('hc1)), \
.INIT_42(slice_init('hc2)), \
.INIT_43(slice_init('hc3)), \
.INIT_44(slice_init('hc4)), \
.INIT_45(slice_init('hc5)), \
.INIT_46(slice_init('hc6)), \
.INIT_47(slice_init('hc7)), \
.INIT_48(slice_init('hc8)), \
.INIT_49(slice_init('hc9)), \
.INIT_4A(slice_init('hca)), \
.INIT_4B(slice_init('hcb)), \
.INIT_4C(slice_init('hcc)), \
.INIT_4D(slice_init('hcd)), \
.INIT_4E(slice_init('hce)), \
.INIT_4F(slice_init('hcf)), \
.INIT_50(slice_init('hd0)), \
.INIT_51(slice_init('hd1)), \
.INIT_52(slice_init('hd2)), \
.INIT_53(slice_init('hd3)), \
.INIT_54(slice_init('hd4)), \
.INIT_55(slice_init('hd5)), \
.INIT_56(slice_init('hd6)), \
.INIT_57(slice_init('hd7)), \
.INIT_58(slice_init('hd8)), \
.INIT_59(slice_init('hd9)), \
.INIT_5A(slice_init('hda)), \
.INIT_5B(slice_init('hdb)), \
.INIT_5C(slice_init('hdc)), \
.INIT_5D(slice_init('hdd)), \
.INIT_5E(slice_init('hde)), \
.INIT_5F(slice_init('hdf)), \
.INIT_60(slice_init('he0)), \
.INIT_61(slice_init('he1)), \
.INIT_62(slice_init('he2)), \
.INIT_63(slice_init('he3)), \
.INIT_64(slice_init('he4)), \
.INIT_65(slice_init('he5)), \
.INIT_66(slice_init('he6)), \
.INIT_67(slice_init('he7)), \
.INIT_68(slice_init('he8)), \
.INIT_69(slice_init('he9)), \
.INIT_6A(slice_init('hea)), \
.INIT_6B(slice_init('heb)), \
.INIT_6C(slice_init('hec)), \
.INIT_6D(slice_init('hed)), \
.INIT_6E(slice_init('hee)), \
.INIT_6F(slice_init('hef)), \
.INIT_70(slice_init('hf0)), \
.INIT_71(slice_init('hf1)), \
.INIT_72(slice_init('hf2)), \
.INIT_73(slice_init('hf3)), \
.INIT_74(slice_init('hf4)), \
.INIT_75(slice_init('hf5)), \
.INIT_76(slice_init('hf6)), \
.INIT_77(slice_init('hf7)), \
.INIT_78(slice_init('hf8)), \
.INIT_79(slice_init('hf9)), \
.INIT_7A(slice_init('hfa)), \
.INIT_7B(slice_init('hfb)), \
.INIT_7C(slice_init('hfc)), \
.INIT_7D(slice_init('hfd)), \
.INIT_7E(slice_init('hfe)), \
.INIT_7F(slice_init('hff)),
`define PARAMS_INITP_36 \
.INITP_00(slice_initp('h00)), \
.INITP_01(slice_initp('h01)), \
.INITP_02(slice_initp('h02)), \
.INITP_03(slice_initp('h03)), \
.INITP_04(slice_initp('h04)), \
.INITP_05(slice_initp('h05)), \
.INITP_06(slice_initp('h06)), \
.INITP_07(slice_initp('h07)), \
.INITP_08(slice_initp('h08)), \
.INITP_09(slice_initp('h09)), \
.INITP_0A(slice_initp('h0a)), \
.INITP_0B(slice_initp('h0b)), \
.INITP_0C(slice_initp('h0c)), \
.INITP_0D(slice_initp('h0d)), \
.INITP_0E(slice_initp('h0e)), \
.INITP_0F(slice_initp('h0f)),
`define MAKE_DO(do, dop, rdata) \
wire [63:0] do; \
wire [7:0] dop; \
assign rdata = { \
dop[7], \
do[63:56], \
dop[6], \
do[55:48], \
dop[5], \
do[47:40], \
dop[4], \
do[39:32], \
dop[3], \
do[31:24], \
dop[2], \
do[23:16], \
dop[1], \
do[15:8], \
dop[0], \
do[7:0] \
};
`define MAKE_DI(di, dip, wdata) \
wire [63:0] di; \
wire [7:0] dip; \
assign { \
dip[7], \
di[63:56], \
dip[6], \
di[55:48], \
dip[5], \
di[47:40], \
dip[4], \
di[39:32], \
dip[3], \
di[31:24], \
dip[2], \
di[23:16], \
dip[1], \
di[15:8], \
dip[0], \
di[7:0] \
} = wdata;
function [71:0] ival;
input integer width;
input [71:0] val;
if (width == 72)
ival = {
val[71],
val[62],
val[53],
val[44],
val[35],
val[26],
val[17],
val[8],
val[70:63],
val[61:54],
val[52:45],
val[43:36],
val[34:27],
val[25:18],
val[16:9],
val[7:0]
};
else if (width == 36)
ival = {
val[35],
val[26],
val[17],
val[8],
val[34:27],
val[25:18],
val[16:9],
val[7:0]
};
else if (width == 18)
ival = {
val[17],
val[8],
val[16:9],
val[7:0]
};
else
ival = val;
endfunction
function [255:0] slice_init;
input integer idx;
integer i;
for (i = 0; i < 32; i = i + 1)
slice_init[i*8+:8] = INIT[(idx * 32 + i)*9+:8];
endfunction
function [255:0] slice_initp;
input integer idx;
integer i;
for (i = 0; i < 256; i = i + 1)
slice_initp[i] = INIT[(idx * 256 + i)*9+8];
endfunction

View file

@ -0,0 +1,238 @@
module $__ANALOGDEVICES_BLOCKRAM_FULL_ (...);
// libmap params
parameter INIT = 0;
parameter OPTION_MODE = "NONE";
parameter OPTION_SIZE = "NONE";
parameter OPTION_ERR = "NONE";
parameter PORT_A_WR_EN_WIDTH = 1;
parameter PORT_A_CLK_POL = 1;
parameter PORT_B_WR_EN_WIDTH = PORT_A_WR_EN_WIDTH;
parameter PORT_B_CLK_POL = 1;
// needs -force-params
parameter WIDTH = 40;
parameter ABITS = 13;
// non libmap params
`ifdef IS_T40LP
localparam NODE = "T40LP_Gen2.4";
`endif
`ifdef IS_T16FFC
localparam NODE = "T16FFC_Gen2.4";
`endif
// localparam BRAM_MODE = "SDP_2048x36_BP";
localparam BRAM_MODE = (OPTION_ERR!="NONE") ? {OPTION_MODE, "_", OPTION_SIZE, "_", OPTION_ERR} :
{OPTION_MODE, "_", OPTION_SIZE};
localparam PBITS = (OPTION_ERR=="BP") ? PORT_A_WR_EN_WIDTH : 1;
// libmap ports
input PORT_A_CLK;
input PORT_A_CLK_EN;
input [ABITS-1:0] PORT_A_ADDR;
input [WIDTH-1:0] PORT_A_WR_DATA;
output [WIDTH-1:0] PORT_A_RD_DATA;
input [PORT_A_WR_EN_WIDTH-1:0] PORT_A_WR_EN;
input PORT_B_CLK;
input PORT_B_CLK_EN;
input [ABITS-1:0] PORT_B_ADDR;
input [WIDTH-1:0] PORT_B_WR_DATA;
output [WIDTH-1:0] PORT_B_RD_DATA;
input [PORT_B_WR_EN_WIDTH-1:0] PORT_B_WR_EN;
`ifdef IS_T40LP
RBRAM
`endif
`ifdef IS_T16FFC
RBRAM2
`endif
#(
.TARGET_NODE(NODE),
.BRAM_MODE(BRAM_MODE),
.QA_REG((OPTION_ERR=="ECC") ? 1 : 0),
.QB_REG((OPTION_ERR=="ECC") ? 1 : 0),
.CLKA_INV(!PORT_A_CLK_POL),
.CLKB_INV(!PORT_B_CLK_POL),
.DATA_WIDTH(WIDTH),
.ADDR_WIDTH(ABITS),
.WE_WIDTH(PORT_A_WR_EN_WIDTH),
.PERR_WIDTH(PBITS),
)
_TECHMAP_REPLACE_
(
.QA(PORT_A_RD_DATA),
.DA(PORT_A_WR_DATA),
.CEA(PORT_A_CLK_EN),
.WEA(PORT_A_WR_EN),
.AA(PORT_A_ADDR),
.CLKA(PORT_A_CLK),
.QB(PORT_B_RD_DATA),
.DB(PORT_B_WR_DATA),
.CEB(PORT_B_CLK_EN),
.WEB(PORT_B_WR_EN),
.AB(PORT_B_ADDR),
.CLKB(PORT_B_CLK),
);
// check config
generate
if (PORT_A_WR_EN_WIDTH == PORT_B_WR_EN_WIDTH)
case (BRAM_MODE)
`ifdef IS_T40LP
"SDP_1024x18_FP",
"SDP_1024x16_BP",
"SDP_2048x09",
"SDP_4096x05",
"SDP_1024x32_ECC",
"SDP_1024x40",
"SDP_1024x36_BP",
"SDP_512x32_ECC",
"SDP_512x36_BP",
"SDP_2048x10",
"SP_512x32_ECC",
"SP_512x36_BP",
"SP_1024x20",
"SP2_512x18_BP",
"SP2_1024x09",
"SP2_2048x05": wire _TECHMAP_FAIL_ = 0;
`endif
`ifdef IS_T16FFC
"TDP_2048x18_FP",
"TDP_2048x16_BP",
"TDP_4096x09",
"TDP_8192x05",
"TDP_2048x32_ECC",
"TDP_2048x40",
"TDP_2048x36_BP",
"SDP_2048x18_FP",
"SDP_2048x16_BP",
// The following are rejected in eXpreso
// "SDP_4096x09",
// "SDP_8192x05",
// "SDP_2048x32_ECC",
// "SDP_2048x40",
// "SDP_2048x36_BP",
"SDP_1024x32_ECC",
"SDP_1024x36_BP",
"SDP_4096x10",
"SP_1024x32_ECC",
"SP_1024x36_BP",
"SP_2048x20",
"SP2_1024x18_BP",
"SP2_2048x09",
"SP2_4096x05": wire _TECHMAP_FAIL_ = 0;
`endif
default: wire _TECHMAP_FAIL_ = 1;
endcase
else
wire _TECHMAP_FAIL_ = 1;
endgenerate
endmodule
module $__ANALOGDEVICES_BLOCKRAM_HALF_ (...);
// libmap params
parameter INIT = 0;
parameter OPTION_MODE = "NONE";
parameter OPTION_SIZE = "NONE";
parameter OPTION_ERR = "NONE";
parameter PORT_A_WR_EN_WIDTH = 1;
parameter PORT_A_CLK_POL = 1;
parameter PORT_B_WR_EN_WIDTH = PORT_A_WR_EN_WIDTH;
parameter PORT_B_CLK_POL = 1;
// needs -force-params
parameter WIDTH = 40;
parameter ABITS = 13;
// libmap ports
input PORT_A_CLK;
input PORT_A_CLK_EN;
input [ABITS-1:0] PORT_A_ADDR;
input [WIDTH-1:0] PORT_A_WR_DATA;
output [WIDTH-1:0] PORT_A_RD_DATA;
input [PORT_A_WR_EN_WIDTH-1:0] PORT_A_WR_EN;
input PORT_B_CLK;
input PORT_B_CLK_EN;
input [ABITS-1:0] PORT_B_ADDR;
input [WIDTH-1:0] PORT_B_WR_DATA;
output [WIDTH-1:0] PORT_B_RD_DATA;
input [PORT_B_WR_EN_WIDTH-1:0] PORT_B_WR_EN;
$__ANALOGDEVICES_BLOCKRAM_FULL_
# (
.INIT(INIT),
.OPTION_MODE(OPTION_MODE),
.OPTION_SIZE(OPTION_SIZE),
.OPTION_ERR(OPTION_ERR),
.PORT_A_WR_EN_WIDTH(PORT_A_WR_EN_WIDTH),
.PORT_A_CLK_POL(PORT_A_CLK_POL),
.PORT_B_WR_EN_WIDTH(PORT_B_WR_EN_WIDTH),
.PORT_B_CLK_POL(PORT_B_CLK_POL),
.WIDTH(WIDTH),
.ABITS(ABITS)
)
_TECHMAP_REPLACE_
(
.PORT_A_CLK(PORT_A_CLK),
.PORT_A_CLK_EN(PORT_A_CLK_EN),
.PORT_A_ADDR(PORT_A_ADDR),
.PORT_A_WR_DATA(PORT_A_WR_DATA),
.PORT_A_RD_DATA(PORT_A_RD_DATA),
.PORT_A_WR_EN(PORT_A_WR_EN),
.PORT_B_CLK(PORT_B_CLK),
.PORT_B_CLK_EN(PORT_B_CLK_EN),
.PORT_B_ADDR(PORT_B_ADDR),
.PORT_B_WR_DATA(PORT_B_WR_DATA),
.PORT_B_RD_DATA(PORT_B_RD_DATA),
.PORT_B_WR_EN(PORT_B_WR_EN)
);
endmodule
module $__ANALOGDEVICES_BLOCKRAM_QUARTER_ (...);
// libmap params
parameter INIT = 0;
parameter OPTION_MODE = "NONE";
parameter OPTION_SIZE = "NONE";
parameter OPTION_ERR = "NONE";
parameter PORT_A_WR_EN_WIDTH = 1;
parameter PORT_A_CLK_POL = 1;
parameter PORT_B_WR_EN_WIDTH = PORT_A_WR_EN_WIDTH;
parameter PORT_B_CLK_POL = 1;
// needs -force-params
parameter WIDTH = 40;
parameter ABITS = 13;
// libmap ports
input PORT_A_CLK;
input PORT_A_CLK_EN;
input [ABITS-1:0] PORT_A_ADDR;
input [WIDTH-1:0] PORT_A_WR_DATA;
output [WIDTH-1:0] PORT_A_RD_DATA;
input [PORT_A_WR_EN_WIDTH-1:0] PORT_A_WR_EN;
$__ANALOGDEVICES_BLOCKRAM_FULL_
# (
.INIT(INIT),
.OPTION_MODE(OPTION_MODE),
.OPTION_SIZE(OPTION_SIZE),
.OPTION_ERR(OPTION_ERR),
.PORT_A_WR_EN_WIDTH(PORT_A_WR_EN_WIDTH),
.PORT_A_CLK_POL(PORT_A_CLK_POL),
.PORT_B_WR_EN_WIDTH(PORT_B_WR_EN_WIDTH),
.PORT_B_CLK_POL(PORT_B_CLK_POL),
.WIDTH(WIDTH),
.ABITS(ABITS)
)
_TECHMAP_REPLACE_
(
.PORT_A_CLK(PORT_A_CLK),
.PORT_A_CLK_EN(PORT_A_CLK_EN),
.PORT_A_ADDR(PORT_A_ADDR),
.PORT_A_WR_DATA(PORT_A_WR_DATA),
.PORT_A_RD_DATA(PORT_A_RD_DATA),
.PORT_A_WR_EN(PORT_A_WR_EN),
);
endmodule

View file

@ -0,0 +1,364 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* 2019 Eddie Hung <eddie@fpgeh.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.
*
*/
module \$__SHREG_ (input C, input D, input E, output Q);
parameter DEPTH = 0;
parameter [DEPTH-1:0] INIT = 0;
parameter CLKPOL = 1;
parameter ENPOL = 2;
\$__XILINX_SHREG_ #(.DEPTH(DEPTH), .INIT(INIT), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) _TECHMAP_REPLACE_ (.C(C), .D(D), .L(DEPTH-1), .E(E), .Q(Q));
endmodule
module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, output SO);
parameter DEPTH = 0;
parameter [DEPTH-1:0] INIT = 0;
parameter CLKPOL = 1;
parameter ENPOL = 2;
// shregmap's INIT parameter shifts out LSB first;
// however Analog Devices expects MSB first
function [DEPTH-1:0] brev;
input [DEPTH-1:0] din;
integer i;
begin
for (i = 0; i < DEPTH; i=i+1)
brev[i] = din[DEPTH-1-i];
end
endfunction
localparam [DEPTH-1:0] INIT_R = brev(INIT);
parameter _TECHMAP_CONSTMSK_L_ = 0;
wire CE;
generate
if (ENPOL == 0)
assign CE = ~E;
else if (ENPOL == 1)
assign CE = E;
else
assign CE = 1'b1;
if (DEPTH == 1) begin
if (CLKPOL)
FFRE #(.INIT(INIT_R)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(CE), .R(1'b0));
else
FFRE_N #(.INIT(INIT_R)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(CE), .R(1'b0));
end else
if (DEPTH <= 16) begin
SRL16E #(.INIT(INIT_R), .IS_CLK_INVERTED(~CLKPOL[0])) _TECHMAP_REPLACE_ (.A0(L[0]), .A1(L[1]), .A2(L[2]), .A3(L[3]), .CE(CE), .CLK(C), .D(D), .Q(Q));
end else
if (DEPTH > 17 && DEPTH <= 32) begin
SRLC32E #(.INIT(INIT_R), .IS_CLK_INVERTED(~CLKPOL[0])) _TECHMAP_REPLACE_ (.A(L[4:0]), .CE(CE), .CLK(C), .D(D), .Q(Q));
end else
if (DEPTH > 33 && DEPTH <= 64) begin
wire T0, T1, T2;
SRLC32E #(.INIT(INIT_R[32-1:0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D(D), .Q(T0), .Q31(T1));
\$__XILINX_SHREG_ #(.DEPTH(DEPTH-32), .INIT(INIT[DEPTH-32-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_1 (.C(C), .D(T1), .L(L), .E(E), .Q(T2));
if (&_TECHMAP_CONSTMSK_L_)
assign Q = T2;
else
LUTMUX7 fpga_mux_0 (.O(Q), .I0(T0), .I1(T2), .S(L[5]));
end else
if (DEPTH > 65 && DEPTH <= 96) begin
wire T0, T1, T2, T3, T4, T5, T6;
SRLC32E #(.INIT(INIT_R[32-1: 0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D( D), .Q(T0), .Q31(T1));
SRLC32E #(.INIT(INIT_R[64-1:32]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_1 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T1), .Q(T2), .Q31(T3));
\$__XILINX_SHREG_ #(.DEPTH(DEPTH-64), .INIT(INIT[DEPTH-64-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_2 (.C(C), .D(T3), .L(L[4:0]), .E(E), .Q(T4));
if (&_TECHMAP_CONSTMSK_L_)
assign Q = T4;
else
\$__ANALOGDEVICES_LUTMUX78 fpga_hard_mux (.I0(T0), .I1(T2), .I2(T4), .I3(1'bx), .S0(L[5]), .S1(L[6]), .O(Q));
end else
if (DEPTH > 97 && DEPTH < 128) begin
wire T0, T1, T2, T3, T4, T5, T6, T7, T8;
SRLC32E #(.INIT(INIT_R[32-1: 0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D( D), .Q(T0), .Q31(T1));
SRLC32E #(.INIT(INIT_R[64-1:32]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_1 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T1), .Q(T2), .Q31(T3));
SRLC32E #(.INIT(INIT_R[96-1:64]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_2 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T3), .Q(T4), .Q31(T5));
\$__XILINX_SHREG_ #(.DEPTH(DEPTH-96), .INIT(INIT[DEPTH-96-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_3 (.C(C), .D(T5), .L(L[4:0]), .E(E), .Q(T6));
if (&_TECHMAP_CONSTMSK_L_)
assign Q = T6;
else
\$__ANALOGDEVICES_LUTMUX78 fpga_hard_mux (.I0(T0), .I1(T2), .I2(T4), .I3(T6), .S0(L[5]), .S1(L[6]), .O(Q));
end
else if (DEPTH == 128) begin
wire T0, T1, T2, T3, T4, T5, T6;
SRLC32E #(.INIT(INIT_R[ 32-1: 0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D( D), .Q(T0), .Q31(T1));
SRLC32E #(.INIT(INIT_R[ 64-1:32]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_1 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T1), .Q(T2), .Q31(T3));
SRLC32E #(.INIT(INIT_R[ 96-1:64]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_2 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T3), .Q(T4), .Q31(T5));
SRLC32E #(.INIT(INIT_R[128-1:96]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_3 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T5), .Q(T6), .Q31(SO));
if (&_TECHMAP_CONSTMSK_L_)
assign Q = T6;
else
\$__ANALOGDEVICES_LUTMUX78 fpga_hard_mux (.I0(T0), .I1(T2), .I2(T4), .I3(T6), .S0(L[5]), .S1(L[6]), .O(Q));
end
// For fixed length, if just 1 over a convenient value, decompose
else if (DEPTH <= 129 && &_TECHMAP_CONSTMSK_L_) begin
wire T;
\$__XILINX_SHREG_ #(.DEPTH(DEPTH-1), .INIT(INIT[DEPTH-1:1]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl (.C(C), .D(D), .L({32{1'b1}}), .E(E), .Q(T));
\$__XILINX_SHREG_ #(.DEPTH(1), .INIT(INIT[0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_last (.C(C), .D(T), .L(L), .E(E), .Q(Q));
end
// For variable length, if just 1 over a convenient value, then bump up one more
else if (DEPTH < 129 && ~&_TECHMAP_CONSTMSK_L_)
\$__XILINX_SHREG_ #(.DEPTH(DEPTH+1), .INIT({INIT,1'b0}), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) _TECHMAP_REPLACE_ (.C(C), .D(D), .L(L), .E(E), .Q(Q));
else begin
localparam depth0 = 128;
localparam num_srl128 = DEPTH / depth0;
localparam depthN = DEPTH % depth0;
wire [num_srl128 + (depthN > 0 ? 1 : 0) - 1:0] T;
wire [num_srl128 + (depthN > 0 ? 1 : 0) :0] S;
assign S[0] = D;
genvar i;
for (i = 0; i < num_srl128; i++)
\$__XILINX_SHREG_ #(.DEPTH(depth0), .INIT(INIT[DEPTH-1-i*depth0-:depth0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl (.C(C), .D(S[i]), .L(L[$clog2(depth0)-1:0]), .E(E), .Q(T[i]), .SO(S[i+1]));
if (depthN > 0)
\$__XILINX_SHREG_ #(.DEPTH(depthN), .INIT(INIT[depthN-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_last (.C(C), .D(S[num_srl128]), .L(L[$clog2(depth0)-1:0]), .E(E), .Q(T[num_srl128]));
if (&_TECHMAP_CONSTMSK_L_)
assign Q = T[num_srl128 + (depthN > 0 ? 1 : 0) - 1];
else
assign Q = T[L[DEPTH-1:$clog2(depth0)]];
end
endgenerate
endmodule
`ifdef MIN_MUX_INPUTS
module \$__ANALOGDEVICES_SHIFTX (A, B, Y);
parameter A_SIGNED = 0;
parameter B_SIGNED = 0;
parameter A_WIDTH = 1;
parameter B_WIDTH = 1;
parameter Y_WIDTH = 1;
(* force_downto *)
input [A_WIDTH-1:0] A;
(* force_downto *)
input [B_WIDTH-1:0] B;
(* force_downto *)
output [Y_WIDTH-1:0] Y;
parameter [A_WIDTH-1:0] _TECHMAP_CONSTMSK_A_ = 0;
parameter [A_WIDTH-1:0] _TECHMAP_CONSTVAL_A_ = 0;
parameter [B_WIDTH-1:0] _TECHMAP_CONSTMSK_B_ = 0;
parameter [B_WIDTH-1:0] _TECHMAP_CONSTVAL_B_ = 0;
function integer A_WIDTH_trimmed;
input integer start;
begin
A_WIDTH_trimmed = start;
while (A_WIDTH_trimmed > 0 && _TECHMAP_CONSTMSK_A_[A_WIDTH_trimmed-1] && _TECHMAP_CONSTVAL_A_[A_WIDTH_trimmed-1] === 1'bx)
A_WIDTH_trimmed = A_WIDTH_trimmed - 1;
end
endfunction
generate
genvar i, j;
// Bit-blast
if (Y_WIDTH > 1) begin
for (i = 0; i < Y_WIDTH; i++)
\$__ANALOGDEVICES_SHIFTX #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(A_WIDTH-Y_WIDTH+1), .B_WIDTH(B_WIDTH), .Y_WIDTH(1'd1)) bitblast (.A(A[A_WIDTH-Y_WIDTH+i:i]), .B(B), .Y(Y[i]));
end
// If the LSB of B is constant zero (and Y_WIDTH is 1) then
// we can optimise by removing every other entry from A
// and popping the constant zero from B
else if (_TECHMAP_CONSTMSK_B_[0] && !_TECHMAP_CONSTVAL_B_[0]) begin
wire [(A_WIDTH+1)/2-1:0] A_i;
for (i = 0; i < (A_WIDTH+1)/2; i++)
assign A_i[i] = A[i*2];
\$__ANALOGDEVICES_SHIFTX #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH((A_WIDTH+1'd1)/2'd2), .B_WIDTH(B_WIDTH-1'd1), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(A_i), .B(B[B_WIDTH-1:1]), .Y(Y));
end
// Trim off any leading 1'bx -es in A
else if (_TECHMAP_CONSTMSK_A_[A_WIDTH-1] && _TECHMAP_CONSTVAL_A_[A_WIDTH-1] === 1'bx) begin
localparam A_WIDTH_new = A_WIDTH_trimmed(A_WIDTH-1);
if (A_WIDTH_new == 0)
assign Y = 1'bx;
else
\$__ANALOGDEVICES_SHIFTX #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(A_WIDTH_new), .B_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(A[A_WIDTH_new-1:0]), .B(B), .Y(Y));
end
else if (A_WIDTH < `MIN_MUX_INPUTS) begin
wire _TECHMAP_FAIL_ = 1;
end
else if (A_WIDTH == 2) begin
LUTMUX7 fpga_hard_mux (.I0(A[0]), .I1(A[1]), .S(B[0]), .O(Y));
end
else if (A_WIDTH <= 4) begin
wire [4-1:0] Ax;
if (A_WIDTH == 4)
assign Ax = A;
else
// Rather than extend with 1'bx which gets flattened to 1'b0
// causing the "don't care" status to get lost, extend with
// the same driver of F7B.I0 so that we can optimise F7B away
// later
assign Ax = {A[1], A};
\$__ANALOGDEVICES_LUTMUX78 fpga_hard_mux (.I0(Ax[0]), .I1(Ax[2]), .I2(Ax[1]), .I3(Ax[3]), .S0(B[1]), .S1(B[0]), .O(Y));
end
// Note that the following decompositions are 'backwards' in that
// the LSBs are placed on the hard resources, and the soft resources
// are used for MSBs.
// This has the effect of more effectively utilising the hard mux;
// take for example a 5:1 multiplexer, currently this would map as:
//
// A[0] \___ __ A[0] \__ __
// A[4] / \| \ whereas the more A[1] / \| \
// A[1] _____| | obvious mapping A[2] \___| |
// A[2] _____| |-- of MSBs to hard A[3] / | |__
// A[3]______| | resources would A[4] ____| |
// |__/ lead to: 1'bx ____| |
// || |__/
// || ||
// B[1:0] B[1:2]
//
// Expectation would be that the 'forward' mapping (right) is more
// area efficient (consider a 9:1 multiplexer using 2x4:1 multiplexers
// on its I0 and I1 inputs, and A[8] and 1'bx on its I2 and I3 inputs)
// but that the 'backwards' mapping (left) is more delay efficient
// since smaller LUTs are faster than wider ones.
else if (A_WIDTH <= 8) begin
wire [8-1:0] Ax = {{{8-A_WIDTH}{1'bx}}, A};
wire T0 = B[2] ? Ax[4] : Ax[0];
wire T1 = B[2] ? Ax[5] : Ax[1];
wire T2 = B[2] ? Ax[6] : Ax[2];
wire T3 = B[2] ? Ax[7] : Ax[3];
\$__ANALOGDEVICES_LUTMUX78 fpga_hard_mux (.I0(T0), .I1(T2), .I2(T1), .I3(T3), .S0(B[1]), .S1(B[0]), .O(Y));
end
else if (A_WIDTH <= 16) begin
wire [16-1:0] Ax = {{{16-A_WIDTH}{1'bx}}, A};
wire T0 = B[2] ? B[3] ? Ax[12] : Ax[4]
: B[3] ? Ax[ 8] : Ax[0];
wire T1 = B[2] ? B[3] ? Ax[13] : Ax[5]
: B[3] ? Ax[ 9] : Ax[1];
wire T2 = B[2] ? B[3] ? Ax[14] : Ax[6]
: B[3] ? Ax[10] : Ax[2];
wire T3 = B[2] ? B[3] ? Ax[15] : Ax[7]
: B[3] ? Ax[11] : Ax[3];
\$__ANALOGDEVICES_LUTMUX78 fpga_hard_mux (.I0(T0), .I1(T2), .I2(T1), .I3(T3), .S0(B[1]), .S1(B[0]), .O(Y));
end
else begin
localparam num_mux16 = (A_WIDTH+15) / 16;
localparam clog2_num_mux16 = $clog2(num_mux16);
wire [num_mux16-1:0] T;
wire [num_mux16*16-1:0] Ax = {{(num_mux16*16-A_WIDTH){1'bx}}, A};
for (i = 0; i < num_mux16; i++)
\$__ANALOGDEVICES_SHIFTX #(
.A_SIGNED(A_SIGNED),
.B_SIGNED(B_SIGNED),
.A_WIDTH(16),
.B_WIDTH(4),
.Y_WIDTH(Y_WIDTH)
) fpga_mux (
.A(Ax[i*16+:16]),
.B(B[3:0]),
.Y(T[i])
);
\$__ANALOGDEVICES_SHIFTX #(
.A_SIGNED(A_SIGNED),
.B_SIGNED(B_SIGNED),
.A_WIDTH(num_mux16),
.B_WIDTH(clog2_num_mux16),
.Y_WIDTH(Y_WIDTH)
) _TECHMAP_REPLACE_ (
.A(T),
.B(B[B_WIDTH-1-:clog2_num_mux16]),
.Y(Y));
end
endgenerate
endmodule
(* techmap_celltype = "$__ANALOGDEVICES_SHIFTX" *)
module _90__ANALOGDEVICES_SHIFTX (A, B, Y);
parameter A_SIGNED = 0;
parameter B_SIGNED = 0;
parameter A_WIDTH = 1;
parameter B_WIDTH = 1;
parameter Y_WIDTH = 1;
(* force_downto *)
input [A_WIDTH-1:0] A;
(* force_downto *)
input [B_WIDTH-1:0] B;
(* force_downto *)
output [Y_WIDTH-1:0] Y;
\$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(A_WIDTH), .B_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(A), .B(B), .Y(Y));
endmodule
module \$_MUX_ (A, B, S, Y);
input A, B, S;
output Y;
generate
if (`MIN_MUX_INPUTS == 2)
\$__ANALOGDEVICES_SHIFTX #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(2), .B_WIDTH(1), .Y_WIDTH(1)) _TECHMAP_REPLACE_ (.A({B,A}), .B(S), .Y(Y));
else
wire _TECHMAP_FAIL_ = 1;
endgenerate
endmodule
module \$_MUX4_ (A, B, C, D, S, T, Y);
input A, B, C, D, S, T;
output Y;
\$__ANALOGDEVICES_SHIFTX #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(4), .B_WIDTH(2), .Y_WIDTH(1)) _TECHMAP_REPLACE_ (.A({D,C,B,A}), .B({T,S}), .Y(Y));
endmodule
module \$_MUX8_ (A, B, C, D, E, F, G, H, S, T, U, Y);
input A, B, C, D, E, F, G, H, S, T, U;
output Y;
\$__ANALOGDEVICES_SHIFTX #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(8), .B_WIDTH(3), .Y_WIDTH(1)) _TECHMAP_REPLACE_ (.A({H,G,F,E,D,C,B,A}), .B({U,T,S}), .Y(Y));
endmodule
module \$_MUX16_ (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V, Y);
input A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V;
output Y;
\$__ANALOGDEVICES_SHIFTX #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(16), .B_WIDTH(4), .Y_WIDTH(1)) _TECHMAP_REPLACE_ (.A({P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A}), .B({V,U,T,S}), .Y(Y));
endmodule
`endif
module \$__ANALOGDEVICES_LUTMUX78 (O, I0, I1, I2, I3, S0, S1);
output O;
input I0, I1, I2, I3, S0, S1;
wire T0, T1;
parameter _TECHMAP_BITS_CONNMAP_ = 0;
parameter [_TECHMAP_BITS_CONNMAP_-1:0] _TECHMAP_CONNMAP_I0_ = 0;
parameter [_TECHMAP_BITS_CONNMAP_-1:0] _TECHMAP_CONNMAP_I1_ = 0;
parameter [_TECHMAP_BITS_CONNMAP_-1:0] _TECHMAP_CONNMAP_I2_ = 0;
parameter [_TECHMAP_BITS_CONNMAP_-1:0] _TECHMAP_CONNMAP_I3_ = 0;
parameter _TECHMAP_CONSTMSK_S0_ = 0;
parameter _TECHMAP_CONSTVAL_S0_ = 0;
parameter _TECHMAP_CONSTMSK_S1_ = 0;
parameter _TECHMAP_CONSTVAL_S1_ = 0;
if (_TECHMAP_CONSTMSK_S0_ && _TECHMAP_CONSTVAL_S0_ === 1'b1)
assign T0 = I1;
else if (_TECHMAP_CONSTMSK_S0_ || _TECHMAP_CONNMAP_I0_ === _TECHMAP_CONNMAP_I1_)
assign T0 = I0;
else
LUTMUX7 mux7a (.I0(I0), .I1(I1), .S(S0), .O(T0));
if (_TECHMAP_CONSTMSK_S0_ && _TECHMAP_CONSTVAL_S0_ === 1'b1)
assign T1 = I3;
else if (_TECHMAP_CONSTMSK_S0_ || _TECHMAP_CONNMAP_I2_ === _TECHMAP_CONNMAP_I3_)
assign T1 = I2;
else
LUTMUX7 mux7b (.I0(I2), .I1(I3), .S(S0), .O(T1));
if (_TECHMAP_CONSTMSK_S1_ && _TECHMAP_CONSTVAL_S1_ === 1'b1)
assign O = T1;
else if (_TECHMAP_CONSTMSK_S1_ || (_TECHMAP_CONNMAP_I0_ === _TECHMAP_CONNMAP_I1_ && _TECHMAP_CONNMAP_I1_ === _TECHMAP_CONNMAP_I2_ && _TECHMAP_CONNMAP_I2_ === _TECHMAP_CONNMAP_I3_))
assign O = T0;
else
LUTMUX8 mux8 (.I0(T0), .I1(T1), .S(S1), .O(O));
endmodule

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,60 @@
module \$__MUL22X22 (input [21:0] A, input [21:0] B, output [43:0] Y);
parameter A_SIGNED = 0;
parameter B_SIGNED = 0;
parameter A_WIDTH = 0;
parameter B_WIDTH = 0;
parameter Y_WIDTH = 0;
wire [47:0] P_48;
RBBDSP #(
// Disable all registers
.AI_SEL_IN(1'b0),
.BC_CI(2'b00),
.BI_SEL(1'b0),
.BI_SEL_IN(1'b0),
.CE_A(1'b0),
.CE_ADD(1'b0),
.CE_B(1'b0),
.CE_C(1'b0),
.CE_CRY(1'b0),
.CE_D(2'b0),
.CE_M(1'b0),
.CE_OPCODE(1'b0),
.CE_PADD(1'b0),
.CE_RST(1'b1),
.CE_SEL(1'b0),
.CE_SFT(1'b0),
.CI_SEL(4'd3),
.DI_SEL(1'b0),
.DI_SEL_IN(1'b0),
.OPCODE_SEL(1'b0),
.OP_ADD(10'b0),
.OP_CPLX(1'b0),
.OP_MULT(2'b11),
.OP_PADD(10'b0000000000),
.OP_SFT(6'b000000),
.OP_X(4'b1010),
.OP_Y(4'b0101),
.OP_Z(4'b0000),
.PO_LOC_SEL(1'b1),
.PO_NWK_SEL(1'b1),
.REG_A(1'b0),
.REG_ADD(1'b0),
.REG_B(1'b0),
.REG_C(1'b0),
.REG_CRY(1'b0),
.REG_D(2'b0),
.REG_M(1'b0),
.REG_OPCODE(1'b0),
.REG_PADD(1'b0),
.REG_SFT(1'b0),
.RST_SEL(1'b0),
.FF_SYNC_RST(1'b0),
) _TECHMAP_REPLACE_ (
.P(P_48),
.A(A),
.B(B),
.D(48'b0)
);
assign Y = P_48;
endmodule

View file

@ -0,0 +1,63 @@
/*
* 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.
*
*/
`ifndef _NO_FFS
// Async reset, enable.
module \$_DFFE_NP0P_ (input D, C, E, R, output Q);
FFCE_N #(.INIT(1'b0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .CLR(R));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule
module \$_DFFE_PP0P_ (input D, C, E, R, output Q);
FFCE #(.INIT(1'b0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .CLR(R));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule
module \$_DFFE_NP1P_ (input D, C, E, R, output Q);
FFPE_N #(.INIT(1'b1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .PRE(R));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule
module \$_DFFE_PP1P_ (input D, C, E, R, output Q);
FFPE #(.INIT(1'b1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .PRE(R));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule
// Sync reset, enable.
module \$_SDFFE_NP0P_ (input D, C, E, R, output Q);
FFRE_N #(.INIT(1'b0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(R));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule
module \$_SDFFE_PP0P_ (input D, C, E, R, output Q);
FFRE #(.INIT(1'b0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(R));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule
module \$_SDFFE_NP1P_ (input D, C, E, R, output Q);
FFSE_N #(.INIT(1'b1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .S(R));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule
module \$_SDFFE_PP1P_ (input D, C, E, R, output Q);
FFSE #(.INIT(1'b1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .S(R));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule
`endif

View file

@ -0,0 +1,79 @@
/*
* 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.
*
*/
// ============================================================================
// LUT mapping
`ifndef _NO_LUTS
module \$lut (A, Y);
parameter WIDTH = 0;
parameter LUT = 0;
(* force_downto *)
input [WIDTH-1:0] A;
output Y;
generate
if (WIDTH == 1) begin
LUT1 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
.I0(A[0]));
end else
if (WIDTH == 2) begin
LUT2 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
.I0(A[0]), .I1(A[1]));
end else
if (WIDTH == 3) begin
LUT3 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
.I0(A[0]), .I1(A[1]), .I2(A[2]));
end else
if (WIDTH == 4) begin
LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
.I0(A[0]), .I1(A[1]), .I2(A[2]),
.I3(A[3]));
end else
if (WIDTH == 5) begin
LUT5 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
.I0(A[0]), .I1(A[1]), .I2(A[2]),
.I3(A[3]), .I4(A[4]));
end else
if (WIDTH == 6) begin
LUT6 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
.I0(A[0]), .I1(A[1]), .I2(A[2]),
.I3(A[3]), .I4(A[4]), .I5(A[5]));
end else
if (WIDTH == 7) begin
wire f0, f1;
\$lut #(.LUT(LUT[ 63: 0]), .WIDTH(6)) lut0 (.A(A[5:0]), .Y(f0));
\$lut #(.LUT(LUT[127:64]), .WIDTH(6)) lut1 (.A(A[5:0]), .Y(f1));
LUTMUX7 mux7(.I0(f0), .I1(f1), .S(A[6]), .O(Y));
end else
if (WIDTH == 8) begin
wire f0, f1;
\$lut #(.LUT(LUT[127: 0]), .WIDTH(7)) lut0 (.A(A[6:0]), .Y(f0));
\$lut #(.LUT(LUT[255:128]), .WIDTH(7)) lut1 (.A(A[6:0]), .Y(f1));
LUTMUX8 mux8 (.I0(f0), .I1(f1), .S(A[7]), .O(Y));
end else begin
wire _TECHMAP_FAIL_ = 1;
end
endgenerate
endmodule
`endif

View file

@ -0,0 +1,20 @@
ram distributed $__ANALOGDEVICES_LUTRAM_ {
option "SIZE" 32 abits 5;
option "SIZE" 64 abits 6;
width 1;
init no_undef;
prune_rom;
port arsw "RW" {
clock posedge;
}
option "MODE" "SP" {
option "SIZE" 32 cost 2;
option "SIZE" 64 cost 2;
}
option "MODE" "DP" {
option "SIZE" 32 cost 4;
option "SIZE" 64 cost 8;
port ar "R" {
}
}
}

View file

@ -0,0 +1,139 @@
module $__ANALOGDEVICES_LUTRAM_ (...);
parameter INIT = 0;
parameter OPTION_SIZE = 32;
parameter OPTION_MODE = "SP";
parameter ABITS = 5;
parameter WIDTH = 1;
output PORT_RW_RD_DATA;
input PORT_RW_WR_DATA;
input [ABITS-1:0] PORT_RW_ADDR;
input PORT_RW_WR_EN;
input PORT_RW_CLK;
output PORT_R_RD_DATA;
input [ABITS-1:0] PORT_R_ADDR;
generate
if (OPTION_MODE=="SP")
case(OPTION_SIZE)
32:
RAMS32X1
#(
.INIT(INIT)
)
_TECHMAP_REPLACE_
(
.O(PORT_RW_RD_DATA),
.A0(PORT_RW_ADDR[0]),
.A1(PORT_RW_ADDR[1]),
.A2(PORT_RW_ADDR[2]),
.A3(PORT_RW_ADDR[3]),
.A4(PORT_RW_ADDR[4]),
.D(PORT_RW_WR_DATA),
.WCLK(PORT_RW_CLK),
.WE(PORT_RW_WR_EN)
);
64:
RAMS64X1
#(
.INIT(INIT)
)
_TECHMAP_REPLACE_
(
.O(PORT_RW_RD_DATA),
.A0(PORT_RW_ADDR[0]),
.A1(PORT_RW_ADDR[1]),
.A2(PORT_RW_ADDR[2]),
.A3(PORT_RW_ADDR[3]),
.A4(PORT_RW_ADDR[4]),
.A5(PORT_RW_ADDR[5]),
.D(PORT_RW_WR_DATA),
.WCLK(PORT_RW_CLK),
.WE(PORT_RW_WR_EN)
);
default:
$error("invalid SIZE/MODE combination");
endcase
else if (OPTION_MODE=="DP")
case (OPTION_SIZE)
32:
RAMD32X1
#(
.INIT(INIT)
)
_TECHMAP_REPLACE_
(
.DPO(PORT_R_RD_DATA),
.SPO(PORT_RW_RD_DATA),
.A0(PORT_RW_ADDR[0]),
.A1(PORT_RW_ADDR[1]),
.A2(PORT_RW_ADDR[2]),
.A3(PORT_RW_ADDR[3]),
.A4(PORT_RW_ADDR[4]),
.D(PORT_RW_WR_DATA),
.DPRA0(PORT_R_ADDR[0]),
.DPRA1(PORT_R_ADDR[1]),
.DPRA2(PORT_R_ADDR[2]),
.DPRA3(PORT_R_ADDR[3]),
.DPRA4(PORT_R_ADDR[4]),
.WCLK(PORT_RW_CLK),
.WE(PORT_RW_WR_EN)
);
64:
RAMD64X1
#(
.INIT(INIT)
)
_TECHMAP_REPLACE_
(
.DPO(PORT_R_RD_DATA),
.SPO(PORT_RW_RD_DATA),
.A0(PORT_RW_ADDR[0]),
.A1(PORT_RW_ADDR[1]),
.A2(PORT_RW_ADDR[2]),
.A3(PORT_RW_ADDR[3]),
.A4(PORT_RW_ADDR[4]),
.A5(PORT_RW_ADDR[5]),
.D(PORT_RW_WR_DATA),
.DPRA0(PORT_R_ADDR[0]),
.DPRA1(PORT_R_ADDR[1]),
.DPRA2(PORT_R_ADDR[2]),
.DPRA3(PORT_R_ADDR[3]),
.DPRA4(PORT_R_ADDR[4]),
.DPRA5(PORT_R_ADDR[5]),
.WCLK(PORT_RW_CLK),
.WE(PORT_RW_WR_EN)
);
default:
$error("invalid SIZE/MODE combination");
endcase
else
wire _TECHMAP_FAIL_ = 1;
endgenerate
endmodule
module $__ANALOGDEVICES_LUTRAM_DP_ (...);
parameter INIT = 0;
parameter OPTION_SIZE = 32;
parameter ABITS = 5;
parameter WIDTH = 1;
output PORT_RW_RD_DATA;
input PORT_RW_WR_DATA;
input [ABITS-1:0] PORT_RW_ADDR;
input PORT_RW_WR_EN;
input PORT_RW_CLK;
output PORT_R_RD_DATA;
input [ABITS-1:0] PORT_R_ADDR;
generate
endgenerate
endmodule

View file

@ -0,0 +1,74 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* 2019 Eddie Hung <eddie@fpgeh.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.
*
*/
// The purpose of these mapping rules is to allow preserve all (sufficiently
// wide) $shiftx cells during 'techmap' so that they can be mapped to hard
// resources, rather than being bit-blasted to gates during 'techmap'
// execution
module \$shiftx (A, B, Y);
parameter A_SIGNED = 0;
parameter B_SIGNED = 0;
parameter A_WIDTH = 1;
parameter B_WIDTH = 1;
parameter Y_WIDTH = 1;
(* force_downto *)
input [A_WIDTH-1:0] A;
(* force_downto *)
input [B_WIDTH-1:0] B;
(* force_downto *)
output [Y_WIDTH-1:0] Y;
parameter [B_WIDTH-1:0] _TECHMAP_CONSTMSK_B_ = 0;
parameter [B_WIDTH-1:0] _TECHMAP_CONSTVAL_B_ = 0;
generate
if (B_SIGNED) begin
if (_TECHMAP_CONSTMSK_B_[B_WIDTH-1] && (_TECHMAP_CONSTVAL_B_[B_WIDTH-1] == 1'b0 || _TECHMAP_CONSTVAL_B_[B_WIDTH-1] === 1'bx))
// Optimisation to remove B_SIGNED if sign bit of B is constant-0
\$shiftx #(
.A_SIGNED(A_SIGNED),
.B_SIGNED(0),
.A_WIDTH(A_WIDTH),
.B_WIDTH(B_WIDTH-1'd1),
.Y_WIDTH(Y_WIDTH)
) _TECHMAP_REPLACE_ (
.A(A), .B(B[B_WIDTH-2:0]), .Y(Y)
);
else
wire _TECHMAP_FAIL_ = 1;
end
else begin
if (((A_WIDTH + Y_WIDTH - 1) / Y_WIDTH) < `MIN_MUX_INPUTS)
wire _TECHMAP_FAIL_ = 1;
else
\$__ANALOGDEVICES_SHIFTX #(
.A_SIGNED(A_SIGNED),
.B_SIGNED(B_SIGNED),
.A_WIDTH(A_WIDTH),
.B_WIDTH(B_WIDTH),
.Y_WIDTH(Y_WIDTH)
) _TECHMAP_REPLACE_ (
.A(A), .B(B), .Y(Y)
);
end
endgenerate
endmodule

View file

@ -0,0 +1,49 @@
module FF (input C, D, output Q);
parameter INIT = 1'b0;
if (INIT === 1'b1) begin
FFPE _TECHMAP_REPLACE_ (.C(C), .D(D), .PRE(1'b0), .CE(1'b1), .Q(Q));
end else begin
FFCE _TECHMAP_REPLACE_ (.C(C), .D(D), .CLR(1'b0), .CE(1'b1), .Q(Q));
end
endmodule
module FF_N (input C, D, output Q);
parameter INIT = 1'b0;
if (INIT === 1'b1) begin
FFPE_N _TECHMAP_REPLACE_ (.C(C), .D(D), .PRE(1'b0), .CE(1'b1), .Q(Q));
end else begin
FFCE_N _TECHMAP_REPLACE_ (.C(C), .D(D), .CLR(1'b0), .CE(1'b1), .Q(Q));
end
endmodule
module FFC (input C, D, CLR, output Q);
FFCE _TECHMAP_REPLACE_ (.C(C), .D(D), .CLR(CLR), .CE(1'b1), .Q(Q));
endmodule
module FFC_N (input C, D, CLR, output Q);
FFCE_N _TECHMAP_REPLACE_ (.C(C), .D(D), .CLR(CLR), .CE(1'b1), .Q(Q));
endmodule
module FFP (input C, D, PRE, output Q);
FFPE _TECHMAP_REPLACE_ (.C(C), .D(D), .PRE(PRE), .CE(1'b1), .Q(Q));
endmodule
module FFP_N (input C, D, CLR, output Q);
FFPE_N _TECHMAP_REPLACE_ (.C(C), .D(D), .PRE(PRE), .CE(1'b1), .Q(Q));
endmodule
module FFR (input C, D, R, output Q);
FFRE _TECHMAP_REPLACE_ (.C(C), .D(D), .R(R), .CE(1'b1), .Q(Q));
endmodule
module FFR_N (input C, D, R, output Q);
FFRE_N _TECHMAP_REPLACE_ (.C(C), .D(D), .R(R), .CE(1'b1), .Q(Q));
endmodule
module FFS (input C, D, S, output Q);
FFSE _TECHMAP_REPLACE_ (.C(C), .D(D), .S(S), .CE(1'b1), .Q(Q));
endmodule
module FFS_N (input C, D, S, output Q);
FFSE_N _TECHMAP_REPLACE_ (.C(C), .D(D), .S(S), .CE(1'b1), .Q(Q));
endmodule

View file

@ -0,0 +1,516 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* (C) 2019 Eddie Hung <eddie@fpgeh.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/register.h"
#include "kernel/celltypes.h"
#include "kernel/rtlil.h"
#include "kernel/log.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct SynthAnalogDevicesPass : public ScriptPass
{
SynthAnalogDevicesPass() : ScriptPass("synth_analogdevices", "synthesis for Analog Devices FPGAs") { }
void on_register() override
{
RTLIL::constpad["synth_analogdevices.abc9.W"] = "300"; // Number with which ABC will map a 6-input gate
// to one LUT6 (instead of a LUT5 + LUT2)
}
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" synth_analogdevices [options]\n");
log("\n");
log("This command runs synthesis for Analog Devices FPGAs. This command does not operate on\n");
log("partly selected designs.\n");
log("\n");
log(" -top <module>\n");
log(" use the specified module as top module\n");
log("\n");
log(" -tech <tech>\n");
log(" run synthesis for the specified ADI technology process\n");
log(" currently only affects the type of BRAM used.\n");
log(" supported values:\n");
log(" - t40lp (RBRAM)\n");
log(" - t16ffc (RBRAM2, default)\n");
log("\n");
log(" -edif <file>\n");
log(" write the design to the specified edif file. writing of an output file\n");
log(" is omitted if this parameter is not specified.\n");
log("\n");
log(" -nobram\n");
log(" do not use block RAM cells in output netlist\n");
log("\n");
log(" -nolutram\n");
log(" do not use distributed RAM cells in output netlist\n");
log("\n");
log(" -nosrl\n");
log(" do not use distributed SRL cells in output netlist\n");
log("\n");
log(" -nocarry\n");
log(" do not use XORCY/MUXCY/CARRY4 cells in output netlist\n");
log("\n");
log(" -nowidelut\n");
log(" do not use MUXF[7-8] resources to implement LUTs larger than native for\n");
log(" the target\n");
log("\n");
log(" -nodsp\n");
log(" do not use DSP48*s to implement multipliers and associated logic\n");
log("\n");
log(" -noiopad\n");
log(" disable I/O buffer insertion (useful for hierarchical or \n");
log(" out-of-context flows)\n");
log("\n");
log(" -noclkbuf\n");
log(" disable automatic clock buffer insertion\n");
log("\n");
log(" -widemux <int>\n");
log(" enable inference of hard multiplexer resources (MUXF[78]) for muxes at\n");
log(" or above this number of inputs (minimum value 2, recommended value >= 5)\n");
log(" default: 0 (no inference)\n");
log("\n");
log(" -run <from_label>:<to_label>\n");
log(" only run the commands between the labels (see below). an empty\n");
log(" from label is synonymous to 'begin', and empty to label is\n");
log(" synonymous to the end of the command list.\n");
log("\n");
log(" -noflatten\n");
log(" do not flatten design before synthesis\n");
log("\n");
log(" -dff\n");
log(" run 'abc'/'abc9' with -dff option\n");
log("\n");
log(" -retime\n");
log(" run 'abc' with '-D 1' option to enable flip-flop retiming.\n");
log(" implies -dff.\n");
log("\n");
log(" -noabc9\n");
log(" disable use of new ABC9 flow\n");
log("\n");
log("\n");
log("The following commands are executed by this synthesis command:\n");
help_script();
log("\n");
}
std::string top_opt, edif_file, json_file, tech, tech_param;
bool flatten, retime, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, nodsp;
bool abc9, dff;
bool flatten_before_abc;
int widemux;
int widelut_size;
void clear_flags() override
{
top_opt = "-auto-top";
edif_file.clear();
tech = "t16ffc";
tech_param = " -D IS_T16FFC";
flatten = true;
retime = false;
noiopad = false;
noclkbuf = false;
nocarry = false;
nobram = false;
nolutram = false;
nosrl = false;
nocarry = false;
nowidelut = false;
nodsp = false;
abc9 = true;
dff = false;
flatten_before_abc = false;
widemux = 0;
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
std::string run_from, run_to;
clear_flags();
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
if (args[argidx] == "-top" && argidx+1 < args.size()) {
top_opt = "-top " + args[++argidx];
continue;
}
if (args[argidx] == "-tech" && argidx+1 < args.size()) {
tech = args[++argidx];
if (tech == "t16ffc")
tech_param = " -D IS_T16FFC";
else if (tech == "t40lp")
tech_param = " -D IS_T40LP";
continue;
}
if (args[argidx] == "-edif" && argidx+1 < args.size()) {
edif_file = args[++argidx];
continue;
}
if (args[argidx] == "-run" && argidx+1 < args.size()) {
size_t pos = args[argidx+1].find(':');
if (pos == std::string::npos)
break;
run_from = args[++argidx].substr(0, pos);
run_to = args[argidx].substr(pos+1);
continue;
}
if (args[argidx] == "-noflatten") {
flatten = false;
continue;
}
if (args[argidx] == "-flatten_before_abc") {
flatten_before_abc = true;
continue;
}
if (args[argidx] == "-retime") {
dff = true;
retime = true;
continue;
}
if (args[argidx] == "-nocarry") {
nocarry = true;
continue;
}
if (args[argidx] == "-nowidelut") {
nowidelut = true;
continue;
}
if (args[argidx] == "-iopad") {
continue;
}
if (args[argidx] == "-noiopad") {
noiopad = true;
continue;
}
if (args[argidx] == "-noclkbuf") {
noclkbuf = true;
continue;
}
if (args[argidx] == "-nocarry") {
nocarry = true;
continue;
}
if (args[argidx] == "-nobram") {
nobram = true;
continue;
}
if (args[argidx] == "-nolutram") {
nolutram = true;
continue;
}
if (args[argidx] == "-nosrl") {
nosrl = true;
continue;
}
if (args[argidx] == "-widemux" && argidx+1 < args.size()) {
widemux = atoi(args[++argidx].c_str());
continue;
}
if (args[argidx] == "-noabc9") {
abc9 = false;
continue;
}
if (args[argidx] == "-nodsp") {
nodsp = true;
continue;
}
if (args[argidx] == "-dff") {
dff = true;
continue;
}
if (args[argidx] == "-json" && argidx+1 < args.size()) {
json_file = args[++argidx];
continue;
}
break;
}
extra_args(args, argidx, design);
if (!(tech == "t16ffc" || tech == "t40lp"))
log_cmd_error("Invalid ADI -tech setting: '%s'.\n", tech);
if (widemux != 0 && widemux < 2)
log_cmd_error("-widemux value must be 0 or >= 2.\n");
if (!design->full_selection())
log_cmd_error("This command only operates on fully selected designs!\n");
if (abc9 && retime)
log_cmd_error("-retime option not currently compatible with -abc9!\n");
log_header(design, "Executing SYNTH_ANALOGDEVICES pass.\n");
log_push();
run_script(design, run_from, run_to);
log_pop();
}
void script() override
{
if (check_label("begin")) {
run(stringf("read_verilog -lib -specify %s +/analogdevices/cells_sim.v", tech_param));
run(stringf("hierarchy -check %s", top_opt.c_str()));
}
if (check_label("prepare")) {
run("proc");
if (flatten || help_mode)
run("flatten", "(with '-flatten')");
if (active_design)
active_design->scratchpad_unset("tribuf.added_something");
run("tribuf -logic");
if (noiopad && active_design && active_design->scratchpad_get_bool("tribuf.added_something"))
log_error("Tristate buffers are unsupported without the '-iopad' option.\n");
run("deminout");
run("opt_expr");
run("opt_clean");
run("check");
run("opt -nodffe -nosdff");
run("fsm");
run("opt");
if (help_mode)
run("wreduce [-keepdc]", "(option for '-widemux')");
else
run("wreduce" + std::string(widemux > 0 ? " -keepdc" : ""));
run("peepopt");
run("opt_clean");
if (widemux > 0 || help_mode)
run("muxpack", " ('-widemux' only)");
// xilinx_srl looks for $shiftx cells for identifying variable-length
// shift registers, so attempt to convert $pmux-es to this
// Also: wide multiplexer inference benefits from this too
if (!(nosrl && widemux == 0) || help_mode) {
run("pmux2shiftx", "(skip if '-nosrl' and '-widemux=0')");
run("clean", " (skip if '-nosrl' and '-widemux=0')");
}
}
if (check_label("map_dsp", "(skip if '-nodsp')")) {
if (!nodsp || help_mode) {
run("memory_dff"); // xilinx_dsp will merge registers, reserve memory port registers first
// NB: Analog Devices multipliers are signed only
if (help_mode)
run("techmap -map +/mul2dsp.v -map +/analogdevices/{family}_dsp_map.v {options}");
run("techmap -map +/mul2dsp.v -map +/analogdevices/dsp_map.v -D DSP_A_MAXWIDTH=22 -D DSP_B_MAXWIDTH=22 "
"-D DSP_A_MAXWIDTH_PARTIAL=18 " // Partial multipliers are intentionally
// limited to 18x18 in order to take
// advantage of the (PCOUT << 17) -> PCIN
// dedicated cascade chain capability
"-D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 " // Blocks Nx1 multipliers
"-D DSP_Y_MINWIDTH=9 " // UG901 suggests small multiplies are those 4x4 and smaller
"-D DSP_SIGNEDONLY=1 -D DSP_NAME=$__MUL22X22");
run("select a:mul2dsp");
run("setattr -unset mul2dsp");
run("opt_expr -fine");
run("wreduce");
run("select -clear");
if (help_mode)
run("xilinx_dsp -family <family>");
else
run("xilinx_dsp -family xc7");
run("chtype -set $mul t:$__soft_mul");
}
}
if (check_label("coarse")) {
run("techmap -map +/cmp2lut.v -map +/cmp2lcu.v -D LUT_WIDTH=6");
run("alumacc");
run("share");
run("opt");
run("memory -nomap");
run("opt_clean");
}
if (check_label("map_memory")) {
std::string params = "";
std::string lutrams_map = "+/analogdevices/lutrams_map.v";
std::string brams_map = "+/analogdevices/brams_map.v";
if (help_mode) {
params = " [...]";
} else {
params += " -logic-cost-rom 0.015625";
params += " -force-params";
params += " -lib +/analogdevices/lutrams.txt";
params += " -lib +/analogdevices/brams.txt";
params += tech_param;
brams_map += tech_param;
if (nolutram)
params += " -no-auto-distributed";
if (nobram)
params += " -no-auto-block";
}
run("memory_libmap" + params);
run("techmap -map " + lutrams_map);
run("techmap -map " + brams_map);
}
if (check_label("map_ffram")) {
if (widemux > 0) {
run("opt -fast -mux_bool -undriven -fine"); // Necessary to omit -mux_undef otherwise muxcover
// performs less efficiently
} else {
run("opt -fast -full");
}
run("memory_map");
}
if (check_label("fine")) {
if (help_mode) {
run("simplemap t:$mux", "('-widemux' only)");
run("muxcover <internal options>", "('-widemux' only)");
} else if (widemux > 0) {
run("simplemap t:$mux");
constexpr int cost_mux2 = 100;
std::string muxcover_args = stringf(" -nodecode -mux2=%d", cost_mux2);
switch (widemux) {
case 2: muxcover_args += stringf(" -mux4=%d -mux8=%d -mux16=%d", cost_mux2+1, cost_mux2+2, cost_mux2+3); break;
case 3:
case 4: muxcover_args += stringf(" -mux4=%d -mux8=%d -mux16=%d", cost_mux2*(widemux-1)-2, cost_mux2*(widemux-1)-1, cost_mux2*(widemux-1)); break;
case 5:
case 6:
case 7:
case 8: muxcover_args += stringf(" -mux8=%d -mux16=%d", cost_mux2*(widemux-1)-1, cost_mux2*(widemux-1)); break;
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
default: muxcover_args += stringf(" -mux16=%d", cost_mux2*(widemux-1)-1); break;
}
run("muxcover " + muxcover_args);
}
run("opt -full");
if (!nosrl || help_mode)
run("xilinx_srl -variable -minlen 3", "(skip if '-nosrl')");
std::string techmap_args = " -map +/techmap.v -D LUT_SIZE=6";
if (help_mode)
techmap_args += " [-map +/analogdevices/mux_map.v]";
else if (widemux > 0)
techmap_args += stringf(" -D MIN_MUX_INPUTS=%d -map +/analogdevices/mux_map.v", widemux);
if (!nocarry) {
techmap_args += " -map +/analogdevices/arith_map.v";
}
run("techmap " + techmap_args);
run("opt -fast");
}
if (check_label("map_cells")) {
// Needs to be done before logic optimization, so that inverters (inserted
// here because of negative-polarity output enable) are handled.
if (help_mode || !noiopad)
run("iopadmap -bits -outpad OUTBUF I:O -inpad INBUF O:I A:top", "(skip if '-noiopad')");
std::string techmap_args = "-map +/techmap.v -map +/analogdevices/cells_map.v";
if (widemux > 0)
techmap_args += stringf(" -D MIN_MUX_INPUTS=%d", widemux);
run("techmap " + techmap_args);
run("clean");
}
if (check_label("map_ffs")) {
run("dfflegalize -cell $_DFFE_?P?P_ r -cell $_SDFFE_?P?P_ r");
if (abc9 || help_mode) {
if (dff || help_mode)
run("zinit -all w:* t:$_SDFFE_*", "('-dff' only)");
run("techmap -map +/analogdevices/ff_map.v", "('-abc9' only)");
}
}
if (check_label("map_luts")) {
run("opt_expr -mux_undef -noclkinv");
if (flatten_before_abc)
run("flatten");
if (help_mode)
run("abc -luts 2:2,3,6:5[,10,20] [-dff] [-D 1]", "(option for '-nowidelut', '-dff', '-retime')");
else if (abc9) {
run("read_verilog -icells -lib -specify +/analogdevices/abc9_model.v");
std::string abc9_opts;
std::string k = "synth_analogdevices.abc9.W";
if (active_design && active_design->scratchpad.count(k))
abc9_opts += stringf(" -W %s", active_design->scratchpad_get_string(k).c_str());
else {
abc9_opts += stringf(" -W %s", RTLIL::constpad.at("synth_analogdevices.abc9.W").c_str());
}
if (nowidelut)
abc9_opts += stringf(" -maxlut 6");
if (dff)
abc9_opts += " -dff";
run("abc9" + abc9_opts);
}
else {
std::string abc_opts;
if (nowidelut)
abc_opts += " -luts 2:2,3,6:5";
else
abc_opts += " -luts 2:2,3,6:5,10,20";
if (dff)
abc_opts += " -dff";
if (retime)
abc_opts += " -D 1";
run("abc -dress" + abc_opts);
}
run("clean");
if (help_mode || !abc9)
run("techmap -map +/analogdevices/ff_map.v", "(only if not '-abc9')");
// This shregmap call infers fixed length shift registers after abc
// has performed any necessary retiming
if (!nosrl || help_mode)
run("xilinx_srl -fixed -minlen 3", "(skip if '-nosrl')");
std::string techmap_args = "-map +/analogdevices/lut_map.v -map +/analogdevices/cells_map.v";
techmap_args += " -D LUT_WIDTH=6";
run("techmap " + techmap_args);
run("xilinx_dffopt");
run("opt_lut_ins -tech analogdevices");
}
if (check_label("finalize")) {
run("clean");
}
if (check_label("check")) {
run("hierarchy -check");
run("stat -tech analogdevices");
run("check -noinit");
run("blackbox =A:whitebox");
}
if (check_label("edif")) {
if (!edif_file.empty() || help_mode) {
run("delete t:$assert t:$scopeinfo");
run(stringf("write_edif %s", edif_file.c_str()));
}
}
}
} SynthAnalogDevicesPass;
PRIVATE_NAMESPACE_END

View file

@ -326,13 +326,13 @@ struct SynthPass : public ScriptPass {
if ((!noabc && !flowmap) || help_mode) {
#ifdef YOSYS_ENABLE_ABC
if (help_mode) {
run(abc + " -fast", " (unless -noabc, unless -lut)");
run(abc + " -fast -lut k", "(unless -noabc, if -lut)");
run(abc, " (unless -noabc, unless -lut)");
run(abc + " -lut k", "(unless -noabc, if -lut)");
} else {
if (lut)
run(stringf("%s -fast -lut %d", abc, lut));
run(stringf("%s -lut %d", abc, lut));
else
run(abc + " -fast");
run(abc);
}
run("opt -fast", " (unless -noabc)");
#endif

View file

@ -149,6 +149,30 @@ module OutPass4_frame_config (input CLK, I0, I1, I2, I3);
endmodule
(* blackbox, keep *)
module InPass4_frame_config_mux #(
parameter [3:0] O_reg = 0
) (
input CLK,
output O0,
output O1,
output O2,
output O3
);
endmodule
(* blackbox, keep *)
module OutPass4_frame_config_mux #(
parameter [3:0] I_reg = 0
) (
input I0,
input I1,
input I2,
input I3,
input CLK
);
endmodule
(* keep *)
module IO_1_bidirectional_frame_config_pass (input CLK, T, I, output Q, O, (* iopad_external_pin *) inout PAD);
assign PAD = T ? 1'bz : I;

View file

@ -3,6 +3,7 @@ OBJS += techlibs/gowin/synth_gowin.o
$(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_map.v))
$(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_sim.v))
$(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_latch.v))
$(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_xtra_gw1n.v))
$(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_xtra_gw2a.v))
$(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_xtra_gw5a.v))

View file

@ -51,11 +51,6 @@ ram block $__GOWIN_DP_ {
portoption "WRITE_MODE" 1 {
rdwr new;
}
ifndef gw5a {
portoption "WRITE_MODE" 2 {
rdwr old;
}
}
wrbe_separate;
}
}

View file

@ -0,0 +1,37 @@
`default_nettype none
// DL D Latch with Positive Gate
module \$_DLATCH_P_ (input E, D, output Q);
DL _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(E));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule
// DLN D Latch with Negative Gate
module \$_DLATCH_N_ (input E, D, output Q);
DLN _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(E));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule
// DLC D Latch with Positive Gate and Asynchronous Clear
module \$_DLATCH_PP0_ (input E, R, D, output Q);
DLC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(E), .CLEAR(R));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule
// DLNC D Latch with Negative Gate and Asynchronous Clear
module \$_DLATCH_NP0_ (input E, R, D, output Q);
DLNC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(E), .CLEAR(R));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule
// DLP D Latch with Positive Gate and Asynchronous Preset
module \$_DLATCH_PP1_ (input E, R, D, output Q);
DLP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(E), .PRESET(R));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule
// DLNP D Latch with Negative Gate and Asynchronous Preset
module \$_DLATCH_NP1_ (input E, R, D, output Q);
DLNP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(E), .PRESET(R));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule

View file

@ -540,6 +540,101 @@ module DFFNCE (output reg Q, input D, CLK, CE, CLEAR);
end
endmodule // DFFNCE (negative clock edge; asynchronous clear; clock enable)
// Latch sim cells
// Gate signal uses CLK port name to match the physical DFF BEL pin
module DL (output reg Q, input D, CLK);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (CLK) Q <= D;
endmodule
module DLN (output reg Q, input D, CLK);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (!CLK) Q <= D;
endmodule
module DLE (output reg Q, input D, CLK, CE);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (CLK && CE) Q <= D;
endmodule
module DLNE (output reg Q, input D, CLK, CE);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (!CLK && CE) Q <= D;
endmodule
module DLC (output reg Q, input D, CLK, CLEAR);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (CLEAR) Q <= 1'b0;
else if (CLK) Q <= D;
endmodule
module DLCE (output reg Q, input D, CLK, CE, CLEAR);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (CLEAR) Q <= 1'b0;
else if (CLK && CE) Q <= D;
endmodule
module DLNC (output reg Q, input D, CLK, CLEAR);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (CLEAR) Q <= 1'b0;
else if (!CLK) Q <= D;
endmodule
module DLNCE (output reg Q, input D, CLK, CE, CLEAR);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (CLEAR) Q <= 1'b0;
else if (!CLK && CE) Q <= D;
endmodule
module DLP (output reg Q, input D, CLK, PRESET);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (PRESET) Q <= 1'b1;
else if (CLK) Q <= D;
endmodule
module DLPE (output reg Q, input D, CLK, CE, PRESET);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (PRESET) Q <= 1'b1;
else if (CLK && CE) Q <= D;
endmodule
module DLNP (output reg Q, input D, CLK, PRESET);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (PRESET) Q <= 1'b1;
else if (!CLK) Q <= D;
endmodule
module DLNPE (output reg Q, input D, CLK, CE, PRESET);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (PRESET) Q <= 1'b1;
else if (!CLK && CE) Q <= D;
endmodule
// TODO add more DFF sim cells
module VCC(output V);

View file

@ -340,17 +340,18 @@ struct SynthGowinPass : public ScriptPass
run("opt_clean");
if (family == "gw5a") {
if (strict_gw5a_dffs) {
run("dfflegalize -cell $_SDFFE_PP?P_ r -cell $_DFFE_PP?P_ r");
run("dfflegalize -cell $_SDFFE_PP?P_ r -cell $_DFFE_PP?P_ r -cell $_DLATCH_?_ x -cell $_DLATCH_?P?_ x");
} else {
run("dfflegalize -cell $_DFF_?_ 0 -cell $_SDFFE_PP?P_ r -cell $_DFFE_PP?P_ r");
run("dfflegalize -cell $_DFF_?_ 0 -cell $_SDFFE_PP?P_ r -cell $_DFFE_PP?P_ r -cell $_DLATCH_?_ x -cell $_DLATCH_?P?_ x");
}
} else {
if (nodffe)
run("dfflegalize -cell $_DFF_?_ 0 -cell $_SDFF_?P?_ r -cell $_DFF_?P?_ r");
run("dfflegalize -cell $_DFF_?_ 0 -cell $_SDFF_?P?_ r -cell $_DFF_?P?_ r -cell $_DLATCH_?_ x -cell $_DLATCH_?P?_ x");
else
run("dfflegalize -cell $_DFF_?_ 0 -cell $_DFFE_?P_ 0 -cell $_SDFF_?P?_ r -cell $_SDFFE_?P?P_ r -cell $_DFF_?P?_ r -cell $_DFFE_?P?P_ r");
run("dfflegalize -cell $_DFF_?_ 0 -cell $_DFFE_?P_ 0 -cell $_SDFF_?P?_ r -cell $_SDFFE_?P?P_ r -cell $_DFF_?P?_ r -cell $_DFFE_?P?P_ r -cell $_DLATCH_?_ x -cell $_DLATCH_?P?_ x");
}
run("techmap -map +/gowin/cells_map.v");
run("techmap -map +/gowin/cells_latch.v");
run("opt_expr -mux_undef");
run("simplemap");
}

View file

@ -236,6 +236,7 @@ struct SynthQuickLogicPass : public ScriptPass {
run("ql_dsp_macc");
run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=20 -D DSP_B_MAXWIDTH=18 -D DSP_A_MINWIDTH=11 -D DSP_B_MINWIDTH=10 -D DSP_NAME=$__QL_MUL20X18");
run("chtype -set $mul t:$__soft_mul");
run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=10 -D DSP_B_MAXWIDTH=9 -D DSP_A_MINWIDTH=4 -D DSP_B_MINWIDTH=4 -D DSP_NAME=$__QL_MUL10X9");
run("chtype -set $mul t:$__soft_mul");

1
tests/arch/analogdevices/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
t_*.ys

View file

@ -0,0 +1,13 @@
read_verilog ../common/add_sub.v
hierarchy -top top
proc
design -save orig
equiv_opt -assert -map +/analogdevices/cells_sim.v synth_analogdevices -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd top # Constrain all select calls below inside the top module
stat
select -assert-count 8 t:LUT2
select -assert-count 2 t:CRY4
select -assert-count 2 t:CRY4INIT
select -assert-none t:LUT2 t:CRY4 t:CRY4INIT %% t:* %D

View file

@ -0,0 +1,47 @@
read_verilog ../common/adffs.v
design -save read
hierarchy -top adff
proc
equiv_opt -async2sync -assert -map +/analogdevices/cells_sim.v synth_analogdevices -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd adff # Constrain all select calls below inside the top module
select -assert-count 1 t:FFCE
select -assert-none t:FFCE %% t:* %D
design -load read
hierarchy -top adffn
proc
equiv_opt -async2sync -assert -map +/analogdevices/cells_sim.v synth_analogdevices -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd adffn # Constrain all select calls below inside the top module
select -assert-count 1 t:FFCE
select -assert-count 1 t:LUT1
select -assert-none t:FFCE t:LUT1 %% t:* %D
design -load read
hierarchy -top dffs
proc
equiv_opt -async2sync -assert -map +/analogdevices/cells_sim.v synth_analogdevices -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd dffs # Constrain all select calls below inside the top module
select -assert-count 1 t:FFRE
select -assert-count 1 t:LUT2
stat
select -assert-none t:FFRE t:LUT2 %% t:* %D
design -load read
hierarchy -top ndffnr
proc
equiv_opt -async2sync -assert -map +/analogdevices/cells_sim.v synth_analogdevices -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd ndffnr # Constrain all select calls below inside the top module
select -assert-count 1 t:FFRE_N
select -assert-count 1 t:LUT1
select -assert-none t:FFRE_N t:LUT1 %% t:* %D

View file

@ -0,0 +1,50 @@
# Memory bits <= 18K; Data width <= 36; Address width <= 14: -> RBRAM2
# w4b | r16b
design -reset
read_verilog asym_ram_sdp_read_wider.v
synth_analogdevices -top asym_ram_sdp_read_wider -noiopad
select -assert-count 1 t:RBRAM2
# w8b | r16b
design -reset
read_verilog asym_ram_sdp_read_wider.v
chparam -set WIDTHA 8 -set SIZEA 512 -set ADDRWIDTHA 9 asym_ram_sdp_read_wider
synth_analogdevices -top asym_ram_sdp_read_wider -noiopad
select -assert-count 1 t:RBRAM2
# w4b | r32b
design -reset
read_verilog asym_ram_sdp_read_wider.v
chparam -set WIDTHB 32 -set SIZEB 128 -set ADDRWIDTHB 7 asym_ram_sdp_read_wider
synth_analogdevices -top asym_ram_sdp_read_wider -noiopad
select -assert-count 2 t:RBRAM2
# w16b | r4b
design -reset
read_verilog asym_ram_sdp_write_wider.v
synth_analogdevices -top asym_ram_sdp_write_wider -noiopad
select -assert-count 1 t:RBRAM2
# w16b | r8b
design -reset
read_verilog asym_ram_sdp_write_wider.v
chparam -set WIDTHB 8 -set SIZEB 512 -set ADDRWIDTHB 9 asym_ram_sdp_read_wider
synth_analogdevices -top asym_ram_sdp_write_wider -noiopad
select -assert-count 1 t:RBRAM2
# w32b | r4b
design -reset
read_verilog asym_ram_sdp_write_wider.v
chparam -set WIDTHA 32 -set SIZEA 128 -set ADDRWIDTHA 7 asym_ram_sdp_read_wider
synth_analogdevices -top asym_ram_sdp_write_wider -noiopad
select -assert-count 1 t:RBRAM2
# w4b | r24b
design -reset
read_verilog asym_ram_sdp_read_wider.v
chparam -set SIZEA 768
chparam -set WIDTHB 24 -set SIZEB 128 -set ADDRWIDTHB 7 asym_ram_sdp_read_wider
synth_analogdevices -top asym_ram_sdp_read_wider -noiopad
select -assert-count 2 t:RBRAM2

View file

@ -0,0 +1,73 @@
// Asymmetric port RAM
// Read Wider than Write. Read Statement in loop
//asym_ram_sdp_read_wider.v
module asym_ram_sdp_read_wider (clkA, clkB, enaA, weA, enaB, addrA, addrB, diA, doB);
parameter WIDTHA = 4;
parameter SIZEA = 1024;
parameter ADDRWIDTHA = 10;
parameter WIDTHB = 16;
parameter SIZEB = 256;
parameter ADDRWIDTHB = 8;
input clkA;
input clkB;
input weA;
input enaA, enaB;
input [ADDRWIDTHA-1:0] addrA;
input [ADDRWIDTHB-1:0] addrB;
input [WIDTHA-1:0] diA;
output [WIDTHB-1:0] doB;
`define max(a,b) {(a) > (b) ? (a) : (b)}
`define min(a,b) {(a) < (b) ? (a) : (b)}
function integer log2;
input integer value;
reg [31:0] shifted;
integer res;
begin
if (value < 2)
log2 = value;
else
begin
shifted = value-1;
for (res=0; shifted>0; res=res+1)
shifted = shifted>>1;
log2 = res;
end
end
endfunction
localparam maxSIZE = `max(SIZEA, SIZEB);
localparam maxWIDTH = `max(WIDTHA, WIDTHB);
localparam minWIDTH = `min(WIDTHA, WIDTHB);
localparam RATIO = maxWIDTH / minWIDTH;
localparam log2RATIO = log2(RATIO);
(* ram_style="block" *)
reg [minWIDTH-1:0] RAM [0:maxSIZE-1];
reg [WIDTHB-1:0] readB;
always @(posedge clkA)
begin
if (enaA) begin
if (weA)
RAM[addrA] <= diA;
end
end
always @(posedge clkB)
begin : ramread
integer i;
reg [log2RATIO-1:0] lsbaddr;
if (enaB) begin
for (i = 0; i < RATIO; i = i+1) begin
lsbaddr = i;
readB[(i+1)*minWIDTH-1 -: minWIDTH] <= RAM[{addrB, lsbaddr}];
end
end
end
assign doB = readB;
endmodule

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