From e609a59325e2539e534fe8ad86edec067b9f8192 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:59:16 +1300 Subject: [PATCH 01/55] CI: Fixes from zizmor > A static analysis tool for Github Actions https://github.com/woodruffw/zizmor Mostly means adding `persist-credentials: false` to checkouts and replacing `{{ env.VARNAME }}` with shell substitution. --- .github/workflows/codeql.yml | 4 +++- .github/workflows/extra-builds.yml | 7 +++++-- .github/workflows/prepare-docs.yml | 8 ++++---- .github/workflows/source-vendor.yml | 1 + .github/workflows/test-build.yml | 14 ++++++++++---- .github/workflows/test-compile.yml | 1 + .github/workflows/test-verific.yml | 5 +++-- .github/workflows/update-flake-lock.yml | 2 ++ .github/workflows/version.yml | 1 + .github/workflows/wheels.yml | 1 + 10 files changed, 31 insertions(+), 13 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 24ae7c898..75d799fe1 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -16,7 +16,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - submodules: true + submodules: true + persist-credentials: false + - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: diff --git a/.github/workflows/extra-builds.yml b/.github/workflows/extra-builds.yml index 1a00d0163..0c3146e41 100644 --- a/.github/workflows/extra-builds.yml +++ b/.github/workflows/extra-builds.yml @@ -25,7 +25,8 @@ jobs: steps: - uses: actions/checkout@v4 with: - submodules: true + submodules: true + persist-credentials: false - name: Build run: make vcxsrc YOSYS_VER=latest - uses: actions/upload-artifact@v4 @@ -59,7 +60,8 @@ jobs: steps: - uses: actions/checkout@v4 with: - submodules: true + submodules: true + persist-credentials: false - name: Build run: | WASI_SDK=wasi-sdk-19.0 @@ -95,6 +97,7 @@ jobs: - uses: actions/checkout@v4 with: submodules: true + persist-credentials: false - uses: cachix/install-nix-action@v26 with: install_url: https://releases.nixos.org/nix/nix-2.18.1/install diff --git a/.github/workflows/prepare-docs.yml b/.github/workflows/prepare-docs.yml index 79dfb7912..fb1fab426 100644 --- a/.github/workflows/prepare-docs.yml +++ b/.github/workflows/prepare-docs.yml @@ -20,7 +20,7 @@ jobs: # only run on push *or* pull_request, not both concurrent_skipping: ${{ env.docs_export && 'never' || 'same_content_newer'}} - id: docs_var - run: echo "docs_export=${{ env.docs_export }}" >> $GITHUB_OUTPUT + run: echo "docs_export=${docs_export}" >> $GITHUB_OUTPUT prepare-docs: # docs builds are needed for anything on main, any tagged versions, and any tag @@ -47,12 +47,12 @@ jobs: echo "ENABLE_VERIFIC_LIBERTY := 1" >> Makefile.conf echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 1" >> Makefile.conf echo "ENABLE_CCACHE := 1" >> Makefile.conf - make -j${{ env.procs }} ENABLE_LTO=1 + make -j$procs ENABLE_LTO=1 - name: Prepare docs shell: bash run: - make docs/prep -j${{ env.procs }} TARGETS= EXTRA_TARGETS= + make docs/prep -j$procs TARGETS= EXTRA_TARGETS= - name: Upload artifact uses: actions/upload-artifact@v4 @@ -72,7 +72,7 @@ jobs: - name: Test build docs shell: bash run: | - make -C docs html -j${{ env.procs }} TARGETS= EXTRA_TARGETS= + make -C docs html -j$procs TARGETS= EXTRA_TARGETS= - name: Trigger RTDs build if: ${{ needs.check_docs_rebuild.outputs.docs_export == 'true' }} diff --git a/.github/workflows/source-vendor.yml b/.github/workflows/source-vendor.yml index 4dddb9a20..dc9480ef6 100644 --- a/.github/workflows/source-vendor.yml +++ b/.github/workflows/source-vendor.yml @@ -10,6 +10,7 @@ jobs: uses: actions/checkout@v4 with: submodules: 'recursive' + persist-credentials: false - name: Create clean tarball run: | diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index 95af300c9..ef8d0e09d 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -46,6 +46,7 @@ jobs: uses: actions/checkout@v4 with: submodules: true + persist-credentials: false - name: Setup environment uses: ./.github/actions/setup-build-env @@ -89,6 +90,8 @@ jobs: steps: - name: Checkout Yosys uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup environment uses: ./.github/actions/setup-build-env @@ -115,7 +118,7 @@ jobs: uses: actions/cache@v4 with: path: .local/ - key: ${{ matrix.os }}-${{ env.IVERILOG_GIT }} + key: ${{ matrix.os }}-${IVERILOG_GIT} - name: Build iverilog if: steps.cache-iverilog.outputs.cache-hit != 'true' @@ -167,6 +170,8 @@ jobs: steps: - name: Checkout Yosys uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup environment uses: ./.github/actions/setup-build-env @@ -188,7 +193,7 @@ jobs: - name: Run tests shell: bash run: | - make -C docs test -j${{ env.procs }} + make -C docs test -j$procs test-docs-build: name: Try build docs @@ -204,6 +209,7 @@ jobs: uses: actions/checkout@v4 with: submodules: true + persist-credentials: false - name: Runtime environment run: | @@ -213,7 +219,7 @@ jobs: run: | make config-clang echo "ENABLE_CCACHE := 1" >> Makefile.conf - make -j${{ env.procs }} + make -j$procs - name: Install doc prereqs shell: bash @@ -223,7 +229,7 @@ jobs: - name: Build docs shell: bash run: | - make docs DOC_TARGET=${{ matrix.docs-target }} -j${{ env.procs }} + make docs DOC_TARGET=${{ matrix.docs-target }} -j$procs - name: Store docs build artifact uses: actions/upload-artifact@v4 diff --git a/.github/workflows/test-compile.yml b/.github/workflows/test-compile.yml index 74c3e2639..4f60af0f8 100644 --- a/.github/workflows/test-compile.yml +++ b/.github/workflows/test-compile.yml @@ -48,6 +48,7 @@ jobs: uses: actions/checkout@v4 with: submodules: true + persist-credentials: false - name: Setup environment uses: ./.github/actions/setup-build-env diff --git a/.github/workflows/test-verific.yml b/.github/workflows/test-verific.yml index 98b05e8dd..013c9f8ca 100644 --- a/.github/workflows/test-verific.yml +++ b/.github/workflows/test-verific.yml @@ -40,7 +40,7 @@ jobs: echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 1" >> Makefile.conf echo "ENABLE_CCACHE := 1" >> Makefile.conf echo "ENABLE_FUNCTIONAL_TESTS := 1" >> Makefile.conf - make -j${{ env.procs }} ENABLE_LTO=1 + make -j$procs ENABLE_LTO=1 - name: Install Yosys run: | @@ -51,6 +51,7 @@ jobs: with: repository: 'YosysHQ/sby' path: 'sby' + persist-credentials: false - name: Build SBY run: | @@ -58,7 +59,7 @@ jobs: - name: Run Yosys tests run: | - make -j${{ env.procs }} test + make -j$procs test - name: Run Verific specific Yosys tests run: | diff --git a/.github/workflows/update-flake-lock.yml b/.github/workflows/update-flake-lock.yml index c7aa6ecab..de7ef04d6 100644 --- a/.github/workflows/update-flake-lock.yml +++ b/.github/workflows/update-flake-lock.yml @@ -10,6 +10,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install Nix uses: DeterminateSystems/nix-installer-action@main - name: Update flake.lock diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index f73c68bdf..26dcba4a4 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -14,6 +14,7 @@ jobs: with: fetch-depth: 0 submodules: true + persist-credentials: false - name: Take last commit id: log run: echo "message=$(git log --no-merges -1 --oneline)" >> $GITHUB_OUTPUT diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index d66239a16..79f971dac 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -53,6 +53,7 @@ jobs: with: fetch-depth: 0 submodules: true + persist-credentials: false - if: ${{ matrix.os.family == 'linux' }} name: "[Linux] Set up QEMU" uses: docker/setup-qemu-action@v3 From 91cd382f8bb06c19c89742f8c56eb6187ef28d19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 18 Mar 2025 13:25:10 +0100 Subject: [PATCH 02/55] macc: Rename 'ports' to 'terms' throughout codebase --- kernel/consteval.h | 2 +- kernel/macc.h | 56 ++++++++++++++++++------------------- kernel/satgen.cc | 2 +- passes/opt/share.cc | 58 +++++++++++++++++++-------------------- passes/techmap/alumacc.cc | 52 +++++++++++++++++------------------ passes/techmap/booth.cc | 6 ++-- passes/techmap/maccmap.cc | 54 ++++++++++++++++++------------------ passes/tests/test_cell.cc | 12 ++++---- 8 files changed, 121 insertions(+), 121 deletions(-) diff --git a/kernel/consteval.h b/kernel/consteval.h index 05e94ab86..844120ef0 100644 --- a/kernel/consteval.h +++ b/kernel/consteval.h @@ -315,7 +315,7 @@ struct ConstEval Macc macc; macc.from_cell(cell); - for (auto &port : macc.ports) { + for (auto &port : macc.terms) { if (!eval(port.in_a, undef, cell)) return false; if (!eval(port.in_b, undef, cell)) diff --git a/kernel/macc.h b/kernel/macc.h index 5fc6036ed..ceda1dceb 100644 --- a/kernel/macc.h +++ b/kernel/macc.h @@ -26,18 +26,18 @@ YOSYS_NAMESPACE_BEGIN struct Macc { - struct port_t { + struct term_t { RTLIL::SigSpec in_a, in_b; bool is_signed, do_subtract; }; - std::vector ports; + std::vector terms; void optimize(int width) { - std::vector new_ports; + std::vector new_terms; RTLIL::Const off(0, width); - for (auto &port : ports) + for (auto &port : terms) { if (GetSize(port.in_a) == 0 && GetSize(port.in_b) == 0) continue; @@ -68,25 +68,25 @@ struct Macc port.in_b.remove(GetSize(port.in_b)-1); } - new_ports.push_back(port); + new_terms.push_back(port); } if (off.as_bool()) { - port_t port; + term_t port; port.in_a = off; port.is_signed = false; port.do_subtract = false; - new_ports.push_back(port); + new_terms.push_back(port); } - new_ports.swap(ports); + new_terms.swap(terms); } void from_cell_v1(RTLIL::Cell *cell) { RTLIL::SigSpec port_a = cell->getPort(ID::A); - ports.clear(); + terms.clear(); auto config_bits = cell->getParam(ID::CONFIG); int config_cursor = 0; @@ -105,7 +105,7 @@ struct Macc { log_assert(config_cursor + 2 + 2*num_bits <= config_width); - port_t this_port; + term_t this_port; this_port.is_signed = config_bits[config_cursor++] == State::S1; this_port.do_subtract = config_bits[config_cursor++] == State::S1; @@ -126,11 +126,11 @@ struct Macc port_a_cursor += size_b; if (size_a || size_b) - ports.push_back(this_port); + terms.push_back(this_port); } for (auto bit : cell->getPort(ID::B)) - ports.push_back(port_t{{bit}, {}, false, false}); + terms.push_back(term_t{{bit}, {}, false, false}); log_assert(config_cursor == config_width); log_assert(port_a_cursor == GetSize(port_a)); @@ -148,7 +148,7 @@ struct Macc RTLIL::SigSpec port_b = cell->getPort(ID::B); RTLIL::SigSpec port_c = cell->getPort(ID::C); - ports.clear(); + terms.clear(); int nproducts = cell->getParam(ID::NPRODUCTS).as_int(); const Const &product_neg = cell->getParam(ID::PRODUCT_NEGATED); @@ -158,7 +158,7 @@ struct Macc const Const &b_signed = cell->getParam(ID::B_SIGNED); int ai = 0, bi = 0; for (int i = 0; i < nproducts; i++) { - port_t term; + term_t term; log_assert(a_signed[i] == b_signed[i]); term.is_signed = (a_signed[i] == State::S1); @@ -171,7 +171,7 @@ struct Macc bi += b_width; term.do_subtract = (product_neg[i] == State::S1); - ports.push_back(term); + terms.push_back(term); } log_assert(port_a.size() == ai); log_assert(port_b.size() == bi); @@ -182,7 +182,7 @@ struct Macc const Const &c_signed = cell->getParam(ID::C_SIGNED); int ci = 0; for (int i = 0; i < naddends; i++) { - port_t term; + term_t term; term.is_signed = (c_signed[i] == State::S1); int c_width = c_widths.extract(16 * i, 16).as_int(false); @@ -191,7 +191,7 @@ struct Macc ci += c_width; term.do_subtract = (addend_neg[i] == State::S1); - ports.push_back(term); + terms.push_back(term); } log_assert(port_c.size() == ci); } @@ -205,23 +205,23 @@ struct Macc Const c_signed, c_widths, addend_negated; SigSpec a, b, c; - for (int i = 0; i < (int) ports.size(); i++) { - SigSpec term_a = ports[i].in_a, term_b = ports[i].in_b; + for (int i = 0; i < (int) terms.size(); i++) { + SigSpec term_a = terms[i].in_a, term_b = terms[i].in_b; if (term_b.empty()) { // addend c_widths.append(Const(term_a.size(), 16)); - c_signed.append(ports[i].is_signed ? RTLIL::S1 : RTLIL::S0); - addend_negated.append(ports[i].do_subtract ? RTLIL::S1 : RTLIL::S0); + c_signed.append(terms[i].is_signed ? RTLIL::S1 : RTLIL::S0); + addend_negated.append(terms[i].do_subtract ? RTLIL::S1 : RTLIL::S0); c.append(term_a); naddends++; } else { // product a_widths.append(Const(term_a.size(), 16)); b_widths.append(Const(term_b.size(), 16)); - a_signed.append(ports[i].is_signed ? RTLIL::S1 : RTLIL::S0); - b_signed.append(ports[i].is_signed ? RTLIL::S1 : RTLIL::S0); - product_negated.append(ports[i].do_subtract ? RTLIL::S1 : RTLIL::S0); + a_signed.append(terms[i].is_signed ? RTLIL::S1 : RTLIL::S0); + b_signed.append(terms[i].is_signed ? RTLIL::S1 : RTLIL::S0); + product_negated.append(terms[i].do_subtract ? RTLIL::S1 : RTLIL::S0); a.append(term_a); b.append(term_b); nproducts++; @@ -265,7 +265,7 @@ struct Macc for (auto &bit : result.bits()) bit = State::S0; - for (auto &port : ports) + for (auto &port : terms) { if (!port.in_a.is_fully_const() || !port.in_b.is_fully_const()) return false; @@ -287,9 +287,9 @@ struct Macc bool is_simple_product() { - return ports.size() == 1 && - !ports[0].in_b.empty() && - !ports[0].do_subtract; + return terms.size() == 1 && + !terms[0].in_b.empty() && + !terms[0].do_subtract; } Macc(RTLIL::Cell *cell = nullptr) diff --git a/kernel/satgen.cc b/kernel/satgen.cc index 9e5fa9111..13f243b14 100644 --- a/kernel/satgen.cc +++ b/kernel/satgen.cc @@ -750,7 +750,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) std::vector tmp(GetSize(y), ez->CONST_FALSE); - for (auto &port : macc.ports) + for (auto &port : macc.terms) { std::vector in_a = importDefSigSpec(port.in_a, timestep); std::vector in_b = importDefSigSpec(port.in_b, timestep); diff --git a/passes/opt/share.cc b/passes/opt/share.cc index 57a52969b..14e184a99 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -110,7 +110,7 @@ struct ShareWorker // Code for sharing and comparing MACC cells // --------------------------------------------------- - static int bits_macc_port(const Macc::port_t &p, int width) + static int bits_macc_term(const Macc::term_t &p, int width) { if (GetSize(p.in_a) == 0 || GetSize(p.in_b) == 0) return min(max(GetSize(p.in_a), GetSize(p.in_b)), width); @@ -120,8 +120,8 @@ struct ShareWorker static int bits_macc(const Macc &m, int width) { int bits = 0; - for (auto &p : m.ports) - bits += bits_macc_port(p, width); + for (auto &p : m.terms) + bits += bits_macc_term(p, width); return bits; } @@ -132,7 +132,7 @@ struct ShareWorker return bits_macc(m, width); } - static bool cmp_macc_ports(const Macc::port_t &p1, const Macc::port_t &p2) + static bool cmp_macc_ports(const Macc::term_t &p1, const Macc::term_t &p2) { bool mul1 = GetSize(p1.in_a) && GetSize(p1.in_b); bool mul2 = GetSize(p2.in_a) && GetSize(p2.in_b); @@ -161,7 +161,7 @@ struct ShareWorker return false; } - int share_macc_ports(Macc::port_t &p1, Macc::port_t &p2, int w1, int w2, + int share_macc_ports(Macc::term_t &p1, Macc::term_t &p2, int w1, int w2, RTLIL::SigSpec act = RTLIL::SigSpec(), Macc *supermacc = nullptr, pool *supercell_aux = nullptr) { if (p1.do_subtract != p2.do_subtract) @@ -216,12 +216,12 @@ struct ShareWorker supercell_aux->insert(module->addMux(NEW_ID, sig_b2, sig_b1, act, sig_b)); } - Macc::port_t p; + Macc::term_t p; p.in_a = sig_a; p.in_b = sig_b; p.is_signed = force_signed; p.do_subtract = p1.do_subtract; - supermacc->ports.push_back(p); + supermacc->terms.push_back(p); } int score = 1000 + abs(GetSize(p1.in_a) - GetSize(p2.in_a)) * max(abs(GetSize(p1.in_b) - GetSize(p2.in_b)), 1); @@ -248,15 +248,15 @@ struct ShareWorker m1.optimize(w1); m2.optimize(w2); - std::sort(m1.ports.begin(), m1.ports.end(), cmp_macc_ports); - std::sort(m2.ports.begin(), m2.ports.end(), cmp_macc_ports); + std::sort(m1.terms.begin(), m1.terms.end(), cmp_macc_ports); + std::sort(m2.terms.begin(), m2.terms.end(), cmp_macc_ports); std::set m1_unmapped, m2_unmapped; - for (int i = 0; i < GetSize(m1.ports); i++) + for (int i = 0; i < GetSize(m1.terms); i++) m1_unmapped.insert(i); - for (int i = 0; i < GetSize(m2.ports); i++) + for (int i = 0; i < GetSize(m2.terms); i++) m2_unmapped.insert(i); while (1) @@ -265,7 +265,7 @@ struct ShareWorker for (int i : m1_unmapped) for (int j : m2_unmapped) { - int score = share_macc_ports(m1.ports[i], m2.ports[j], w1, w2); + int score = share_macc_ports(m1.terms[i], m2.terms[j], w1, w2); if (score >= 0 && (best_i < 0 || best_score > score)) best_i = i, best_j = j, best_score = score; } @@ -273,55 +273,55 @@ struct ShareWorker if (best_i >= 0) { m1_unmapped.erase(best_i); m2_unmapped.erase(best_j); - share_macc_ports(m1.ports[best_i], m2.ports[best_j], w1, w2, act, &supermacc, supercell_aux); + share_macc_ports(m1.terms[best_i], m2.terms[best_j], w1, w2, act, &supermacc, supercell_aux); } else break; } for (int i : m1_unmapped) { - RTLIL::SigSpec sig_a = m1.ports[i].in_a; - RTLIL::SigSpec sig_b = m1.ports[i].in_b; + RTLIL::SigSpec sig_a = m1.terms[i].in_a; + RTLIL::SigSpec sig_b = m1.terms[i].in_b; if (supercell_aux && GetSize(sig_a)) { sig_a = module->addWire(NEW_ID, GetSize(sig_a)); - supercell_aux->insert(module->addMux(NEW_ID, RTLIL::SigSpec(0, GetSize(sig_a)), m1.ports[i].in_a, act, sig_a)); + supercell_aux->insert(module->addMux(NEW_ID, RTLIL::SigSpec(0, GetSize(sig_a)), m1.terms[i].in_a, act, sig_a)); } if (supercell_aux && GetSize(sig_b)) { sig_b = module->addWire(NEW_ID, GetSize(sig_b)); - supercell_aux->insert(module->addMux(NEW_ID, RTLIL::SigSpec(0, GetSize(sig_b)), m1.ports[i].in_b, act, sig_b)); + supercell_aux->insert(module->addMux(NEW_ID, RTLIL::SigSpec(0, GetSize(sig_b)), m1.terms[i].in_b, act, sig_b)); } - Macc::port_t p; + Macc::term_t p; p.in_a = sig_a; p.in_b = sig_b; - p.is_signed = m1.ports[i].is_signed; - p.do_subtract = m1.ports[i].do_subtract; - supermacc.ports.push_back(p); + p.is_signed = m1.terms[i].is_signed; + p.do_subtract = m1.terms[i].do_subtract; + supermacc.terms.push_back(p); } for (int i : m2_unmapped) { - RTLIL::SigSpec sig_a = m2.ports[i].in_a; - RTLIL::SigSpec sig_b = m2.ports[i].in_b; + RTLIL::SigSpec sig_a = m2.terms[i].in_a; + RTLIL::SigSpec sig_b = m2.terms[i].in_b; if (supercell_aux && GetSize(sig_a)) { sig_a = module->addWire(NEW_ID, GetSize(sig_a)); - supercell_aux->insert(module->addMux(NEW_ID, m2.ports[i].in_a, RTLIL::SigSpec(0, GetSize(sig_a)), act, sig_a)); + supercell_aux->insert(module->addMux(NEW_ID, m2.terms[i].in_a, RTLIL::SigSpec(0, GetSize(sig_a)), act, sig_a)); } if (supercell_aux && GetSize(sig_b)) { sig_b = module->addWire(NEW_ID, GetSize(sig_b)); - supercell_aux->insert(module->addMux(NEW_ID, m2.ports[i].in_b, RTLIL::SigSpec(0, GetSize(sig_b)), act, sig_b)); + supercell_aux->insert(module->addMux(NEW_ID, m2.terms[i].in_b, RTLIL::SigSpec(0, GetSize(sig_b)), act, sig_b)); } - Macc::port_t p; + Macc::term_t p; p.in_a = sig_a; p.in_b = sig_b; - p.is_signed = m2.ports[i].is_signed; - p.do_subtract = m2.ports[i].do_subtract; - supermacc.ports.push_back(p); + p.is_signed = m2.terms[i].is_signed; + p.do_subtract = m2.terms[i].do_subtract; + supermacc.terms.push_back(p); } if (supercell) diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc index 591c51c74..3dc54e8bb 100644 --- a/passes/techmap/alumacc.cc +++ b/passes/techmap/alumacc.cc @@ -142,7 +142,7 @@ struct AlumaccWorker log(" creating $macc model for %s (%s).\n", log_id(cell), log_id(cell->type)); maccnode_t *n = new maccnode_t; - Macc::port_t new_port; + Macc::term_t new_term; n->cell = cell; n->y = sigmap(cell->getPort(ID::Y)); @@ -153,32 +153,32 @@ struct AlumaccWorker if (cell->type.in(ID($pos), ID($neg))) { - new_port.in_a = sigmap(cell->getPort(ID::A)); - new_port.is_signed = cell->getParam(ID::A_SIGNED).as_bool(); - new_port.do_subtract = cell->type == ID($neg); - n->macc.ports.push_back(new_port); + new_term.in_a = sigmap(cell->getPort(ID::A)); + new_term.is_signed = cell->getParam(ID::A_SIGNED).as_bool(); + new_term.do_subtract = cell->type == ID($neg); + n->macc.terms.push_back(new_term); } if (cell->type.in(ID($add), ID($sub))) { - new_port.in_a = sigmap(cell->getPort(ID::A)); - new_port.is_signed = cell->getParam(ID::A_SIGNED).as_bool(); - new_port.do_subtract = false; - n->macc.ports.push_back(new_port); + new_term.in_a = sigmap(cell->getPort(ID::A)); + new_term.is_signed = cell->getParam(ID::A_SIGNED).as_bool(); + new_term.do_subtract = false; + n->macc.terms.push_back(new_term); - new_port.in_a = sigmap(cell->getPort(ID::B)); - new_port.is_signed = cell->getParam(ID::B_SIGNED).as_bool(); - new_port.do_subtract = cell->type == ID($sub); - n->macc.ports.push_back(new_port); + new_term.in_a = sigmap(cell->getPort(ID::B)); + new_term.is_signed = cell->getParam(ID::B_SIGNED).as_bool(); + new_term.do_subtract = cell->type == ID($sub); + n->macc.terms.push_back(new_term); } if (cell->type.in(ID($mul))) { - new_port.in_a = sigmap(cell->getPort(ID::A)); - new_port.in_b = sigmap(cell->getPort(ID::B)); - new_port.is_signed = cell->getParam(ID::A_SIGNED).as_bool(); - new_port.do_subtract = false; - n->macc.ports.push_back(new_port); + new_term.in_a = sigmap(cell->getPort(ID::A)); + new_term.in_b = sigmap(cell->getPort(ID::B)); + new_term.is_signed = cell->getParam(ID::A_SIGNED).as_bool(); + new_term.do_subtract = false; + n->macc.terms.push_back(new_term); } log_assert(sig_macc.count(n->y) == 0); @@ -190,7 +190,7 @@ struct AlumaccWorker { std::vector port_sizes; - for (auto &port : macc.ports) { + for (auto &port : macc.terms) { if (port.is_signed != is_signed) return true; if (!port.is_signed && port.do_subtract) @@ -235,9 +235,9 @@ struct AlumaccWorker if (delete_nodes.count(n)) continue; - for (int i = 0; i < GetSize(n->macc.ports); i++) + for (int i = 0; i < GetSize(n->macc.terms); i++) { - auto &port = n->macc.ports[i]; + auto &port = n->macc.terms[i]; if (GetSize(port.in_b) > 0 || sig_macc.count(port.in_a) == 0) continue; @@ -253,13 +253,13 @@ struct AlumaccWorker log(" merging $macc model for %s into %s.\n", log_id(other_n->cell), log_id(n->cell)); bool do_subtract = port.do_subtract; - for (int j = 0; j < GetSize(other_n->macc.ports); j++) { + for (int j = 0; j < GetSize(other_n->macc.terms); j++) { if (do_subtract) - other_n->macc.ports[j].do_subtract = !other_n->macc.ports[j].do_subtract; + other_n->macc.terms[j].do_subtract = !other_n->macc.terms[j].do_subtract; if (j == 0) - n->macc.ports[i--] = other_n->macc.ports[j]; + n->macc.terms[i--] = other_n->macc.terms[j]; else - n->macc.ports.push_back(other_n->macc.ports[j]); + n->macc.terms.push_back(other_n->macc.terms[j]); } delete_nodes.insert(other_n); @@ -288,7 +288,7 @@ struct AlumaccWorker bool subtract_b = false; alunode_t *alunode; - for (auto &port : n->macc.ports) + for (auto &port : n->macc.terms) if (GetSize(port.in_b) > 0) { goto next_macc; } else if (GetSize(port.in_a) == 1 && !port.is_signed && !port.do_subtract) { diff --git a/passes/techmap/booth.cc b/passes/techmap/booth.cc index cd9012e6c..a692b99fe 100644 --- a/passes/techmap/booth.cc +++ b/passes/techmap/booth.cc @@ -227,9 +227,9 @@ struct BoothPassWorker { continue; } - A = macc.ports[0].in_a; - B = macc.ports[0].in_b; - is_signed = macc.ports[0].is_signed; + A = macc.terms[0].in_a; + B = macc.terms[0].in_b; + is_signed = macc.terms[0].is_signed; Y = cell->getPort(ID::Y); } else { continue; diff --git a/passes/techmap/maccmap.cc b/passes/techmap/maccmap.cc index 3dde92438..42b615002 100644 --- a/passes/techmap/maccmap.cc +++ b/passes/techmap/maccmap.cc @@ -278,42 +278,42 @@ void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap) return; } - for (auto &port : macc.ports) - if (GetSize(port.in_b) == 0) - log(" %s %s (%d bits, %s)\n", port.do_subtract ? "sub" : "add", log_signal(port.in_a), - GetSize(port.in_a), port.is_signed ? "signed" : "unsigned"); + for (auto &term : macc.terms) + if (GetSize(term.in_b) == 0) + log(" %s %s (%d bits, %s)\n", term.do_subtract ? "sub" : "add", log_signal(term.in_a), + GetSize(term.in_a), term.is_signed ? "signed" : "unsigned"); else - log(" %s %s * %s (%dx%d bits, %s)\n", port.do_subtract ? "sub" : "add", log_signal(port.in_a), log_signal(port.in_b), - GetSize(port.in_a), GetSize(port.in_b), port.is_signed ? "signed" : "unsigned"); + log(" %s %s * %s (%dx%d bits, %s)\n", term.do_subtract ? "sub" : "add", log_signal(term.in_a), log_signal(term.in_b), + GetSize(term.in_a), GetSize(term.in_b), term.is_signed ? "signed" : "unsigned"); if (unmap) { typedef std::pair summand_t; std::vector summands; - RTLIL::SigSpec bit_ports; + RTLIL::SigSpec bit_terms; - for (auto &port : macc.ports) { + for (auto &term : macc.terms) { summand_t this_summand; - if (GetSize(port.in_b)) { + if (GetSize(term.in_b)) { this_summand.first = module->addWire(NEW_ID, width); - module->addMul(NEW_ID, port.in_a, port.in_b, this_summand.first, port.is_signed); - } else if (GetSize(port.in_a) == 1 && GetSize(port.in_b) == 0 && !port.is_signed && !port.do_subtract) { - // Mimic old 'bit_ports' treatment in case it's relevant for performance, + module->addMul(NEW_ID, term.in_a, term.in_b, this_summand.first, term.is_signed); + } else if (GetSize(term.in_a) == 1 && GetSize(term.in_b) == 0 && !term.is_signed && !term.do_subtract) { + // Mimic old 'bit_terms' treatment in case it's relevant for performance, // i.e. defer single-bit summands to be the last ones - bit_ports.append(port.in_a); + bit_terms.append(term.in_a); continue; - } else if (GetSize(port.in_a) != width) { + } else if (GetSize(term.in_a) != width) { this_summand.first = module->addWire(NEW_ID, width); - module->addPos(NEW_ID, port.in_a, this_summand.first, port.is_signed); + module->addPos(NEW_ID, term.in_a, this_summand.first, term.is_signed); } else { - this_summand.first = port.in_a; + this_summand.first = term.in_a; } - this_summand.second = port.do_subtract; + this_summand.second = term.do_subtract; summands.push_back(this_summand); } - for (auto &bit : bit_ports) + for (auto &bit : bit_terms) summands.push_back(summand_t(bit, false)); if (GetSize(summands) == 0) @@ -350,20 +350,20 @@ void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap) else { MaccmapWorker worker(module, width); - RTLIL::SigSpec bit_ports; + RTLIL::SigSpec bit_terms; - for (auto &port : macc.ports) { - // Mimic old 'bit_ports' treatment in case it's relevant for performance, + for (auto &term : macc.terms) { + // Mimic old 'bit_terms' treatment in case it's relevant for performance, // i.e. defer single-bit summands to be the last ones - if (GetSize(port.in_a) == 1 && GetSize(port.in_b) == 0 && !port.is_signed && !port.do_subtract) - bit_ports.append(port.in_a); - else if (GetSize(port.in_b) == 0) - worker.add(port.in_a, port.is_signed, port.do_subtract); + if (GetSize(term.in_a) == 1 && GetSize(term.in_b) == 0 && !term.is_signed && !term.do_subtract) + bit_terms.append(term.in_a); + else if (GetSize(term.in_b) == 0) + worker.add(term.in_a, term.is_signed, term.do_subtract); else - worker.add(port.in_a, port.in_b, port.is_signed, port.do_subtract); + worker.add(term.in_a, term.in_b, term.is_signed, term.do_subtract); } - for (auto bit : bit_ports) + for (auto bit : bit_terms) worker.add(bit, 0); module->connect(cell->getPort(ID::Y), worker.synth()); diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index 39b7ccd3a..a34eafc2f 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -189,17 +189,17 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce } else size_b = 0; - Macc::port_t this_port; + Macc::term_t this_term; wire_a->width += size_a; - this_port.in_a = RTLIL::SigSpec(wire_a, wire_a->width - size_a, size_a); + this_term.in_a = RTLIL::SigSpec(wire_a, wire_a->width - size_a, size_a); wire_a->width += size_b; - this_port.in_b = RTLIL::SigSpec(wire_a, wire_a->width - size_b, size_b); + this_term.in_b = RTLIL::SigSpec(wire_a, wire_a->width - size_b, size_b); - this_port.is_signed = xorshift32(2) == 1; - this_port.do_subtract = xorshift32(2) == 1; - macc.ports.push_back(this_port); + this_term.is_signed = xorshift32(2) == 1; + this_term.do_subtract = xorshift32(2) == 1; + macc.terms.push_back(this_term); } // Macc::to_cell sets the input ports macc.to_cell(cell); From 251285be4c086eba80cafe637029218bb2e8f2a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 18 Mar 2025 13:51:09 +0100 Subject: [PATCH 03/55] cost: Add `$mem_v2`, `$macc_v2` estimates --- kernel/cost.cc | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/kernel/cost.cc b/kernel/cost.cc index 5d897ab7a..985220f14 100644 --- a/kernel/cost.cc +++ b/kernel/cost.cc @@ -1,4 +1,5 @@ #include "kernel/cost.h" +#include "kernel/macc.h" USING_YOSYS_NAMESPACE @@ -148,6 +149,9 @@ unsigned int CellCosts::get(RTLIL::Cell *cell) log_assert(cell->hasPort(ID::Q) && "Weird flip flop"); log_debug("%s is ff\n", cell->name.c_str()); return cell->getParam(ID::WIDTH).as_int(); + } else if (cell->type.in(ID($mem), ID($mem_v2))) { + log_debug("%s is mem\n", cell->name.c_str()); + return cell->getParam(ID::WIDTH).as_int() * cell->getParam(ID::SIZE).as_int(); } else if (y_coef(cell->type)) { // linear with Y_WIDTH or WIDTH log_assert((cell->hasParam(ID::Y_WIDTH) || cell->hasParam(ID::WIDTH)) && "Unknown width"); @@ -173,6 +177,22 @@ unsigned int CellCosts::get(RTLIL::Cell *cell) unsigned int coef = cell->type == ID($mul) ? 3 : 5; log_debug("%s coef*(sum**2) %d * %d\n", cell->name.c_str(), coef, sum * sum); return coef * sum * sum; + } else if (cell->type.in(ID($macc), ID($macc_v2))) { + // quadratic per term + unsigned int cost_sum = 0; + Macc macc; + macc.from_cell(cell); + unsigned int y_width = cell->getParam(ID::Y_WIDTH).as_int(); + for (auto &term: macc.terms) { + if (term.in_b.empty()) { + // neglect addends + continue; + } + unsigned a_width = term.in_a.size(), b_width = term.in_b.size(); + unsigned int sum = a_width + b_width + std::min(y_width, a_width + b_width); + cost_sum += 3 * sum * sum; + } + return cost_sum; } else if (cell->type == ID($lut)) { int width = cell->getParam(ID::WIDTH).as_int(); unsigned int cost = 1U << (unsigned int)width; @@ -187,8 +207,8 @@ unsigned int CellCosts::get(RTLIL::Cell *cell) log_debug("%s is free\n", cell->name.c_str()); return 0; } - // TODO: $fsm $mem.* $macc - // ignored: $pow + // TODO: $fsm + // ignored: $pow $memrd $memwr $meminit (and v2 counterparts) log_warning("Can't determine cost of %s cell (%d parameters).\n", log_id(cell->type), GetSize(cell->parameters)); return 1; From 1113c8c95a568740497d2a5f5497d1d3b592a2c9 Mon Sep 17 00:00:00 2001 From: Kelvin Chung Date: Fri, 28 Feb 2025 19:35:47 +0000 Subject: [PATCH 04/55] feat: Allow full constant wrapping for hilomap --- passes/techmap/hilomap.cc | 53 ++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/passes/techmap/hilomap.cc b/passes/techmap/hilomap.cc index c1b947221..f342db340 100644 --- a/passes/techmap/hilomap.cc +++ b/passes/techmap/hilomap.cc @@ -26,29 +26,41 @@ PRIVATE_NAMESPACE_BEGIN static std::string hicell_celltype, hicell_portname; static std::string locell_celltype, locell_portname; +static std::string fullcell_celltype, fullcell_portname, fullcell_paramname; static bool singleton_mode; +static bool multi_bit; static RTLIL::Module *module; static RTLIL::SigBit last_hi, last_lo; +static RTLIL::SigChunk value; void hilomap_worker(RTLIL::SigSpec &sig) { - for (auto &bit : sig) { - if (bit == RTLIL::State::S1 && !hicell_celltype.empty()) { - if (!singleton_mode || last_hi == RTLIL::State::Sm) { - last_hi = module->addWire(NEW_ID); - RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(hicell_celltype)); - cell->setPort(RTLIL::escape_id(hicell_portname), last_hi); + if (multi_bit && sig.is_fully_const()){ + value = module->addWire(NEW_ID, sig.size()); + RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(fullcell_celltype)); + cell->setParam(RTLIL::escape_id(fullcell_paramname), sig.as_const()); + cell->setPort(RTLIL::escape_id(fullcell_portname), value); + sig = value; + } + else{ + for (auto &bit : sig) { + if (bit == RTLIL::State::S1 && !hicell_celltype.empty()) { + if (!singleton_mode || last_hi == RTLIL::State::Sm) { + last_hi = module->addWire(NEW_ID); + RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(hicell_celltype)); + cell->setPort(RTLIL::escape_id(hicell_portname), last_hi); + } + bit = last_hi; } - bit = last_hi; - } - if (bit == RTLIL::State::S0 && !locell_celltype.empty()) { - if (!singleton_mode || last_lo == RTLIL::State::Sm) { - last_lo = module->addWire(NEW_ID); - RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(locell_celltype)); - cell->setPort(RTLIL::escape_id(locell_portname), last_lo); + if (bit == RTLIL::State::S0 && !locell_celltype.empty()) { + if (!singleton_mode || last_lo == RTLIL::State::Sm) { + last_lo = module->addWire(NEW_ID); + RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(locell_celltype)); + cell->setPort(RTLIL::escape_id(locell_portname), last_lo); + } + bit = last_lo; } - bit = last_lo; } } } @@ -68,6 +80,10 @@ struct HilomapPass : public Pass { log(" -locell \n"); log(" Replace constant lo bits with this cell.\n"); log("\n"); + log(" -wrap \n"); + log(" Replace constant bits with this cell.\n"); + log(" The value of the constant will be stored to the parameter specified.\n"); + log("\n"); log(" -singleton\n"); log(" Create only one hi/lo cell and connect all constant bits\n"); log(" to that cell. Per default a separate cell is created for\n"); @@ -83,7 +99,7 @@ struct HilomapPass : public Pass { locell_celltype = std::string(); locell_portname = std::string(); singleton_mode = false; - + multi_bit = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -97,6 +113,13 @@ struct HilomapPass : public Pass { locell_portname = args[++argidx]; continue; } + if (args[argidx] == "-wrap" && argidx+3 < args.size()){ + fullcell_celltype = args[++argidx]; + fullcell_portname = args[++argidx]; + fullcell_paramname = args[++argidx]; + multi_bit = true; + continue; + } if (args[argidx] == "-singleton") { singleton_mode = true; continue; From 7bbdf6049aeced309a2fe7e10f2f60b6cd7dd596 Mon Sep 17 00:00:00 2001 From: Kelvin Chung Date: Wed, 26 Mar 2025 11:52:55 +0000 Subject: [PATCH 05/55] Move implementation to constmap and add test --- passes/techmap/Makefile.inc | 1 + passes/techmap/constmap.cc | 82 +++++++++++++++++++++++++++++++++++++ passes/techmap/hilomap.cc | 53 +++++++----------------- tests/techmap/constmap.ys | 31 ++++++++++++++ 4 files changed, 129 insertions(+), 38 deletions(-) create mode 100644 passes/techmap/constmap.cc create mode 100644 tests/techmap/constmap.ys diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 4e1d16744..bd18bd826 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -53,6 +53,7 @@ OBJS += passes/techmap/flowmap.o OBJS += passes/techmap/extractinv.o OBJS += passes/techmap/cellmatch.o OBJS += passes/techmap/clockgate.o +OBJS += passes/techmap/constmap.o endif ifeq ($(DISABLE_SPAWN),0) diff --git a/passes/techmap/constmap.cc b/passes/techmap/constmap.cc new file mode 100644 index 000000000..59bffbeb6 --- /dev/null +++ b/passes/techmap/constmap.cc @@ -0,0 +1,82 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2025 King Lok Chung + * + * 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/rtlil.h" +#include "kernel/log.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +static std::string celltype, cell_portname, cell_paramname; + +static RTLIL::Module *module; +static RTLIL::SigChunk value; + +void constmap_worker(RTLIL::SigSpec &sig) +{ + if (sig.is_fully_const()){ + value = module->addWire(NEW_ID, sig.size()); + RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype)); + cell->setParam(RTLIL::escape_id(cell_paramname), sig.as_const()); + cell->setPort(RTLIL::escape_id(cell_portname), value); + sig = value; + } +} + +struct ConstmapPass : public Pass { + ConstmapPass() : Pass("constmap", "technology mapping of coarse constant value") { } + void help() override + { + log("\n"); + log(" constmap [options] [selection]\n"); + log("\n"); + log("Map constants to a driver cell.\n"); + log("\n"); + log(" -cell \n"); + log(" Replace constant bits with this cell.\n"); + log(" The value of the constant will be stored to the parameter specified.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing CONSTMAP pass (mapping to constant driver).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-cell" && argidx+3 < args.size()){ + celltype = args[++argidx]; + cell_portname = args[++argidx]; + cell_paramname = args[++argidx]; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto mod : design->selected_modules()) + { + module = mod; + module->rewrite_sigspecs(constmap_worker); + } + } +} HilomapPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/hilomap.cc b/passes/techmap/hilomap.cc index f342db340..c1b947221 100644 --- a/passes/techmap/hilomap.cc +++ b/passes/techmap/hilomap.cc @@ -26,41 +26,29 @@ PRIVATE_NAMESPACE_BEGIN static std::string hicell_celltype, hicell_portname; static std::string locell_celltype, locell_portname; -static std::string fullcell_celltype, fullcell_portname, fullcell_paramname; static bool singleton_mode; -static bool multi_bit; static RTLIL::Module *module; static RTLIL::SigBit last_hi, last_lo; -static RTLIL::SigChunk value; void hilomap_worker(RTLIL::SigSpec &sig) { - if (multi_bit && sig.is_fully_const()){ - value = module->addWire(NEW_ID, sig.size()); - RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(fullcell_celltype)); - cell->setParam(RTLIL::escape_id(fullcell_paramname), sig.as_const()); - cell->setPort(RTLIL::escape_id(fullcell_portname), value); - sig = value; - } - else{ - for (auto &bit : sig) { - if (bit == RTLIL::State::S1 && !hicell_celltype.empty()) { - if (!singleton_mode || last_hi == RTLIL::State::Sm) { - last_hi = module->addWire(NEW_ID); - RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(hicell_celltype)); - cell->setPort(RTLIL::escape_id(hicell_portname), last_hi); - } - bit = last_hi; + for (auto &bit : sig) { + if (bit == RTLIL::State::S1 && !hicell_celltype.empty()) { + if (!singleton_mode || last_hi == RTLIL::State::Sm) { + last_hi = module->addWire(NEW_ID); + RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(hicell_celltype)); + cell->setPort(RTLIL::escape_id(hicell_portname), last_hi); } - if (bit == RTLIL::State::S0 && !locell_celltype.empty()) { - if (!singleton_mode || last_lo == RTLIL::State::Sm) { - last_lo = module->addWire(NEW_ID); - RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(locell_celltype)); - cell->setPort(RTLIL::escape_id(locell_portname), last_lo); - } - bit = last_lo; + bit = last_hi; + } + if (bit == RTLIL::State::S0 && !locell_celltype.empty()) { + if (!singleton_mode || last_lo == RTLIL::State::Sm) { + last_lo = module->addWire(NEW_ID); + RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(locell_celltype)); + cell->setPort(RTLIL::escape_id(locell_portname), last_lo); } + bit = last_lo; } } } @@ -80,10 +68,6 @@ struct HilomapPass : public Pass { log(" -locell \n"); log(" Replace constant lo bits with this cell.\n"); log("\n"); - log(" -wrap \n"); - log(" Replace constant bits with this cell.\n"); - log(" The value of the constant will be stored to the parameter specified.\n"); - log("\n"); log(" -singleton\n"); log(" Create only one hi/lo cell and connect all constant bits\n"); log(" to that cell. Per default a separate cell is created for\n"); @@ -99,7 +83,7 @@ struct HilomapPass : public Pass { locell_celltype = std::string(); locell_portname = std::string(); singleton_mode = false; - multi_bit = false; + size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -113,13 +97,6 @@ struct HilomapPass : public Pass { locell_portname = args[++argidx]; continue; } - if (args[argidx] == "-wrap" && argidx+3 < args.size()){ - fullcell_celltype = args[++argidx]; - fullcell_portname = args[++argidx]; - fullcell_paramname = args[++argidx]; - multi_bit = true; - continue; - } if (args[argidx] == "-singleton") { singleton_mode = true; continue; diff --git a/tests/techmap/constmap.ys b/tests/techmap/constmap.ys new file mode 100644 index 000000000..ceac0e570 --- /dev/null +++ b/tests/techmap/constmap.ys @@ -0,0 +1,31 @@ +read_verilog << EOT + +module test(); + wire [31:0] in; + wire [31:0] out; + assign out = in + 16; +endmodule + +EOT + +constmap -cell const_cell O value +select -assert-count 1 t:const_cell +select -assert-count 1 r:value=16 + +design -reset +read_verilog << EOT + +module test(); + wire [31:0] in; + wire [31:0] out1; + wire [31:0] out2; + assign out1 = in + 16; + assign out2 = in + 32; +endmodule + +EOT + +constmap -cell const_cell O value +select -assert-count 2 t:const_cell +select -assert-count 1 r:value=16 +select -assert-count 1 r:value=32 From a0dabf9203139588e94e793cf17c2e8a2dfec51d Mon Sep 17 00:00:00 2001 From: Kelvin Chung Date: Wed, 26 Mar 2025 22:24:41 +0000 Subject: [PATCH 06/55] Add extra test --- tests/techmap/constmap.ys | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/techmap/constmap.ys b/tests/techmap/constmap.ys index ceac0e570..2854f2e57 100644 --- a/tests/techmap/constmap.ys +++ b/tests/techmap/constmap.ys @@ -13,6 +13,13 @@ select -assert-count 1 t:const_cell select -assert-count 1 r:value=16 design -reset + +read_verilog -lib << EOT +module const_cell(O); +output O; +endmodule +EOT + read_verilog << EOT module test(); @@ -29,3 +36,5 @@ constmap -cell const_cell O value select -assert-count 2 t:const_cell select -assert-count 1 r:value=16 select -assert-count 1 r:value=32 +select -assert-count 1 test/out1 %ci* r:value=16 %i +select -assert-count 1 test/out2 %ci* r:value=32 %i From 0a6d9f4dc993dc8d7d68204681ed83e56fa9c77a Mon Sep 17 00:00:00 2001 From: Sean Luchen Date: Mon, 31 Mar 2025 10:44:23 -0700 Subject: [PATCH 07/55] Factor report_unexpected_token out into its own function. --- passes/techmap/libparse.cc | 69 ++++++++++++++++---------------------- passes/techmap/libparse.h | 1 + 2 files changed, 30 insertions(+), 40 deletions(-) diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 06dd6288e..afc49b454 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -332,6 +332,32 @@ int LibertyParser::lexer(std::string &str) return c; } +void LibertyParser::report_unexpected_token(int tok) +{ + std::string eReport; + switch(tok) + { + case 'n': + error("Unexpected newline."); + break; + case '[': + case ']': + case '}': + case '{': + case '\"': + case ':': + eReport = "Unexpected '"; + eReport += static_cast(tok); + eReport += "'."; + error(eReport); + break; + default: + eReport = "Unexpected token: "; + eReport += static_cast(tok); + error(eReport); + } +} + LibertyAst *LibertyParser::parse() { std::string str; @@ -350,26 +376,7 @@ LibertyAst *LibertyParser::parse() return NULL; if (tok != 'v') { - std::string eReport; - switch(tok) - { - case 'n': - error("Unexpected newline."); - break; - case '[': - case ']': - case '}': - case '{': - case '\"': - case ':': - eReport = "Unexpected '"; - eReport += static_cast(tok); - eReport += "'."; - error(eReport); - break; - default: - error(); - } + report_unexpected_token(tok); } LibertyAst *ast = new LibertyAst; @@ -460,25 +467,7 @@ LibertyAst *LibertyParser::parse() continue; } if (tok != 'v') { - std::string eReport; - switch(tok) - { - case 'n': - continue; - case '[': - case ']': - case '}': - case '{': - case '\"': - case ':': - eReport = "Unexpected '"; - eReport += static_cast(tok); - eReport += "'."; - error(eReport); - break; - default: - error(); - } + report_unexpected_token(tok); } ast->args.push_back(arg); } @@ -495,7 +484,7 @@ LibertyAst *LibertyParser::parse() break; } - error(); + report_unexpected_token(tok); } return ast; diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index 16808fc58..ea81abe2f 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -105,6 +105,7 @@ namespace Yosys */ int lexer(std::string &str); + void report_unexpected_token(int tok); LibertyAst *parse(); void error() const; void error(const std::string &str) const; From ac1033ecd554b5685f89167ce240ab6fee60ca4c Mon Sep 17 00:00:00 2001 From: Sean Luchen Date: Mon, 31 Mar 2025 10:46:18 -0700 Subject: [PATCH 08/55] Factor parse_vector_range out into its own function. This also fixes the parsing a bit. It was consuming 1 fewer token than required. --- passes/techmap/libparse.cc | 80 ++++++++++++++++++++------------------ passes/techmap/libparse.h | 1 + 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index afc49b454..09afcf9f3 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -358,6 +358,48 @@ void LibertyParser::report_unexpected_token(int tok) } } +// FIXME: the AST needs to be extended to store +// these vector ranges. +int LibertyParser::parse_vector_range(int tok) +{ + // parse vector range [A] or [A:B] + std::string arg; + tok = lexer(arg); + if (tok != 'v') + { + // expected a vector array index + error("Expected a number."); + } + else + { + // fixme: check for number A + } + tok = lexer(arg); + // optionally check for : in case of [A:B] + // if it isn't we just expect ']' + // as we have [A] + if (tok == ':') + { + tok = lexer(arg); + if (tok != 'v') + { + // expected a vector array index + error("Expected a number."); + } + else + { + // fixme: check for number B + tok = lexer(arg); + } + } + // expect a closing bracket of array range + if (tok != ']') + { + error("Expected ']' on array range."); + } + return lexer(arg); +} + LibertyAst *LibertyParser::parse() { std::string str; @@ -425,45 +467,9 @@ LibertyAst *LibertyParser::parse() if (tok == ')') break; - // FIXME: the AST needs to be extended to store - // these vector ranges. if (tok == '[') { - // parse vector range [A] or [A:B] - std::string arg; - tok = lexer(arg); - if (tok != 'v') - { - // expected a vector array index - error("Expected a number."); - } - else - { - // fixme: check for number A - } - tok = lexer(arg); - // optionally check for : in case of [A:B] - // if it isn't we just expect ']' - // as we have [A] - if (tok == ':') - { - tok = lexer(arg); - if (tok != 'v') - { - // expected a vector array index - error("Expected a number."); - } - else - { - // fixme: check for number B - tok = lexer(arg); - } - } - // expect a closing bracket of array range - if (tok != ']') - { - error("Expected ']' on array range."); - } + tok = parse_vector_range(tok); continue; } if (tok != 'v') { diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index ea81abe2f..686a2b49f 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -106,6 +106,7 @@ namespace Yosys int lexer(std::string &str); void report_unexpected_token(int tok); + int parse_vector_range(int tok); LibertyAst *parse(); void error() const; void error(const std::string &str) const; From 23f59e0196fd6275f1f6267f59ed7f358704c9f5 Mon Sep 17 00:00:00 2001 From: Sean Luchen Date: Mon, 31 Mar 2025 10:47:39 -0700 Subject: [PATCH 09/55] Support array ranges for identifiers in the Liberty parser. This change only handles the case `id : id[range] ;`. --- passes/techmap/libparse.cc | 4 +++- passes/techmap/libparse.h | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 09afcf9f3..d85e9d915 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -436,7 +436,9 @@ LibertyAst *LibertyParser::parse() if (tok == ':' && ast->value.empty()) { tok = lexer(ast->value); if (tok == 'v') { - tok = lexer(str); + tok = lexer(str); + if (tok == '[') + tok = parse_vector_range(tok); } while (tok == '+' || tok == '-' || tok == '*' || tok == '/' || tok == '!') { ast->value += tok; diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index 686a2b49f..61ae4d334 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -105,8 +105,8 @@ namespace Yosys */ int lexer(std::string &str); - void report_unexpected_token(int tok); - int parse_vector_range(int tok); + void report_unexpected_token(int tok); + int parse_vector_range(int tok); LibertyAst *parse(); void error() const; void error(const std::string &str) const; From 4610889d27627fdd6dc01518e07fd617e317eeac Mon Sep 17 00:00:00 2001 From: Sean Luchen Date: Tue, 1 Apr 2025 13:01:00 -0700 Subject: [PATCH 10/55] Fix two parsing bugs that were causing private regression tests to fail. These were introduced by 0a6d9f4. 1) While in a paren "(", don't error on newline. 2) Don't parse an extra token when parsing vector ranges. Let the caller parse the next token as necessary. --- passes/techmap/libparse.cc | 13 ++++++++----- passes/techmap/libparse.h | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index d85e9d915..e5ed094b3 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -360,7 +360,7 @@ void LibertyParser::report_unexpected_token(int tok) // FIXME: the AST needs to be extended to store // these vector ranges. -int LibertyParser::parse_vector_range(int tok) +void LibertyParser::parse_vector_range(int tok) { // parse vector range [A] or [A:B] std::string arg; @@ -397,7 +397,6 @@ int LibertyParser::parse_vector_range(int tok) { error("Expected ']' on array range."); } - return lexer(arg); } LibertyAst *LibertyParser::parse() @@ -437,8 +436,10 @@ LibertyAst *LibertyParser::parse() tok = lexer(ast->value); if (tok == 'v') { tok = lexer(str); - if (tok == '[') - tok = parse_vector_range(tok); + if (tok == '[') { + parse_vector_range(tok); + tok = lexer(str); + } } while (tok == '+' || tok == '-' || tok == '*' || tok == '/' || tok == '!') { ast->value += tok; @@ -471,9 +472,11 @@ LibertyAst *LibertyParser::parse() if (tok == '[') { - tok = parse_vector_range(tok); + parse_vector_range(tok); continue; } + if (tok == 'n') + continue; if (tok != 'v') { report_unexpected_token(tok); } diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index 61ae4d334..2f0678513 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -106,7 +106,7 @@ namespace Yosys int lexer(std::string &str); void report_unexpected_token(int tok); - int parse_vector_range(int tok); + void parse_vector_range(int tok); LibertyAst *parse(); void error() const; void error(const std::string &str) const; From bdcbbf2db63297b7f137b0a3371c666f1000a42f Mon Sep 17 00:00:00 2001 From: Sean Luchen Date: Thu, 3 Apr 2025 09:56:24 -0700 Subject: [PATCH 11/55] Fix existing tests/liberty tests, and add them to Makefile. --- Makefile | 1 + tests/liberty/XNOR2X1.lib.filtered.ok | 2 +- tests/liberty/XNOR2X1.lib.verilogsim.ok | 2 +- tests/liberty/normal.lib.filtered.ok | 50 ++++++++-------- tests/liberty/normal.lib.verilogsim.ok | 60 +++++++++---------- tests/liberty/run-test.sh | 5 +- tests/liberty/semicolextra.lib.filtered.ok | 4 +- tests/liberty/semicolextra.lib.verilogsim.ok | 2 +- tests/liberty/semicolmissing.lib.filtered.ok | 4 +- .../liberty/semicolmissing.lib.verilogsim.ok | 4 +- 10 files changed, 68 insertions(+), 66 deletions(-) diff --git a/Makefile b/Makefile index 74128b41d..340186470 100644 --- a/Makefile +++ b/Makefile @@ -889,6 +889,7 @@ SH_TEST_DIRS += tests/rpc SH_TEST_DIRS += tests/memfile SH_TEST_DIRS += tests/fmt SH_TEST_DIRS += tests/cxxrtl +SH_TEST_DIRS += tests/liberty ifeq ($(ENABLE_FUNCTIONAL_TESTS),1) SH_TEST_DIRS += tests/functional endif diff --git a/tests/liberty/XNOR2X1.lib.filtered.ok b/tests/liberty/XNOR2X1.lib.filtered.ok index 14e9b625d..5ca81b2d7 100644 --- a/tests/liberty/XNOR2X1.lib.filtered.ok +++ b/tests/liberty/XNOR2X1.lib.filtered.ok @@ -9,7 +9,7 @@ library(ls05_stdcells) { } pin(Y) { direction : output ; - function : !(B&!A|!B&A) ; + function : "!(B&!A|!B&A)" ; } } } diff --git a/tests/liberty/XNOR2X1.lib.verilogsim.ok b/tests/liberty/XNOR2X1.lib.verilogsim.ok index 62c9f7ffa..89e55e8b8 100644 --- a/tests/liberty/XNOR2X1.lib.verilogsim.ok +++ b/tests/liberty/XNOR2X1.lib.verilogsim.ok @@ -2,5 +2,5 @@ module XNOR2X1 (B, A, Y); input B; input A; output Y; - assign Y = !(B&!A|!B&A); // !(B&!A|!B&A) + assign Y = !(B&!A|!B&A); // "!(B&!A|!B&A)" endmodule diff --git a/tests/liberty/normal.lib.filtered.ok b/tests/liberty/normal.lib.filtered.ok index 757f09cab..eea7df8fe 100644 --- a/tests/liberty/normal.lib.filtered.ok +++ b/tests/liberty/normal.lib.filtered.ok @@ -6,7 +6,7 @@ library(supergate) { } pin(Y) { direction : output ; - function : A' ; + function : "A'" ; } } cell(tri_inv) { @@ -19,7 +19,7 @@ library(supergate) { } pin(Z) { direction : output ; - function : A' ; + function : "A'" ; } } cell(buffer) { @@ -29,7 +29,7 @@ library(supergate) { } pin(Y) { direction : output ; - function : A ; + function : "A" ; } } cell(nand2) { @@ -42,7 +42,7 @@ library(supergate) { } pin(Y) { direction : output ; - function : (A * B)' ; + function : "(A * B)'" ; } } cell(nor2) { @@ -55,7 +55,7 @@ library(supergate) { } pin(Y) { direction : output ; - function : (A + B)' ; + function : "(A + B)'" ; } } cell(xor2) { @@ -68,7 +68,7 @@ library(supergate) { } pin(Y) { direction : output ; - function : (A *B') + (A' * B) ; + function : "(A *B') + (A' * B)" ; } } cell(imux2) { @@ -84,16 +84,16 @@ library(supergate) { } pin(Y) { direction : output ; - function : ( (A * S) + (B * S') )' ; + function : "( (A * S) + (B * S') )'" ; } } cell(dff) { area : 6 ; - ff(IQ, IQN) { - next_state : D ; - clocked_on : CLK ; - clear : RESET ; - preset : PRESET ; + ff("IQ", "IQN") { + next_state : "D" ; + clocked_on : "CLK" ; + clear : "RESET" ; + preset : "PRESET" ; clear_preset_var1 : L ; clear_preset_var2 : L ; } @@ -111,18 +111,18 @@ library(supergate) { } pin(Q) { direction : output ; - function : IQ ; + function : "IQ" ; } pin(QN) { direction : output ; - function : IQN ; + function : "IQN" ; } } cell(latch) { area : 5 ; - latch(IQ, IQN) { - enable : G ; - data_in : D ; + latch("IQ", "IQN") { + enable : "G" ; + data_in : "D" ; } pin(D) { direction : input ; @@ -132,11 +132,11 @@ library(supergate) { } pin(Q) { direction : output ; - function : IQ ; + function : "IQ" ; } pin(QN) { direction : output ; - function : IQN ; + function : "IQN" ; } } cell(aoi211) { @@ -152,7 +152,7 @@ library(supergate) { } pin(Y) { direction : output ; - function : ((A * B) + C)' ; + function : "((A * B) + C)'" ; } } cell(oai211) { @@ -168,7 +168,7 @@ library(supergate) { } pin(Y) { direction : output ; - function : ((A + B) * C)' ; + function : "((A + B) * C)'" ; } } cell(halfadder) { @@ -181,11 +181,11 @@ library(supergate) { } pin(C) { direction : output ; - function : (A * B) ; + function : "(A * B)" ; } pin(Y) { direction : output ; - function : (A *B') + (A' * B) ; + function : "(A *B') + (A' * B)" ; } } cell(fulladder) { @@ -201,11 +201,11 @@ library(supergate) { } pin(CO) { direction : output ; - function : (((A * B)+(B * CI))+(CI * A)) ; + function : "(((A * B)+(B * CI))+(CI * A))" ; } pin(Y) { direction : output ; - function : ((A^B)^CI) ; + function : "((A^B)^CI)" ; } } } diff --git a/tests/liberty/normal.lib.verilogsim.ok b/tests/liberty/normal.lib.verilogsim.ok index 30f164a31..85aed5f4e 100644 --- a/tests/liberty/normal.lib.verilogsim.ok +++ b/tests/liberty/normal.lib.verilogsim.ok @@ -1,86 +1,86 @@ module inv (A, Y); input A; output Y; - assign Y = ~A; // A' + assign Y = ~A; // "A'" endmodule module tri_inv (A, S, Z); input A; input S; output Z; - assign Z = ~A; // A' + assign Z = ~A; // "A'" endmodule module buffer (A, Y); input A; output Y; - assign Y = A; // A + assign Y = A; // "A" endmodule module nand2 (A, B, Y); input A; input B; output Y; - assign Y = ~(A&B); // (A * B)' + assign Y = ~(A&B); // "(A * B)'" endmodule module nor2 (A, B, Y); input A; input B; output Y; - assign Y = ~(A|B); // (A + B)' + assign Y = ~(A|B); // "(A + B)'" endmodule module xor2 (A, B, Y); input A; input B; output Y; - assign Y = (A&~B)|(~A&B); // (A *B') + (A' * B) + assign Y = (A&~B)|(~A&B); // "(A *B') + (A' * B)" endmodule module imux2 (A, B, S, Y); input A; input B; input S; output Y; - assign Y = ~(&(A&S)|(B&~S)&); // ( (A * S) + (B * S') )' + assign Y = ~(&(A&S)|(B&~S)&); // "( (A * S) + (B * S') )'" endmodule module dff (D, CLK, RESET, PRESET, Q, QN); - reg IQ, IQN; + reg "IQ", "IQN"; input D; input CLK; input RESET; input PRESET; output Q; - assign Q = IQ; // IQ + assign Q = IQ; // "IQ" output QN; - assign QN = IQN; // IQN + assign QN = IQN; // "IQN" always @(posedge CLK, posedge RESET, posedge PRESET) begin if ((RESET) && (PRESET)) begin - IQ <= 0; - IQN <= 0; + "IQ" <= 0; + "IQN" <= 0; end else if (RESET) begin - IQ <= 0; - IQN <= 1; + "IQ" <= 0; + "IQN" <= 1; end else if (PRESET) begin - IQ <= 1; - IQN <= 0; + "IQ" <= 1; + "IQN" <= 0; end else begin - // D - IQ <= D; - IQN <= ~(D); + // "D" + "IQ" <= D; + "IQN" <= ~(D); end end endmodule module latch (D, G, Q, QN); - reg IQ, IQN; + reg "IQ", "IQN"; input D; input G; output Q; - assign Q = IQ; // IQ + assign Q = IQ; // "IQ" output QN; - assign QN = IQN; // IQN + assign QN = IQN; // "IQN" always @* begin if (G) begin - IQ <= D; - IQN <= ~(D); + "IQ" <= D; + "IQN" <= ~(D); end end endmodule @@ -89,29 +89,29 @@ module aoi211 (A, B, C, Y); input B; input C; output Y; - assign Y = ~((A&B)|C); // ((A * B) + C)' + assign Y = ~((A&B)|C); // "((A * B) + C)'" endmodule module oai211 (A, B, C, Y); input A; input B; input C; output Y; - assign Y = ~((A|B)&C); // ((A + B) * C)' + assign Y = ~((A|B)&C); // "((A + B) * C)'" endmodule module halfadder (A, B, C, Y); input A; input B; output C; - assign C = (A&B); // (A * B) + assign C = (A&B); // "(A * B)" output Y; - assign Y = (A&~B)|(~A&B); // (A *B') + (A' * B) + assign Y = (A&~B)|(~A&B); // "(A *B') + (A' * B)" endmodule module fulladder (A, B, CI, CO, Y); input A; input B; input CI; output CO; - assign CO = (((A&B)|(B&CI))|(CI&A)); // (((A * B)+(B * CI))+(CI * A)) + assign CO = (((A&B)|(B&CI))|(CI&A)); // "(((A * B)+(B * CI))+(CI * A))" output Y; - assign Y = ((A^B)^CI); // ((A^B)^CI) + assign Y = ((A^B)^CI); // "((A^B)^CI)" endmodule diff --git a/tests/liberty/run-test.sh b/tests/liberty/run-test.sh index ff5b20d74..8fa99d419 100755 --- a/tests/liberty/run-test.sh +++ b/tests/liberty/run-test.sh @@ -7,9 +7,10 @@ for x in *.lib; do ../../yosys-filterlib - $x 2>/dev/null > $x.filtered ../../yosys-filterlib -verilogsim $x > $x.verilogsim diff $x.filtered $x.filtered.ok && diff $x.verilogsim $x.verilogsim.ok -done +done || exit 1 for x in *.ys; do echo "Running $x.." ../../yosys -q -s $x -l ${x%.ys}.log -done +done || exit 1 + diff --git a/tests/liberty/semicolextra.lib.filtered.ok b/tests/liberty/semicolextra.lib.filtered.ok index 791b45084..10f9618cb 100644 --- a/tests/liberty/semicolextra.lib.filtered.ok +++ b/tests/liberty/semicolextra.lib.filtered.ok @@ -10,8 +10,8 @@ library(supergate) { clock : true ; } ff(IQ, IQN) { - clocked_on : CK ; - next_state : D ; + clocked_on : "CK" ; + next_state : "D" ; } pin(Q) { direction : output ; diff --git a/tests/liberty/semicolextra.lib.verilogsim.ok b/tests/liberty/semicolextra.lib.verilogsim.ok index c9eebd6ed..e3b14dbd2 100644 --- a/tests/liberty/semicolextra.lib.verilogsim.ok +++ b/tests/liberty/semicolextra.lib.verilogsim.ok @@ -4,7 +4,7 @@ module DFF (D, CK, Q); input CK; output Q; always @(posedge CK) begin - // D + // "D" IQ <= D; IQN <= ~(D); end diff --git a/tests/liberty/semicolmissing.lib.filtered.ok b/tests/liberty/semicolmissing.lib.filtered.ok index 29022cf80..2a00cc136 100644 --- a/tests/liberty/semicolmissing.lib.filtered.ok +++ b/tests/liberty/semicolmissing.lib.filtered.ok @@ -12,11 +12,11 @@ library(supergate) { } pin(CO) { direction : output ; - function : (((A * B)+(B * CI))+(CI * A)) ; + function : "(((A * B)+(B * CI))+(CI * A))" ; } pin(Y) { direction : output ; - function : ((A^B)^CI) ; + function : "((A^B)^CI)" ; } } } diff --git a/tests/liberty/semicolmissing.lib.verilogsim.ok b/tests/liberty/semicolmissing.lib.verilogsim.ok index 131ce2fdf..56d57209d 100644 --- a/tests/liberty/semicolmissing.lib.verilogsim.ok +++ b/tests/liberty/semicolmissing.lib.verilogsim.ok @@ -3,7 +3,7 @@ module fulladder (A, B, CI, CO, Y); input B; input CI; output CO; - assign CO = (((A&B)|(B&CI))|(CI&A)); // (((A * B)+(B * CI))+(CI * A)) + assign CO = (((A&B)|(B&CI))|(CI&A)); // "(((A * B)+(B * CI))+(CI * A))" output Y; - assign Y = ((A^B)^CI); // ((A^B)^CI) + assign Y = ((A^B)^CI); // "((A^B)^CI)" endmodule From 307db1ec50f800bcef6219274d83e7f40266d364 Mon Sep 17 00:00:00 2001 From: Sean Luchen Date: Thu, 3 Apr 2025 10:01:34 -0700 Subject: [PATCH 12/55] Add tests for #4976. --- tests/liberty/idranges.lib | 6 ++++++ tests/liberty/idranges.lib.filtered.ok | 2 ++ tests/liberty/idranges.lib.verilogsim.ok | 0 3 files changed, 8 insertions(+) create mode 100644 tests/liberty/idranges.lib create mode 100644 tests/liberty/idranges.lib.filtered.ok create mode 100644 tests/liberty/idranges.lib.verilogsim.ok diff --git a/tests/liberty/idranges.lib b/tests/liberty/idranges.lib new file mode 100644 index 000000000..7149f19ee --- /dev/null +++ b/tests/liberty/idranges.lib @@ -0,0 +1,6 @@ +library("foobar") { + pin("foo") { + bar : baz[0] ; + } +} + diff --git a/tests/liberty/idranges.lib.filtered.ok b/tests/liberty/idranges.lib.filtered.ok new file mode 100644 index 000000000..8a780fd67 --- /dev/null +++ b/tests/liberty/idranges.lib.filtered.ok @@ -0,0 +1,2 @@ +library("foobar") { +} diff --git a/tests/liberty/idranges.lib.verilogsim.ok b/tests/liberty/idranges.lib.verilogsim.ok new file mode 100644 index 000000000..e69de29bb From 5a6b7e1e91ac1e7f2afda5e702adb705a0375c4e Mon Sep 17 00:00:00 2001 From: Akash Levy Date: Sun, 6 Apr 2025 22:54:14 -0700 Subject: [PATCH 13/55] Add NamedObject to py_wrap_generator --- misc/py_wrap_generator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py index 03374c610..4857a9dc3 100644 --- a/misc/py_wrap_generator.py +++ b/misc/py_wrap_generator.py @@ -977,6 +977,7 @@ sources = [ WClass("IdString", link_types.ref_copy, None, "str()", ""), WClass("Const", link_types.ref_copy, None, "as_string()", ""), WClass("AttrObject", link_types.ref_copy, None, None, None), + WClass("NamedObject", link_types.ref_copy, None, None, None), WClass("Selection", link_types.ref_copy, None, None, None), WClass("Monitor", link_types.derive, None, None, None), WClass("CaseRule",link_types.ref_copy, None, None, None, True), From cd3b91413294d615edd19205ba28cddfb43f744a Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 8 Apr 2025 11:57:43 +1200 Subject: [PATCH 14/55] Reinstate #4768 Revert the reversion so that we can fix the bugs that the PR missed. --- backends/firrtl/firrtl.cc | 5 +- backends/rtlil/rtlil_backend.cc | 4 +- .../source/code_examples/extensions/my_cmd.cc | 4 +- .../extending_yosys/extensions.rst | 2 +- kernel/driver.cc | 2 +- kernel/register.cc | 24 +- kernel/rtlil.cc | 219 ++++++++++--- kernel/rtlil.h | 208 +++++++++++-- kernel/tclapi.cc | 2 +- kernel/yosys.cc | 8 +- passes/cmds/add.cc | 2 +- passes/cmds/design.cc | 6 +- passes/cmds/scc.cc | 2 +- passes/cmds/select.cc | 287 ++++++++++-------- passes/cmds/show.cc | 8 +- passes/cmds/stat.cc | 2 +- passes/cmds/viz.cc | 4 +- passes/hierarchy/submod.cc | 2 +- passes/opt/opt_clean.cc | 2 +- passes/sat/cutpoint.cc | 2 +- passes/sat/sim.cc | 4 +- passes/techmap/abc9.cc | 14 +- passes/techmap/abc9_ops.cc | 2 +- passes/techmap/abc_new.cc | 6 +- passes/techmap/aigmap.cc | 3 +- passes/techmap/clockgate.cc | 2 +- passes/techmap/nlutmap.cc | 2 +- tests/cxxrtl/run-test.sh | 2 +- tests/select/{blackboxes.ys => boxes.v} | 12 - tests/select/boxes_equals_clean.ys | 7 + tests/select/boxes_equals_name.ys | 7 + tests/select/boxes_equals_operators.ys | 38 +++ tests/select/boxes_equals_pattern.ys | 7 + tests/select/boxes_equals_wildcard.ys | 6 + tests/select/boxes_no_equals.ys | 7 + tests/select/boxes_no_equals_clean.ys | 18 ++ tests/select/boxes_stack.ys | 29 ++ tests/simple_abc9/run-test.sh | 2 +- 38 files changed, 700 insertions(+), 263 deletions(-) rename tests/select/{blackboxes.ys => boxes.v} (51%) create mode 100644 tests/select/boxes_equals_clean.ys create mode 100644 tests/select/boxes_equals_name.ys create mode 100644 tests/select/boxes_equals_operators.ys create mode 100644 tests/select/boxes_equals_pattern.ys create mode 100644 tests/select/boxes_equals_wildcard.ys create mode 100644 tests/select/boxes_no_equals.ys create mode 100644 tests/select/boxes_no_equals_clean.ys create mode 100644 tests/select/boxes_stack.ys diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index eac0c9719..ceb805dcb 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -1215,9 +1215,6 @@ struct FirrtlBackend : public Backend { } extra_args(f, filename, args, argidx); - if (!design->full_selection()) - log_cmd_error("This command only operates on fully selected designs!\n"); - log_header(design, "Executing FIRRTL backend.\n"); log_push(); @@ -1230,7 +1227,7 @@ struct FirrtlBackend : public Backend { autoid_counter = 0; // Get the top module, or a reasonable facsimile - we need something for the circuit name. - Module *top = design->top_module(); + Module *top = nullptr; Module *last = nullptr; // Generate module and wire names. for (auto module : design->modules()) { diff --git a/backends/rtlil/rtlil_backend.cc b/backends/rtlil/rtlil_backend.cc index 113f1a615..ae60ee6c7 100644 --- a/backends/rtlil/rtlil_backend.cc +++ b/backends/rtlil/rtlil_backend.cc @@ -304,8 +304,8 @@ void RTLIL_BACKEND::dump_conn(std::ostream &f, std::string indent, const RTLIL:: void RTLIL_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Module *module, RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n) { - bool print_header = flag_m || design->selected_whole_module(module->name); - bool print_body = !flag_n || !design->selected_whole_module(module->name); + bool print_header = flag_m || module->is_selected_whole(); + bool print_body = !flag_n || !module->is_selected_whole(); if (print_header) { diff --git a/docs/source/code_examples/extensions/my_cmd.cc b/docs/source/code_examples/extensions/my_cmd.cc index 36ddbe175..e6660469c 100644 --- a/docs/source/code_examples/extensions/my_cmd.cc +++ b/docs/source/code_examples/extensions/my_cmd.cc @@ -51,10 +51,10 @@ struct Test2Pass : public Pass { Test2Pass() : Pass("test2", "demonstrating sigmap on test module") { } void execute(std::vector, RTLIL::Design *design) override { - if (design->selection_stack.back().empty()) + if (design->selection().empty()) log_cmd_error("This command can't operator on an empty selection!\n"); - RTLIL::Module *module = design->modules_.at("\\test"); + RTLIL::Module *module = design->module("\\test"); RTLIL::SigSpec a(module->wire("\\a")), x(module->wire("\\x")), y(module->wire("\\y")); log("%d %d %d\n", a == x, x == y, y == a); // will print "0 0 0" diff --git a/docs/source/yosys_internals/extending_yosys/extensions.rst b/docs/source/yosys_internals/extending_yosys/extensions.rst index b02c4cd99..d30dd2bae 100644 --- a/docs/source/yosys_internals/extending_yosys/extensions.rst +++ b/docs/source/yosys_internals/extending_yosys/extensions.rst @@ -237,7 +237,7 @@ Use ``log_cmd_error()`` to report a recoverable error: .. code:: C++ - if (design->selection_stack.back().empty()) + if (design->selection().empty()) log_cmd_error("This command can't operator on an empty selection!\n"); Use ``log_assert()`` and ``log_abort()`` instead of ``assert()`` and ``abort()``. diff --git a/kernel/driver.cc b/kernel/driver.cc index a3d85bd90..eb1326ce0 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -111,7 +111,7 @@ void run(const char *command) log_last_error = ""; } catch (...) { while (GetSize(yosys_get_design()->selection_stack) > selSize) - yosys_get_design()->selection_stack.pop_back(); + yosys_get_design()->pop_selection(); throw; } } diff --git a/kernel/register.cc b/kernel/register.cc index fcb6ca769..c52bfb5b8 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -260,18 +260,18 @@ void Pass::call(RTLIL::Design *design, std::vector args) pass_register[args[0]]->execute(args, design); pass_register[args[0]]->post_execute(state); while (design->selection_stack.size() > orig_sel_stack_pos) - design->selection_stack.pop_back(); + design->pop_selection(); } void Pass::call_on_selection(RTLIL::Design *design, const RTLIL::Selection &selection, std::string command) { std::string backup_selected_active_module = design->selected_active_module; design->selected_active_module.clear(); - design->selection_stack.push_back(selection); + design->push_selection(selection); Pass::call(design, command); - design->selection_stack.pop_back(); + design->pop_selection(); design->selected_active_module = backup_selected_active_module; } @@ -279,11 +279,11 @@ void Pass::call_on_selection(RTLIL::Design *design, const RTLIL::Selection &sele { std::string backup_selected_active_module = design->selected_active_module; design->selected_active_module.clear(); - design->selection_stack.push_back(selection); + design->push_selection(selection); Pass::call(design, args); - design->selection_stack.pop_back(); + design->pop_selection(); design->selected_active_module = backup_selected_active_module; } @@ -291,12 +291,12 @@ void Pass::call_on_module(RTLIL::Design *design, RTLIL::Module *module, std::str { std::string backup_selected_active_module = design->selected_active_module; design->selected_active_module = module->name.str(); - design->selection_stack.push_back(RTLIL::Selection(false)); - design->selection_stack.back().select(module); + design->push_empty_selection(); + design->select(module); Pass::call(design, command); - design->selection_stack.pop_back(); + design->pop_selection(); design->selected_active_module = backup_selected_active_module; } @@ -304,12 +304,12 @@ void Pass::call_on_module(RTLIL::Design *design, RTLIL::Module *module, std::vec { std::string backup_selected_active_module = design->selected_active_module; design->selected_active_module = module->name.str(); - design->selection_stack.push_back(RTLIL::Selection(false)); - design->selection_stack.back().select(module); + design->push_empty_selection(); + design->select(module); Pass::call(design, args); - design->selection_stack.pop_back(); + design->pop_selection(); design->selected_active_module = backup_selected_active_module; } @@ -651,7 +651,7 @@ void Backend::backend_call(RTLIL::Design *design, std::ostream *f, std::string f } while (design->selection_stack.size() > orig_sel_stack_pos) - design->selection_stack.pop_back(); + design->pop_selection(); } struct SimHelper { diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 3b9a4a8b1..637cc9be3 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -766,8 +766,23 @@ vector RTLIL::AttrObject::get_intvec_attribute(const RTLIL::IdString &id) c return data; } +bool RTLIL::Selection::boxed_module(const RTLIL::IdString &mod_name) const +{ + if (current_design != nullptr) { + auto module = current_design->module(mod_name); + return module && module->get_blackbox_attribute(); + } else { + log_warning("Unable to check if module is boxed for null design.\n"); + return false; + } +} + bool RTLIL::Selection::selected_module(const RTLIL::IdString &mod_name) const { + if (complete_selection) + return true; + if (!selects_boxes && boxed_module(mod_name)) + return false; if (full_selection) return true; if (selected_modules.count(mod_name) > 0) @@ -779,6 +794,10 @@ bool RTLIL::Selection::selected_module(const RTLIL::IdString &mod_name) const bool RTLIL::Selection::selected_whole_module(const RTLIL::IdString &mod_name) const { + if (complete_selection) + return true; + if (!selects_boxes && boxed_module(mod_name)) + return false; if (full_selection) return true; if (selected_modules.count(mod_name) > 0) @@ -788,6 +807,10 @@ bool RTLIL::Selection::selected_whole_module(const RTLIL::IdString &mod_name) co bool RTLIL::Selection::selected_member(const RTLIL::IdString &mod_name, const RTLIL::IdString &memb_name) const { + if (complete_selection) + return true; + if (!selects_boxes && boxed_module(mod_name)) + return false; if (full_selection) return true; if (selected_modules.count(mod_name) > 0) @@ -800,7 +823,17 @@ bool RTLIL::Selection::selected_member(const RTLIL::IdString &mod_name, const RT void RTLIL::Selection::optimize(RTLIL::Design *design) { - if (full_selection) { + if (design != current_design) { + current_design = design; + } + + if (selects_boxes && full_selection) + complete_selection = true; + if (complete_selection) { + full_selection = false; + selects_boxes = true; + } + if (selects_all()) { selected_modules.clear(); selected_members.clear(); return; @@ -810,7 +843,7 @@ void RTLIL::Selection::optimize(RTLIL::Design *design) del_list.clear(); for (auto mod_name : selected_modules) { - if (design->modules_.count(mod_name) == 0) + if (current_design->modules_.count(mod_name) == 0 || (!selects_boxes && boxed_module(mod_name))) del_list.push_back(mod_name); selected_members.erase(mod_name); } @@ -819,7 +852,7 @@ void RTLIL::Selection::optimize(RTLIL::Design *design) del_list.clear(); for (auto &it : selected_members) - if (design->modules_.count(it.first) == 0) + if (current_design->modules_.count(it.first) == 0 || (!selects_boxes && boxed_module(it.first))) del_list.push_back(it.first); for (auto mod_name : del_list) selected_members.erase(mod_name); @@ -827,7 +860,7 @@ void RTLIL::Selection::optimize(RTLIL::Design *design) for (auto &it : selected_members) { del_list.clear(); for (auto memb_name : it.second) - if (design->modules_[it.first]->count_id(memb_name) == 0) + if (current_design->modules_[it.first]->count_id(memb_name) == 0) del_list.push_back(memb_name); for (auto memb_name : del_list) it.second.erase(memb_name); @@ -838,8 +871,8 @@ void RTLIL::Selection::optimize(RTLIL::Design *design) for (auto &it : selected_members) if (it.second.size() == 0) del_list.push_back(it.first); - else if (it.second.size() == design->modules_[it.first]->wires_.size() + design->modules_[it.first]->memories.size() + - design->modules_[it.first]->cells_.size() + design->modules_[it.first]->processes.size()) + else if (it.second.size() == current_design->modules_[it.first]->wires_.size() + current_design->modules_[it.first]->memories.size() + + current_design->modules_[it.first]->cells_.size() + current_design->modules_[it.first]->processes.size()) add_list.push_back(it.first); for (auto mod_name : del_list) selected_members.erase(mod_name); @@ -848,13 +881,24 @@ void RTLIL::Selection::optimize(RTLIL::Design *design) selected_modules.insert(mod_name); } - if (selected_modules.size() == design->modules_.size()) { - full_selection = true; + if (selected_modules.size() == current_design->modules_.size()) { selected_modules.clear(); selected_members.clear(); + if (selects_boxes) + complete_selection = true; + else + full_selection = true; } } +void RTLIL::Selection::clear() +{ + full_selection = false; + complete_selection = false; + selected_modules.clear(); + selected_members.clear(); +} + RTLIL::Design::Design() : verilog_defines (new define_map_t) { @@ -863,7 +907,7 @@ RTLIL::Design::Design() hashidx_ = hashidx_count; refcount_modules_ = 0; - selection_stack.push_back(RTLIL::Selection()); + push_full_selection(); #ifdef WITH_PYTHON RTLIL::Design::get_all_designs()->insert(std::pair(hashidx_, this)); @@ -908,7 +952,7 @@ const RTLIL::Module *RTLIL::Design::module(const RTLIL::IdString& name) const return modules_.count(name) ? modules_.at(name) : NULL; } -RTLIL::Module *RTLIL::Design::top_module() +RTLIL::Module *RTLIL::Design::top_module() const { RTLIL::Module *module = nullptr; int module_count = 0; @@ -1066,6 +1110,7 @@ void RTLIL::Design::sort() void RTLIL::Design::check() { #ifndef NDEBUG + log_assert(!selection_stack.empty()); for (auto &it : modules_) { log_assert(this == it.second->design); log_assert(it.first == it.second->name); @@ -1089,27 +1134,21 @@ bool RTLIL::Design::selected_module(const RTLIL::IdString& mod_name) const { if (!selected_active_module.empty() && mod_name != selected_active_module) return false; - if (selection_stack.size() == 0) - return true; - return selection_stack.back().selected_module(mod_name); + return selection().selected_module(mod_name); } bool RTLIL::Design::selected_whole_module(const RTLIL::IdString& mod_name) const { if (!selected_active_module.empty() && mod_name != selected_active_module) return false; - if (selection_stack.size() == 0) - return true; - return selection_stack.back().selected_whole_module(mod_name); + return selection().selected_whole_module(mod_name); } bool RTLIL::Design::selected_member(const RTLIL::IdString& mod_name, const RTLIL::IdString& memb_name) const { if (!selected_active_module.empty() && mod_name != selected_active_module) return false; - if (selection_stack.size() == 0) - return true; - return selection_stack.back().selected_member(mod_name, memb_name); + return selection().selected_member(mod_name, memb_name); } bool RTLIL::Design::selected_module(RTLIL::Module *mod) const @@ -1122,37 +1161,86 @@ bool RTLIL::Design::selected_whole_module(RTLIL::Module *mod) const return selected_whole_module(mod->name); } -std::vector RTLIL::Design::selected_modules() const +void RTLIL::Design::push_selection(RTLIL::Selection sel) { - std::vector result; - result.reserve(modules_.size()); - for (auto &it : modules_) - if (selected_module(it.first) && !it.second->get_blackbox_attribute()) - result.push_back(it.second); - return result; + sel.current_design = this; + selection_stack.push_back(sel); } -std::vector RTLIL::Design::selected_whole_modules() const +void RTLIL::Design::push_empty_selection() { - std::vector result; - result.reserve(modules_.size()); - for (auto &it : modules_) - if (selected_whole_module(it.first) && !it.second->get_blackbox_attribute()) - result.push_back(it.second); - return result; + push_selection(RTLIL::Selection::EmptySelection(this)); } -std::vector RTLIL::Design::selected_whole_modules_warn(bool include_wb) const +void RTLIL::Design::push_full_selection() { + push_selection(RTLIL::Selection::FullSelection(this)); +} + +void RTLIL::Design::push_complete_selection() +{ + push_selection(RTLIL::Selection::CompleteSelection(this)); +} + +void RTLIL::Design::pop_selection() +{ + selection_stack.pop_back(); + // Default to a full_selection if we ran out of stack + if (selection_stack.empty()) + push_full_selection(); +} + +std::vector RTLIL::Design::selected_modules(RTLIL::SelectPartials partials, RTLIL::SelectBoxes boxes) const +{ + bool include_partials = partials == RTLIL::SELECT_ALL; + bool exclude_boxes = (partials & RTLIL::SB_UNBOXED_ONLY) != 0; + bool ignore_wb = (partials & RTLIL::SB_INCL_WB) != 0; std::vector result; result.reserve(modules_.size()); for (auto &it : modules_) - if (it.second->get_blackbox_attribute(include_wb)) - continue; - else if (selected_whole_module(it.first)) - result.push_back(it.second); - else if (selected_module(it.first)) - log_warning("Ignoring partially selected module %s.\n", log_id(it.first)); + if (selected_whole_module(it.first) || (include_partials && selected_module(it.first))) { + if (!(exclude_boxes && it.second->get_blackbox_attribute(ignore_wb))) + result.push_back(it.second); + else + switch (boxes) + { + case RTLIL::SB_UNBOXED_WARN: + log_warning("Ignoring boxed module %s.\n", log_id(it.first)); + break; + case RTLIL::SB_EXCL_BB_WARN: + log_warning("Ignoring blackbox module %s.\n", log_id(it.first)); + break; + case RTLIL::SB_UNBOXED_ERR: + log_error("Unsupported boxed module %s.\n", log_id(it.first)); + break; + case RTLIL::SB_EXCL_BB_ERR: + log_error("Unsupported blackbox module %s.\n", log_id(it.first)); + break; + case RTLIL::SB_UNBOXED_CMDERR: + log_cmd_error("Unsupported boxed module %s.\n", log_id(it.first)); + break; + case RTLIL::SB_EXCL_BB_CMDERR: + log_cmd_error("Unsupported blackbox module %s.\n", log_id(it.first)); + break; + default: + break; + } + } else if (!include_partials && selected_module(it.first)) { + switch(partials) + { + case RTLIL::SELECT_WHOLE_WARN: + log_warning("Ignoring partially selected module %s.\n", log_id(it.first)); + break; + case RTLIL::SELECT_WHOLE_ERR: + log_error("Unsupported partially selected module %s.\n", log_id(it.first)); + break; + case RTLIL::SELECT_WHOLE_CMDERR: + log_cmd_error("Unsupported partially selected module %s.\n", log_id(it.first)); + break; + default: + break; + } + } return result; } @@ -2284,6 +2372,13 @@ void RTLIL::Module::check() 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 + log_assert(!it.second->get_blackbox_attribute()); + } } for (auto &it : processes) { @@ -2411,6 +2506,16 @@ bool RTLIL::Module::has_processes_warn() const return !processes.empty(); } +bool RTLIL::Module::is_selected() const +{ + return design->selected_module(this->name); +} + +bool RTLIL::Module::is_selected_whole() const +{ + return design->selected_whole_module(this->name); +} + std::vector RTLIL::Module::selected_wires() const { std::vector result; @@ -2431,6 +2536,40 @@ std::vector RTLIL::Module::selected_cells() const return result; } +std::vector RTLIL::Module::selected_memories() const +{ + std::vector result; + result.reserve(memories.size()); + for (auto &it : memories) + if (design->selected(this, it.second)) + result.push_back(it.second); + return result; +} + +std::vector RTLIL::Module::selected_processes() const +{ + std::vector result; + result.reserve(processes.size()); + for (auto &it : processes) + if (design->selected(this, it.second)) + result.push_back(it.second); + return result; +} + +std::vector RTLIL::Module::selected_members() const +{ + std::vector result; + auto cells = selected_cells(); + auto memories = selected_memories(); + auto wires = selected_wires(); + auto processes = selected_processes(); + result.insert(result.end(), cells.begin(), cells.end()); + result.insert(result.end(), memories.begin(), memories.end()); + result.insert(result.end(), wires.begin(), wires.end()); + result.insert(result.end(), processes.begin(), processes.end()); + return result; +} + void RTLIL::Module::add(RTLIL::Wire *wire) { log_assert(!wire->name.empty()); diff --git a/kernel/rtlil.h b/kernel/rtlil.h index f1bd96be7..52e7a17e7 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -56,8 +56,33 @@ namespace RTLIL CONST_FLAG_REAL = 4 // only used for parameters }; + enum SelectPartials : unsigned char { + SELECT_ALL = 0, // include partial modules + SELECT_WHOLE_ONLY = 1, // ignore partial modules + SELECT_WHOLE_WARN = 2, // call log_warning on partial module + SELECT_WHOLE_ERR = 3, // call log_error on partial module + SELECT_WHOLE_CMDERR = 4 // call log_cmd_error on partial module + }; + + enum SelectBoxes : unsigned char { + SB_ALL = 0, // include boxed modules + SB_WARN = 1, // helper for log_warning + SB_ERR = 2, // helper for log_error + SB_CMDERR = 3, // helper for log_cmd_error + SB_UNBOXED_ONLY = 4, // ignore boxed modules + SB_UNBOXED_WARN = 5, // call log_warning on boxed module + SB_UNBOXED_ERR = 6, // call log_error on boxed module + SB_UNBOXED_CMDERR = 7, // call log_cmd_error on boxed module + SB_INCL_WB = 8, // helper for white boxes + SB_EXCL_BB_ONLY = 12, // ignore black boxes, but not white boxes + SB_EXCL_BB_WARN = 13, // call log_warning on black boxed module + SB_EXCL_BB_ERR = 14, // call log_error on black boxed module + SB_EXCL_BB_CMDERR = 15 // call log_cmd_error on black boxed module + }; + struct Const; struct AttrObject; + struct NamedObject; struct Selection; struct Monitor; struct Design; @@ -869,6 +894,11 @@ struct RTLIL::AttrObject vector get_intvec_attribute(const RTLIL::IdString &id) const; }; +struct RTLIL::NamedObject : public RTLIL::AttrObject +{ + RTLIL::IdString name; +}; + struct RTLIL::SigChunk { RTLIL::Wire *wire; @@ -1134,32 +1164,94 @@ public: struct RTLIL::Selection { + // selection includes boxed modules + bool selects_boxes; + // selection covers full design, including boxed modules + bool complete_selection; + // selection covers full design, not including boxed modules bool full_selection; pool selected_modules; dict> selected_members; + RTLIL::Design *current_design; - Selection(bool full = true) : full_selection(full) { } + // create a new selection + Selection( + // should the selection cover the full design + bool full = true, + // should the selection include boxed modules + bool boxes = false, + // the design to select from + RTLIL::Design *design = nullptr + ) : + full_selection(full && !boxes), selects_boxes(boxes), complete_selection(full && boxes), current_design(design) { } + // checks if the given module exists in the current design and is a + // boxed module, warning the user if the current design is not set + bool boxed_module(const RTLIL::IdString &mod_name) const; + + // checks if the given module is included in this selection bool selected_module(const RTLIL::IdString &mod_name) const; + + // checks if the given module is wholly included in this selection, + // i.e. not partially selected bool selected_whole_module(const RTLIL::IdString &mod_name) const; + + // checks if the given member from the given module is included in this + // selection bool selected_member(const RTLIL::IdString &mod_name, const RTLIL::IdString &memb_name) const; + + // optimizes this selection for the given design by: + // - removing non-existent modules and members, any boxed modules and + // their members (if selection does not include boxes), and any + // partially selected modules with no selected members; + // - marking partially selected modules as wholly selected if all + // members of that module are selected; and + // - marking selection as a complete_selection if all modules in the + // given design are selected, or a full_selection if it does not + // include boxes. void optimize(RTLIL::Design *design); + // checks if selection covers full design (may or may not include + // boxed-modules) + bool selects_all() const { + return full_selection || complete_selection; + } + + // add whole module to this selection template void select(T1 *module) { - if (!full_selection && selected_modules.count(module->name) == 0) { + if (!selects_all() && selected_modules.count(module->name) == 0) { selected_modules.insert(module->name); selected_members.erase(module->name); + if (module->get_blackbox_attribute()) + selects_boxes = true; } } + // add member of module to this selection template void select(T1 *module, T2 *member) { - if (!full_selection && selected_modules.count(module->name) == 0) + if (!selects_all() && selected_modules.count(module->name) == 0) { selected_members[module->name].insert(member->name); + if (module->get_blackbox_attribute()) + selects_boxes = true; + } } + // checks if selection is empty bool empty() const { - return !full_selection && selected_modules.empty() && selected_members.empty(); + return !selects_all() && selected_modules.empty() && selected_members.empty(); } + + // clear this selection, leaving it empty + void clear(); + + // create a new selection which is empty + static Selection EmptySelection(RTLIL::Design *design = nullptr) { return Selection(false, false, design); }; + + // create a new selection with all non-boxed modules + static Selection FullSelection(RTLIL::Design *design = nullptr) { return Selection(true, false, design); }; + + // create a new selection with all modules, including boxes + static Selection CompleteSelection(RTLIL::Design *design = nullptr) { return Selection(true, true, design); }; }; struct RTLIL::Monitor @@ -1213,7 +1305,7 @@ struct RTLIL::Design RTLIL::ObjRange modules(); RTLIL::Module *module(const RTLIL::IdString &name); const RTLIL::Module *module(const RTLIL::IdString &name) const; - RTLIL::Module *top_module(); + RTLIL::Module *top_module() const; bool has(const RTLIL::IdString &id) const { return modules_.count(id) != 0; @@ -1240,57 +1332,118 @@ struct RTLIL::Design void check(); void optimize(); + // checks if the given module is included in the current selection bool selected_module(const RTLIL::IdString &mod_name) const; + + // checks if the given module is wholly included in the current + // selection, i.e. not partially selected bool selected_whole_module(const RTLIL::IdString &mod_name) const; + + // checks if the given member from the given module is included in the + // current selection bool selected_member(const RTLIL::IdString &mod_name, const RTLIL::IdString &memb_name) const; + // checks if the given module is included in the current selection bool selected_module(RTLIL::Module *mod) const; + + // checks if the given module is wholly included in the current + // selection, i.e. not partially selected bool selected_whole_module(RTLIL::Module *mod) const; + // push the given selection to the selection stack + void push_selection(RTLIL::Selection sel); + // push a new selection to the selection stack, with nothing selected + void push_empty_selection(); + // push a new selection to the selection stack, with all non-boxed + // modules selected + void push_full_selection(); + // push a new selection to the selection stack, with all modules + // selected including boxes + void push_complete_selection(); + // pop the current selection from the stack, returning to a full + // selection (no boxes) if the stack is empty + void pop_selection(); + + // get the current selection RTLIL::Selection &selection() { return selection_stack.back(); } + // get the current selection const RTLIL::Selection &selection() const { return selection_stack.back(); } + // is the current selection a full selection (no boxes) bool full_selection() const { - return selection_stack.back().full_selection; + return selection().full_selection; } + // is the given module in the current selection template bool selected(T1 *module) const { return selected_module(module->name); } + // is the given member of the given module in the current selection template bool selected(T1 *module, T2 *member) const { return selected_member(module->name, member->name); } + // add whole module to the current selection template void select(T1 *module) { - if (selection_stack.size() > 0) { - RTLIL::Selection &sel = selection_stack.back(); - sel.select(module); - } + RTLIL::Selection &sel = selection(); + sel.select(module); } + // add member of module to the current selection template void select(T1 *module, T2 *member) { - if (selection_stack.size() > 0) { - RTLIL::Selection &sel = selection_stack.back(); - sel.select(module, member); - } + RTLIL::Selection &sel = selection(); + sel.select(module, member); } - std::vector selected_modules() const; - std::vector selected_whole_modules() const; - std::vector selected_whole_modules_warn(bool include_wb = false) const; + // returns all selected modules + std::vector selected_modules( + // controls if partially selected modules are included + RTLIL::SelectPartials partials = SELECT_ALL, + // controls if boxed modules are included + RTLIL::SelectBoxes boxes = SB_UNBOXED_WARN + ) const; + + // returns all selected modules, and may include boxes + std::vector all_selected_modules() const { return selected_modules(SELECT_ALL, SB_ALL); } + // returns all selected unboxed modules, silently ignoring any boxed + // modules in the selection + std::vector selected_unboxed_modules() const { return selected_modules(SELECT_ALL, SB_UNBOXED_ONLY); } + // returns all selected unboxed modules, warning the user if any boxed + // modules have been ignored + std::vector selected_unboxed_modules_warn() const { return selected_modules(SELECT_ALL, SB_UNBOXED_WARN); } + + [[deprecated("Use select_unboxed_whole_modules() to maintain prior behaviour, or consider one of the other selected whole module helpers.")]] + std::vector selected_whole_modules() const { return selected_modules(SELECT_WHOLE_ONLY, SB_UNBOXED_WARN); } + // returns all selected whole modules, silently ignoring partially + // selected modules, and may include boxes + std::vector all_selected_whole_modules() const { return selected_modules(SELECT_WHOLE_ONLY, SB_ALL); } + // returns all selected whole modules, warning the user if any partially + // selected or boxed modules have been ignored; optionally includes + // selected whole modules with the 'whitebox' attribute + std::vector selected_whole_modules_warn( + // should whole modules with the 'whitebox' attribute be + // included + bool include_wb = false + ) const { return selected_modules(SELECT_WHOLE_WARN, include_wb ? SB_EXCL_BB_WARN : SB_UNBOXED_WARN); } + // returns all selected unboxed whole modules, silently ignoring + // partially selected or boxed modules + std::vector selected_unboxed_whole_modules() const { return selected_modules(SELECT_WHOLE_ONLY, SB_UNBOXED_ONLY); } + // returns all selected unboxed whole modules, warning the user if any + // partially selected or boxed modules have been ignored + std::vector selected_unboxed_whole_modules_warn() const { return selected_modules(SELECT_WHOLE_WARN, SB_UNBOXED_WARN); } #ifdef WITH_PYTHON static std::map *get_all_designs(void); #endif }; -struct RTLIL::Module : public RTLIL::AttrObject +struct RTLIL::Module : public RTLIL::NamedObject { Hasher::hash_t hashidx_; [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } @@ -1313,7 +1466,6 @@ public: std::vector connections_; std::vector bindings_; - RTLIL::IdString name; idict avail_parameters; dict parameter_default_values; dict memories; @@ -1358,8 +1510,14 @@ public: bool has_memories_warn() const; bool has_processes_warn() const; + bool is_selected() const; + bool is_selected_whole() const; + std::vector selected_wires() const; std::vector selected_cells() const; + std::vector selected_memories() const; + std::vector selected_processes() const; + std::vector selected_members() const; template bool selected(T *member) const { return design->selected_member(name, member->name); @@ -1645,7 +1803,7 @@ namespace RTLIL_BACKEND { void dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire); } -struct RTLIL::Wire : public RTLIL::AttrObject +struct RTLIL::Wire : public RTLIL::NamedObject { Hasher::hash_t hashidx_; [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } @@ -1668,7 +1826,6 @@ public: void operator=(RTLIL::Wire &other) = delete; RTLIL::Module *module; - RTLIL::IdString name; int width, start_offset, port_id; bool port_input, port_output, upto, is_signed; @@ -1697,14 +1854,13 @@ inline int GetSize(RTLIL::Wire *wire) { return wire->width; } -struct RTLIL::Memory : public RTLIL::AttrObject +struct RTLIL::Memory : public RTLIL::NamedObject { Hasher::hash_t hashidx_; [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } Memory(); - RTLIL::IdString name; int width, start_offset, size; #ifdef WITH_PYTHON ~Memory(); @@ -1712,7 +1868,7 @@ struct RTLIL::Memory : public RTLIL::AttrObject #endif }; -struct RTLIL::Cell : public RTLIL::AttrObject +struct RTLIL::Cell : public RTLIL::NamedObject { Hasher::hash_t hashidx_; [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } @@ -1729,7 +1885,6 @@ public: void operator=(RTLIL::Cell &other) = delete; RTLIL::Module *module; - RTLIL::IdString name; RTLIL::IdString type; dict connections_; dict parameters; @@ -1822,7 +1977,7 @@ struct RTLIL::SyncRule RTLIL::SyncRule *clone() const; }; -struct RTLIL::Process : public RTLIL::AttrObject +struct RTLIL::Process : public RTLIL::NamedObject { Hasher::hash_t hashidx_; [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } @@ -1834,7 +1989,6 @@ protected: ~Process(); public: - RTLIL::IdString name; RTLIL::Module *module; RTLIL::CaseRule root_case; std::vector syncs; diff --git a/kernel/tclapi.cc b/kernel/tclapi.cc index be39ca4a0..e970779d7 100644 --- a/kernel/tclapi.cc +++ b/kernel/tclapi.cc @@ -114,7 +114,7 @@ static int tcl_yosys_cmd(ClientData, Tcl_Interp *interp, int argc, const char *a if (in_repl) { auto design = yosys_get_design(); while (design->selection_stack.size() > 1) - design->selection_stack.pop_back(); + design->pop_selection(); log_reset_stack(); } Tcl_SetResult(interp, (char *)"Yosys command produced an error", TCL_STATIC); diff --git a/kernel/yosys.cc b/kernel/yosys.cc index a5520e6ef..9b0bc92ce 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -312,11 +312,11 @@ const char *create_prompt(RTLIL::Design *design, int recursion_counter) str += "yosys"; if (!design->selected_active_module.empty()) str += stringf(" [%s]", RTLIL::unescape_id(design->selected_active_module).c_str()); - if (!design->selection_stack.empty() && !design->selection_stack.back().full_selection) { + if (!design->full_selection()) { if (design->selected_active_module.empty()) str += "*"; - else if (design->selection_stack.back().selected_modules.size() != 1 || design->selection_stack.back().selected_members.size() != 0 || - design->selection_stack.back().selected_modules.count(design->selected_active_module) == 0) + else if (design->selection().selected_modules.size() != 1 || design->selection().selected_members.size() != 0 || + design->selection().selected_modules.count(design->selected_active_module) == 0) str += "*"; } snprintf(buffer, 100, "%s> ", str.c_str()); @@ -979,7 +979,7 @@ void shell(RTLIL::Design *design) Pass::call(design, command); } catch (log_cmd_error_exception) { while (design->selection_stack.size() > 1) - design->selection_stack.pop_back(); + design->pop_selection(); log_reset_stack(); } design->check(); diff --git a/passes/cmds/add.cc b/passes/cmds/add.cc index c09517254..833d6006d 100644 --- a/passes/cmds/add.cc +++ b/passes/cmds/add.cc @@ -102,7 +102,7 @@ static void add_wire(RTLIL::Design *design, RTLIL::Module *module, std::string n RTLIL::Module *mod = design->module(cell->type); if (mod == nullptr) continue; - if (!design->selected_whole_module(mod->name)) + if (!mod->is_selected_whole()) continue; if (mod->get_blackbox_attribute()) continue; diff --git a/passes/cmds/design.cc b/passes/cmds/design.cc index 168d38563..910c9e366 100644 --- a/passes/cmds/design.cc +++ b/passes/cmds/design.cc @@ -216,8 +216,8 @@ struct DesignPass : public Pass { RTLIL::Selection sel; if (argidx != args.size()) { handle_extra_select_args(this, args, argidx, args.size(), copy_from_design); - sel = copy_from_design->selection_stack.back(); - copy_from_design->selection_stack.pop_back(); + sel = copy_from_design->selection(); + copy_from_design->pop_selection(); argidx = args.size(); } @@ -368,7 +368,7 @@ struct DesignPass : public Pass { design->selection_vars.clear(); design->selected_active_module.clear(); - design->selection_stack.push_back(RTLIL::Selection()); + design->push_full_selection(); } if (reset_mode || reset_vlog_mode || !load_name.empty() || push_mode || pop_mode) diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc index 197bd9319..0f988e57a 100644 --- a/passes/cmds/scc.cc +++ b/passes/cmds/scc.cc @@ -340,7 +340,7 @@ struct SccPass : public Pass { int origSelectPos = design->selection_stack.size() - 1; extra_args(args, argidx, design); - RTLIL::Selection newSelection(false); + auto newSelection = RTLIL::Selection::EmptySelection(design); int scc_counter = 0; for (auto mod : design->selected_modules()) diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index aec4c964b..1d75091af 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -141,24 +141,42 @@ static bool match_attr(const dict &attributes, co return match_attr(attributes, match_expr, std::string(), 0); } +static void select_all(RTLIL::Design *design, RTLIL::Selection &lhs) +{ + if (!lhs.selects_all()) + return; + lhs.current_design = design; + lhs.selected_modules.clear(); + for (auto mod : design->modules()) { + if (!lhs.selects_boxes && mod->get_blackbox_attribute()) + continue; + lhs.selected_modules.insert(mod->name); + } + lhs.full_selection = false; + lhs.complete_selection = false; +} + static void select_op_neg(RTLIL::Design *design, RTLIL::Selection &lhs) { - if (lhs.full_selection) { - lhs.full_selection = false; - lhs.selected_modules.clear(); - lhs.selected_members.clear(); + if (lhs.selects_all()) { + lhs.clear(); return; } if (lhs.selected_modules.size() == 0 && lhs.selected_members.size() == 0) { - lhs.full_selection = true; + if (lhs.selects_boxes) + lhs.complete_selection = true; + else + lhs.full_selection = true; return; } - RTLIL::Selection new_sel(false); + auto new_sel = RTLIL::Selection::EmptySelection(); for (auto mod : design->modules()) { + if (!lhs.selects_boxes && mod->get_blackbox_attribute()) + continue; if (lhs.selected_whole_module(mod->name)) continue; if (!lhs.selected_module(mod->name)) { @@ -212,7 +230,7 @@ static void select_op_random(RTLIL::Design *design, RTLIL::Selection &lhs, int c } } - lhs = RTLIL::Selection(false); + lhs = RTLIL::Selection(false, lhs.selects_boxes, design); while (!objects.empty() && count-- > 0) { @@ -243,7 +261,7 @@ static void select_op_submod(RTLIL::Design *design, RTLIL::Selection &lhs) static void select_op_cells_to_modules(RTLIL::Design *design, RTLIL::Selection &lhs) { - RTLIL::Selection new_sel(false); + RTLIL::Selection new_sel(false, lhs.selects_boxes, design); for (auto mod : design->modules()) if (lhs.selected_module(mod->name)) for (auto cell : mod->cells()) @@ -254,7 +272,7 @@ static void select_op_cells_to_modules(RTLIL::Design *design, RTLIL::Selection & static void select_op_module_to_cells(RTLIL::Design *design, RTLIL::Selection &lhs) { - RTLIL::Selection new_sel(false); + RTLIL::Selection new_sel(false, lhs.selects_boxes, design); for (auto mod : design->modules()) for (auto cell : mod->cells()) if ((design->module(cell->type) != nullptr) && lhs.selected_whole_module(cell->type)) @@ -274,6 +292,8 @@ static void select_op_alias(RTLIL::Design *design, RTLIL::Selection &lhs) { for (auto mod : design->modules()) { + if (!lhs.selects_boxes && mod->get_blackbox_attribute()) + continue; if (lhs.selected_whole_module(mod->name)) continue; if (!lhs.selected_module(mod->name)) @@ -292,18 +312,38 @@ static void select_op_alias(RTLIL::Design *design, RTLIL::Selection &lhs) } } -static void select_op_union(RTLIL::Design*, RTLIL::Selection &lhs, const RTLIL::Selection &rhs) +static void select_op_union(RTLIL::Design* design, RTLIL::Selection &lhs, const RTLIL::Selection &rhs) { - if (rhs.full_selection) { - lhs.full_selection = true; - lhs.selected_modules.clear(); - lhs.selected_members.clear(); + if (lhs.complete_selection) + return; + else if (rhs.complete_selection) { + lhs.complete_selection = true; + lhs.optimize(design); return; } - if (lhs.full_selection) + if (rhs.selects_boxes) { + if (lhs.full_selection) { + select_all(design, lhs); + } + lhs.selects_boxes = true; + } + else if (lhs.full_selection) return; + if (rhs.full_selection) { + if (lhs.selects_boxes) { + auto new_rhs = RTLIL::Selection(rhs); + select_all(design, new_rhs); + for (auto mod : new_rhs.selected_modules) + lhs.selected_modules.insert(mod); + } else { + lhs.clear(); + lhs.full_selection = true; + } + return; + } + for (auto &it : rhs.selected_members) for (auto &it2 : it.second) lhs.selected_members[it.first].insert(it2); @@ -316,21 +356,31 @@ static void select_op_union(RTLIL::Design*, RTLIL::Selection &lhs, const RTLIL:: static void select_op_diff(RTLIL::Design *design, RTLIL::Selection &lhs, const RTLIL::Selection &rhs) { - if (rhs.full_selection) { - lhs.full_selection = false; - lhs.selected_modules.clear(); - lhs.selected_members.clear(); + if (rhs.complete_selection) { + lhs.clear(); return; } - if (lhs.full_selection) { - if (!rhs.full_selection && rhs.selected_modules.size() == 0 && rhs.selected_members.size() == 0) - return; - lhs.full_selection = false; - for (auto mod : design->modules()) - lhs.selected_modules.insert(mod->name); + if (rhs.full_selection) { + if (lhs.selects_boxes) { + auto new_rhs = RTLIL::Selection(rhs); + select_all(design, new_rhs); + select_all(design, lhs); + for (auto mod : new_rhs.selected_modules) { + lhs.selected_modules.erase(mod); + lhs.selected_members.erase(mod); + } + } else { + lhs.clear(); + } + return; } + if (rhs.empty() || lhs.empty()) + return; + + select_all(design, lhs); + for (auto &it : rhs.selected_modules) { lhs.selected_modules.erase(it); lhs.selected_members.erase(it); @@ -366,38 +416,46 @@ static void select_op_diff(RTLIL::Design *design, RTLIL::Selection &lhs, const R static void select_op_intersect(RTLIL::Design *design, RTLIL::Selection &lhs, const RTLIL::Selection &rhs) { - if (rhs.full_selection) + if (rhs.complete_selection) return; - if (lhs.full_selection) { - lhs.full_selection = false; - for (auto mod : design->modules()) - lhs.selected_modules.insert(mod->name); + if (rhs.full_selection && !lhs.selects_boxes) + return; + + if (lhs.empty()) + return; + + if (rhs.empty()) { + lhs.clear(); + return; } + select_all(design, lhs); + std::vector del_list; - for (auto &it : lhs.selected_modules) - if (rhs.selected_modules.count(it) == 0) { - if (rhs.selected_members.count(it) > 0) - for (auto &it2 : rhs.selected_members.at(it)) - lhs.selected_members[it].insert(it2); - del_list.push_back(it); - } + for (auto mod_name : lhs.selected_modules) { + if (rhs.selected_whole_module(mod_name)) + continue; + if (rhs.selected_module(mod_name)) + for (auto memb_name : rhs.selected_members.at(mod_name)) + lhs.selected_members[mod_name].insert(memb_name); + del_list.push_back(mod_name); + } for (auto &it : del_list) lhs.selected_modules.erase(it); del_list.clear(); for (auto &it : lhs.selected_members) { - if (rhs.selected_modules.count(it.first) > 0) + if (rhs.selected_whole_module(it.first)) continue; - if (rhs.selected_members.count(it.first) == 0) { + if (!rhs.selected_module(it.first)) { del_list.push_back(it.first); continue; } std::vector del_list2; for (auto &it2 : it.second) - if (rhs.selected_members.at(it.first).count(it2) == 0) + if (!rhs.selected_member(it.first, it2)) del_list2.push_back(it2); for (auto &it2 : del_list2) it.second.erase(it2); @@ -610,9 +668,7 @@ static void select_filter_active_mod(RTLIL::Design *design, RTLIL::Selection &se return; if (sel.full_selection) { - sel.full_selection = false; - sel.selected_modules.clear(); - sel.selected_members.clear(); + sel.clear(); sel.selected_modules.insert(design->selected_active_module); return; } @@ -645,8 +701,7 @@ static void select_stmt(RTLIL::Design *design, std::string arg, bool disable_emp if (arg[0] == '%') { if (arg == "%") { - if (design->selection_stack.size() > 0) - work_stack.push_back(design->selection_stack.back()); + work_stack.push_back(design->selection()); } else if (arg == "%%") { while (work_stack.size() > 1) { @@ -796,15 +851,16 @@ static void select_stmt(RTLIL::Design *design, std::string arg, bool disable_emp } } - work_stack.push_back(RTLIL::Selection()); + bool full_selection = (arg == "*" && arg_mod == "*"); + work_stack.push_back(RTLIL::Selection(full_selection, select_blackboxes, design)); RTLIL::Selection &sel = work_stack.back(); - if (arg == "*" && arg_mod == "*" && select_blackboxes) { + if (sel.full_selection) { + if (sel.selects_boxes) sel.optimize(design); select_filter_active_mod(design, work_stack.back()); return; } - sel.full_selection = false; for (auto mod : design->modules()) { if (!select_blackboxes && mod->get_blackbox_attribute()) @@ -945,38 +1001,33 @@ static void select_stmt(RTLIL::Design *design, std::string arg, bool disable_emp for (auto &it : arg_mod_found) { if (it.second == false && !disable_empty_warning) { - log_warning("Selection \"%s\" did not match any module.\n", it.first.c_str()); + std::string selection_str = select_blackboxes ? "=" : ""; + selection_str += it.first; + log_warning("Selection \"%s\" did not match any module.\n", selection_str.c_str()); } } for (auto &it : arg_memb_found) { if (it.second == false && !disable_empty_warning) { - log_warning("Selection \"%s\" did not match any object.\n", it.first.c_str()); + std::string selection_str = select_blackboxes ? "=" : ""; + selection_str += it.first; + log_warning("Selection \"%s\" did not match any object.\n", selection_str.c_str()); } } } static std::string describe_selection_for_assert(RTLIL::Design *design, RTLIL::Selection *sel, bool whole_modules = false) { + bool push_selection = &design->selection() != sel; + if (push_selection) design->push_selection(*sel); std::string desc = "Selection contains:\n"; - for (auto mod : design->modules()) + for (auto mod : design->all_selected_modules()) { - if (sel->selected_module(mod->name)) { - if (whole_modules && sel->selected_whole_module(mod->name)) - desc += stringf("%s\n", id2cstr(mod->name)); - for (auto wire : mod->wires()) - if (sel->selected_member(mod->name, wire->name)) - desc += stringf("%s/%s\n", id2cstr(mod->name), id2cstr(wire->name)); - for (auto &it : mod->memories) - if (sel->selected_member(mod->name, it.first)) - desc += stringf("%s/%s\n", id2cstr(mod->name), id2cstr(it.first)); - for (auto cell : mod->cells()) - if (sel->selected_member(mod->name, cell->name)) - desc += stringf("%s/%s\n", id2cstr(mod->name), id2cstr(cell->name)); - for (auto &it : mod->processes) - if (sel->selected_member(mod->name, it.first)) - desc += stringf("%s/%s\n", id2cstr(mod->name), id2cstr(it.first)); - } + if (whole_modules && sel->selected_whole_module(mod->name)) + desc += stringf("%s\n", id2cstr(mod->name)); + for (auto it : mod->selected_members()) + desc += stringf("%s/%s\n", id2cstr(mod->name), id2cstr(it->name)); } + if (push_selection) design->pop_selection(); return desc; } @@ -1001,9 +1052,9 @@ void handle_extra_select_args(Pass *pass, const vector &args, size_t arg work_stack.pop_back(); } if (work_stack.empty()) - design->selection_stack.push_back(RTLIL::Selection(false)); + design->push_empty_selection(); else - design->selection_stack.push_back(work_stack.back()); + design->push_selection(work_stack.back()); } // extern decl. in register.h @@ -1017,7 +1068,7 @@ RTLIL::Selection eval_select_args(const vector &args, RTLIL::Design *des work_stack.pop_back(); } if (work_stack.empty()) - return RTLIL::Selection(false); + return RTLIL::Selection::EmptySelection(design); return work_stack.back(); } @@ -1390,7 +1441,7 @@ struct SelectPass : public Pass { if (f.fail()) log_error("Can't open '%s' for reading: %s\n", read_file.c_str(), strerror(errno)); - RTLIL::Selection sel(false); + auto sel = RTLIL::Selection::EmptySelection(design); string line; while (std::getline(f, line)) { @@ -1431,7 +1482,7 @@ struct SelectPass : public Pass { log_cmd_error("Option -unset can not be combined with -list, -write, -count, -set, %s.\n", common_flagset); if (work_stack.size() == 0 && got_module) { - RTLIL::Selection sel; + auto sel = RTLIL::Selection::FullSelection(design); select_filter_active_mod(design, sel); work_stack.push_back(sel); } @@ -1441,16 +1492,16 @@ struct SelectPass : public Pass { work_stack.pop_back(); } - log_assert(design->selection_stack.size() > 0); + log_assert(!design->selection_stack.empty()); if (clear_mode) { - design->selection_stack.back() = RTLIL::Selection(true); + design->selection() = RTLIL::Selection::FullSelection(design); design->selected_active_module = std::string(); return; } if (none_mode) { - design->selection_stack.back() = RTLIL::Selection(false); + design->selection() = RTLIL::Selection::EmptySelection(design); return; } @@ -1465,28 +1516,17 @@ struct SelectPass : public Pass { if (f == nullptr) log_error("Can't open '%s' for writing: %s\n", write_file.c_str(), strerror(errno)); } - RTLIL::Selection *sel = &design->selection_stack.back(); if (work_stack.size() > 0) - sel = &work_stack.back(); + design->push_selection(work_stack.back()); + RTLIL::Selection *sel = &design->selection(); sel->optimize(design); - for (auto mod : design->modules()) + for (auto mod : design->all_selected_modules()) { if (sel->selected_whole_module(mod->name) && list_mode) log("%s\n", id2cstr(mod->name)); - if (sel->selected_module(mod->name) && !list_mod_mode) { - for (auto wire : mod->wires()) - if (sel->selected_member(mod->name, wire->name)) - LOG_OBJECT("%s/%s\n", id2cstr(mod->name), id2cstr(wire->name)) - for (auto &it : mod->memories) - if (sel->selected_member(mod->name, it.first)) - LOG_OBJECT("%s/%s\n", id2cstr(mod->name), id2cstr(it.first)) - for (auto cell : mod->cells()) - if (sel->selected_member(mod->name, cell->name)) - LOG_OBJECT("%s/%s\n", id2cstr(mod->name), id2cstr(cell->name)) - for (auto &it : mod->processes) - if (sel->selected_member(mod->name, it.first)) - LOG_OBJECT("%s/%s\n", id2cstr(mod->name), id2cstr(it.first)) - } + if (!list_mod_mode) + for (auto it : mod->selected_members()) + LOG_OBJECT("%s/%s\n", id2cstr(mod->name), id2cstr(it->name)) } if (count_mode) { @@ -1495,6 +1535,8 @@ struct SelectPass : public Pass { } if (f != nullptr) fclose(f); + if (work_stack.size() > 0) + design->pop_selection(); #undef LOG_OBJECT return; } @@ -1503,8 +1545,8 @@ struct SelectPass : public Pass { { if (work_stack.size() == 0) log_cmd_error("Nothing to add to selection.\n"); - select_op_union(design, design->selection_stack.back(), work_stack.back()); - design->selection_stack.back().optimize(design); + select_op_union(design, design->selection(), work_stack.back()); + design->selection().optimize(design); return; } @@ -1512,8 +1554,8 @@ struct SelectPass : public Pass { { if (work_stack.size() == 0) log_cmd_error("Nothing to delete from selection.\n"); - select_op_diff(design, design->selection_stack.back(), work_stack.back()); - design->selection_stack.back().optimize(design); + select_op_diff(design, design->selection(), work_stack.back()); + design->selection().optimize(design); return; } @@ -1553,23 +1595,13 @@ struct SelectPass : public Pass { if (work_stack.size() == 0) log_cmd_error("No selection to check.\n"); RTLIL::Selection *sel = &work_stack.back(); + design->push_selection(*sel); sel->optimize(design); - for (auto mod : design->modules()) - if (sel->selected_module(mod->name)) { - module_count++; - for (auto wire : mod->wires()) - if (sel->selected_member(mod->name, wire->name)) - total_count++; - for (auto &it : mod->memories) - if (sel->selected_member(mod->name, it.first)) - total_count++; - for (auto cell : mod->cells()) - if (sel->selected_member(mod->name, cell->name)) - total_count++; - for (auto &it : mod->processes) - if (sel->selected_member(mod->name, it.first)) - total_count++; - } + for (auto mod : design->all_selected_modules()) { + module_count++; + for ([[maybe_unused]] auto member_name : mod->selected_members()) + total_count++; + } if (assert_modcount >= 0 && assert_modcount != module_count) { log_error("Assertion failed: selection contains %d modules instead of the asserted %d:%s\n", @@ -1593,13 +1625,14 @@ struct SelectPass : public Pass { log_error("Assertion failed: selection contains %d elements, less than the minimum number %d:%s\n%s", total_count, assert_min, sel_str.c_str(), desc.c_str()); } + design->pop_selection(); return; } if (!set_name.empty()) { if (work_stack.size() == 0) - design->selection_vars[set_name] = RTLIL::Selection(false); + design->selection_vars[set_name] = RTLIL::Selection::EmptySelection(design); else design->selection_vars[set_name] = work_stack.back(); return; @@ -1613,7 +1646,7 @@ struct SelectPass : public Pass { } if (work_stack.size() == 0) { - RTLIL::Selection &sel = design->selection_stack.back(); + RTLIL::Selection &sel = design->selection(); if (sel.full_selection) log("*\n"); for (auto &it : sel.selected_modules) @@ -1624,8 +1657,8 @@ struct SelectPass : public Pass { return; } - design->selection_stack.back() = work_stack.back(); - design->selection_stack.back().optimize(design); + design->selection() = work_stack.back(); + design->selection().optimize(design); } } SelectPass; @@ -1665,7 +1698,8 @@ struct CdPass : public Pass { log_cmd_error("Invalid number of arguments.\n"); if (args.size() == 1 || args[1] == "/") { - design->selection_stack.back() = RTLIL::Selection(true); + design->pop_selection(); + design->push_full_selection(); design->selected_active_module = std::string(); return; } @@ -1674,7 +1708,8 @@ struct CdPass : public Pass { { string modname = design->selected_active_module; - design->selection_stack.back() = RTLIL::Selection(true); + design->pop_selection(); + design->push_full_selection(); design->selected_active_module = std::string(); while (1) @@ -1691,9 +1726,10 @@ struct CdPass : public Pass { continue; design->selected_active_module = modname; - design->selection_stack.back() = RTLIL::Selection(); - select_filter_active_mod(design, design->selection_stack.back()); - design->selection_stack.back().optimize(design); + design->pop_selection(); + design->push_full_selection(); + select_filter_active_mod(design, design->selection()); + design->selection().optimize(design); return; } @@ -1710,9 +1746,10 @@ struct CdPass : public Pass { if (design->module(modname) != nullptr) { design->selected_active_module = modname; - design->selection_stack.back() = RTLIL::Selection(); - select_filter_active_mod(design, design->selection_stack.back()); - design->selection_stack.back().optimize(design); + design->pop_selection(); + design->push_full_selection(); + select_filter_active_mod(design, design->selection()); + design->selection().optimize(design); return; } @@ -1759,7 +1796,7 @@ struct LsPass : public Pass { { std::vector matches; - for (auto mod : design->selected_modules()) + for (auto mod : design->all_selected_modules()) matches.push_back(mod->name); if (!matches.empty()) { diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 82b5c6bcf..8a1bd58c4 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -802,8 +802,8 @@ struct ShowPass : public Pass { std::pair data; data.first = args[++argidx], argidx++; handle_extra_select_args(this, args, argidx, argidx+1, design); - data.second = design->selection_stack.back(); - design->selection_stack.pop_back(); + data.second = design->selection(); + design->pop_selection(); color_selections.push_back(data); continue; } @@ -811,8 +811,8 @@ struct ShowPass : public Pass { std::pair data; data.first = args[++argidx], argidx++; handle_extra_select_args(this, args, argidx, argidx+1, design); - data.second = design->selection_stack.back(); - design->selection_stack.pop_back(); + data.second = design->selection(); + design->pop_selection(); label_selections.push_back(data); continue; } diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 23583a8a8..6fae312b4 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -468,7 +468,7 @@ struct StatPass : public Pass { first_module = false; } else { log("\n"); - log("=== %s%s ===\n", log_id(mod->name), design->selected_whole_module(mod->name) ? "" : " (partially selected)"); + log("=== %s%s ===\n", log_id(mod->name), mod->is_selected_whole() ? "" : " (partially selected)"); log("\n"); data.log_data(mod->name, false); } diff --git a/passes/cmds/viz.cc b/passes/cmds/viz.cc index 9dd68bd00..131e799ab 100644 --- a/passes/cmds/viz.cc +++ b/passes/cmds/viz.cc @@ -950,8 +950,8 @@ struct VizPass : public Pass { auto type = arg == "-g" || arg == "-G" ? VizConfig::TYPE_G : arg == "-u" || arg == "-U" ? VizConfig::TYPE_U : arg == "-x" || arg == "-X" ? VizConfig::TYPE_X : VizConfig::TYPE_S; - config.groups.push_back({type, design->selection_stack.back()}); - design->selection_stack.pop_back(); + config.groups.push_back({type, design->selection()}); + design->pop_selection(); continue; } if (arg == "-0" || arg == "-1" || arg == "-2" || arg == "-3" || arg == "-4" || diff --git a/passes/hierarchy/submod.cc b/passes/hierarchy/submod.cc index 52fd59cf8..facc5d173 100644 --- a/passes/hierarchy/submod.cc +++ b/passes/hierarchy/submod.cc @@ -246,7 +246,7 @@ struct SubmodWorker SubmodWorker(RTLIL::Design *design, RTLIL::Module *module, bool copy_mode = false, bool hidden_mode = false, std::string opt_name = std::string()) : design(design), module(module), sigmap(module), copy_mode(copy_mode), hidden_mode(hidden_mode), opt_name(opt_name) { - if (!design->selected_whole_module(module->name) && opt_name.empty()) + if (!module->is_selected_whole() && opt_name.empty()) return; if (module->processes.size() > 0) { diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index c37c03607..620b38813 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -734,7 +734,7 @@ struct CleanPass : public Pass { count_rm_cells = 0; count_rm_wires = 0; - for (auto module : design->selected_whole_modules()) { + for (auto module : design->selected_unboxed_whole_modules()) { if (module->has_processes()) continue; rmunused_module(module, purge_mode, ys_debug(), true); diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc index 88f995dda..263a3a4c8 100644 --- a/passes/sat/cutpoint.cc +++ b/passes/sat/cutpoint.cc @@ -57,7 +57,7 @@ struct CutpointPass : public Pass { for (auto module : design->selected_modules()) { - if (design->selected_whole_module(module->name)) { + if (module->is_selected_whole()) { log("Making all outputs of module %s cut points, removing module contents.\n", log_id(module)); module->new_connections(std::vector()); for (auto cell : vector(module->cells())) diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index 8fac93b98..dd2f1d255 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -2887,7 +2887,7 @@ struct SimPass : public Pass { if (!top_mod) log_cmd_error("Design has no top module, use the 'hierarchy' command to specify one.\n"); } else { - auto mods = design->selected_whole_modules(); + auto mods = design->selected_unboxed_whole_modules(); if (GetSize(mods) != 1) log_cmd_error("Only one top module must be selected.\n"); top_mod = mods.front(); @@ -3016,7 +3016,7 @@ struct Fst2TbPass : public Pass { if (!top_mod) log_cmd_error("Design has no top module, use the 'hierarchy' command to specify one.\n"); } else { - auto mods = design->selected_whole_modules(); + auto mods = design->selected_unboxed_whole_modules(); if (GetSize(mods) != 1) log_cmd_error("Only one top module must be selected.\n"); top_mod = mods.front(); diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc index a96a82659..fe5cc7af1 100644 --- a/passes/techmap/abc9.cc +++ b/passes/techmap/abc9.cc @@ -306,9 +306,10 @@ struct Abc9Pass : public ScriptPass } run("design -stash $abc9"); run("design -load $abc9_map"); - run("proc"); + if (help_mode) run("select =*"); + else active_design->push_complete_selection(); run("wbflip"); - run("techmap -wb -map %$abc9 -map +/techmap.v A:abc9_flop"); + run("techmap -autoproc -wb -map %$abc9 -map +/techmap.v A:abc9_flop"); run("opt -nodffe -nosdff"); if (dff_mode || help_mode) { if (!help_mode) @@ -369,6 +370,8 @@ struct Abc9Pass : public ScriptPass if (saved_designs.count("$abc9_holes") || help_mode) { run("design -stash $abc9"); run("design -load $abc9_holes"); + if (help_mode) run("select =*"); + else active_design->push_complete_selection(); run("techmap -wb -map %$abc9 -map +/techmap.v"); run("opt -purge"); run("aigmap"); @@ -391,7 +394,7 @@ struct Abc9Pass : public ScriptPass } else { auto selected_modules = active_design->selected_modules(); - active_design->selection_stack.emplace_back(false); + active_design->push_empty_selection(); for (auto mod : selected_modules) { if (mod->processes.size() > 0) { @@ -400,8 +403,9 @@ struct Abc9Pass : public ScriptPass } log_push(); - active_design->selection().select(mod); + active_design->select(mod); + // this check does nothing because the above line adds the whole module to the selection if (!active_design->selected_whole_module(mod)) log_error("Can't handle partially selected module %s!\n", log_id(mod)); @@ -452,7 +456,7 @@ struct Abc9Pass : public ScriptPass log_pop(); } - active_design->selection_stack.pop_back(); + active_design->pop_selection(); } } diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc index 4c7667a43..6cb569b5a 100644 --- a/passes/techmap/abc9_ops.cc +++ b/passes/techmap/abc9_ops.cc @@ -454,7 +454,7 @@ void prep_bypass(RTLIL::Design *design) void prep_dff(RTLIL::Design *design) { - auto r = design->selection_vars.insert(std::make_pair(ID($abc9_flops), RTLIL::Selection(false))); + auto r = design->selection_vars.insert(std::make_pair(ID($abc9_flops), RTLIL::Selection::EmptySelection(design))); auto &modules_sel = r.first->second; for (auto module : design->selected_modules()) diff --git a/passes/techmap/abc_new.cc b/passes/techmap/abc_new.cc index 5be823916..dfa2e2f71 100644 --- a/passes/techmap/abc_new.cc +++ b/passes/techmap/abc_new.cc @@ -139,7 +139,7 @@ struct AbcNewPass : public ScriptPass { if (!help_mode) { selected_modules = order_modules(active_design, active_design->selected_whole_modules_warn()); - active_design->selection_stack.emplace_back(false); + active_design->push_empty_selection(); } else { selected_modules = {nullptr}; run("foreach module in selection"); @@ -157,7 +157,7 @@ struct AbcNewPass : public ScriptPass { exe_options = abc_exe_options; log_header(active_design, "Mapping module '%s'.\n", log_id(mod)); log_push(); - active_design->selection().select(mod); + active_design->select(mod); } std::string script_save; @@ -194,7 +194,7 @@ struct AbcNewPass : public ScriptPass { } if (!help_mode) { - active_design->selection_stack.pop_back(); + active_design->pop_selection(); } } } diff --git a/passes/techmap/aigmap.cc b/passes/techmap/aigmap.cc index 4836ebe34..19e568a61 100644 --- a/passes/techmap/aigmap.cc +++ b/passes/techmap/aigmap.cc @@ -171,8 +171,7 @@ struct AigmapPass : public Pass { module->remove(cell); if (select_mode) { - log_assert(!design->selection_stack.empty()); - RTLIL::Selection& sel = design->selection_stack.back(); + RTLIL::Selection& sel = design->selection(); sel.selected_members[module->name] = std::move(new_sel); } diff --git a/passes/techmap/clockgate.cc b/passes/techmap/clockgate.cc index b34ad1769..2305cfc94 100644 --- a/passes/techmap/clockgate.cc +++ b/passes/techmap/clockgate.cc @@ -333,7 +333,7 @@ struct ClockgatePass : public Pass { dict clk_nets; int gated_flop_count = 0; - for (auto module : design->selected_whole_modules()) { + for (auto module : design->selected_unboxed_whole_modules()) { for (auto cell : module->cells()) { if (!RTLIL::builtin_ff_cell_types().count(cell->type)) continue; diff --git a/passes/techmap/nlutmap.cc b/passes/techmap/nlutmap.cc index 016789157..c823f10fe 100644 --- a/passes/techmap/nlutmap.cc +++ b/passes/techmap/nlutmap.cc @@ -42,7 +42,7 @@ struct NlutmapWorker RTLIL::Selection get_selection() { - RTLIL::Selection sel(false); + auto sel = RTLIL::Selection::EmptySelection(module->design); for (auto cell : module->cells()) if (!mapped_cells.count(cell)) sel.select(module, cell); diff --git a/tests/cxxrtl/run-test.sh b/tests/cxxrtl/run-test.sh index fd11a3783..ee299fc82 100755 --- a/tests/cxxrtl/run-test.sh +++ b/tests/cxxrtl/run-test.sh @@ -13,5 +13,5 @@ run_subtest value run_subtest value_fuzz # Compile-only test. -../../yosys -p "read_verilog test_unconnected_output.v; proc; clean; write_cxxrtl cxxrtl-test-unconnected_output.cc" +../../yosys -p "read_verilog test_unconnected_output.v; select =*; proc; clean; write_cxxrtl cxxrtl-test-unconnected_output.cc" ${CC:-gcc} -std=c++11 -c -o cxxrtl-test-unconnected_output -I../../backends/cxxrtl/runtime cxxrtl-test-unconnected_output.cc diff --git a/tests/select/blackboxes.ys b/tests/select/boxes.v similarity index 51% rename from tests/select/blackboxes.ys rename to tests/select/boxes.v index 9bfe92c6b..696b26523 100644 --- a/tests/select/blackboxes.ys +++ b/tests/select/boxes.v @@ -1,4 +1,3 @@ -read_verilog -specify < Date: Mon, 7 Apr 2025 21:36:50 +1200 Subject: [PATCH 15/55] rtlil.h: Fix selection ctor ordering --- kernel/rtlil.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 52e7a17e7..aa966a995 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -1183,7 +1183,7 @@ struct RTLIL::Selection // the design to select from RTLIL::Design *design = nullptr ) : - full_selection(full && !boxes), selects_boxes(boxes), complete_selection(full && boxes), current_design(design) { } + selects_boxes(boxes), complete_selection(full && boxes), full_selection(full && !boxes), current_design(design) { } // checks if the given module exists in the current design and is a // boxed module, warning the user if the current design is not set From 1ef9908a859cb7dde193b9e1d1a6e8febc90569b Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 7 Apr 2025 21:37:07 +1200 Subject: [PATCH 16/55] rtlil.cc: Fix box checks in selected_modules --- kernel/rtlil.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 637cc9be3..f835dd269 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1193,8 +1193,8 @@ void RTLIL::Design::pop_selection() std::vector RTLIL::Design::selected_modules(RTLIL::SelectPartials partials, RTLIL::SelectBoxes boxes) const { bool include_partials = partials == RTLIL::SELECT_ALL; - bool exclude_boxes = (partials & RTLIL::SB_UNBOXED_ONLY) != 0; - bool ignore_wb = (partials & RTLIL::SB_INCL_WB) != 0; + bool exclude_boxes = (boxes & RTLIL::SB_UNBOXED_ONLY) != 0; + bool ignore_wb = (boxes & RTLIL::SB_INCL_WB) != 0; std::vector result; result.reserve(modules_.size()); for (auto &it : modules_) From f410f98d891d474ea6579dfc58234bcf58baa034 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 7 Apr 2025 21:55:47 +1200 Subject: [PATCH 17/55] clean ignores boxes --- tests/select/boxes_equals_clean.ys | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 tests/select/boxes_equals_clean.ys diff --git a/tests/select/boxes_equals_clean.ys b/tests/select/boxes_equals_clean.ys deleted file mode 100644 index 71c4e392b..000000000 --- a/tests/select/boxes_equals_clean.ys +++ /dev/null @@ -1,7 +0,0 @@ -read_verilog -specify boxes.v -clean - -logger -expect-no-warnings -select -assert-count 5 =wb -clean =wb -select -assert-count 4 =wb From f042c368986755ae908fdc2ae0f58b9a05c3b368 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 8 Apr 2025 11:43:27 +1200 Subject: [PATCH 18/55] rtlil.h: Extra comment on helper enums i.e. making explicit the ones that aren't intended for direct use. --- kernel/rtlil.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/rtlil.h b/kernel/rtlil.h index aa966a995..96c8c523b 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -66,14 +66,14 @@ namespace RTLIL enum SelectBoxes : unsigned char { SB_ALL = 0, // include boxed modules - SB_WARN = 1, // helper for log_warning - SB_ERR = 2, // helper for log_error - SB_CMDERR = 3, // helper for log_cmd_error + SB_WARN = 1, // helper for log_warning (not for direct use) + SB_ERR = 2, // helper for log_error (not for direct use) + SB_CMDERR = 3, // helper for log_cmd_error (not for direct use) SB_UNBOXED_ONLY = 4, // ignore boxed modules SB_UNBOXED_WARN = 5, // call log_warning on boxed module SB_UNBOXED_ERR = 6, // call log_error on boxed module SB_UNBOXED_CMDERR = 7, // call log_cmd_error on boxed module - SB_INCL_WB = 8, // helper for white boxes + SB_INCL_WB = 8, // helper for white boxes (not for direct use) SB_EXCL_BB_ONLY = 12, // ignore black boxes, but not white boxes SB_EXCL_BB_WARN = 13, // call log_warning on black boxed module SB_EXCL_BB_ERR = 14, // call log_error on black boxed module From dbc2611dd6308262e15413c82ae4ec16145db85c Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 8 Apr 2025 11:47:07 +1200 Subject: [PATCH 19/55] test_select: Add and exercise test_select pass Developer facing, intended to check internal selection semantics work as expected. i.e. it would have revealed the bug in the now reverted PR. --- passes/cmds/Makefile.inc | 1 + passes/cmds/test_select.cc | 172 +++++++++++++++++++++++++++++++ tests/select/internal_selects.ys | 165 +++++++++++++++++++++++++++++ 3 files changed, 338 insertions(+) create mode 100644 passes/cmds/test_select.cc create mode 100644 tests/select/internal_selects.ys diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index b15e91edb..af7e1bca6 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -54,3 +54,4 @@ OBJS += passes/cmds/portarcs.o OBJS += passes/cmds/wrapcell.o OBJS += passes/cmds/setenv.o OBJS += passes/cmds/abstract.o +OBJS += passes/cmds/test_select.o diff --git a/passes/cmds/test_select.cc b/passes/cmds/test_select.cc new file mode 100644 index 000000000..0076500ce --- /dev/null +++ b/passes/cmds/test_select.cc @@ -0,0 +1,172 @@ +#include "kernel/yosys.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct TestSelectPass : public Pass { + TestSelectPass() : Pass("test_select", "call internal selection methods on design for testing purposes") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" test_select [options]\n"); + log("\n"); + log("Test semantics of internal 'RTLIL::Design::selected_modules()' by modifying the\n"); + log("current selection to only include the results of the call.\n"); + log("\n"); + log("Includes partially selected modules by default, use one of the following options\n"); + log("to remove them instead:\n"); + log("\n"); + log(" -whole_only\n"); + log("\n"); + log(" -whole_warn\n"); + log(" -whole_err\n"); + log(" -whole_cmderr\n"); + log(" remove partially selected modules, raising warning, error, or cmd error\n"); + log("\n"); + log(" test_select -unboxed_only [options]\n"); + log("\n"); + log("Remove boxed modules from selection.\n"); + log("\n"); + log(" -include_wb\n"); + log(" don't remove white boxes from selection\n"); + log("\n"); + log(" -warn_boxes\n"); + log(" -err_boxes\n"); + log(" -cmderr_boxes\n"); + log(" raise warning, error, or cmd error if a box is removed\n"); + log("\n"); + } + void execute(vector args, RTLIL::Design *design) override + { + log_header(design, "Executing TEST_SELECTION pass.\n"); + bool whole_only = false; + bool whole_warn = false; + bool whole_err = false; + bool whole_cmderr = false; + int whole_opts = 0; + + bool warn_boxes = false; + bool err_boxes = false; + bool cmderr_boxes = false; + int box_level = 0; + + bool unboxed_only = false; + bool include_wb = false; + + int argidx; + for (argidx = 1; argidx < GetSize(args); argidx++) + { + if (args[argidx] == "-whole_only") { + whole_only = true; + whole_opts++; + continue; + } + if (args[argidx] == "-whole_warn") { + whole_warn = true; + whole_opts++; + continue; + } + if (args[argidx] == "-whole_err") { + whole_err = true; + whole_opts++; + continue; + } + if (args[argidx] == "-whole_cmderr") { + whole_cmderr = true; + whole_opts++; + continue; + } + if (args[argidx] == "-warn_boxes") { + warn_boxes = true; + box_level++; + continue; + } + if (args[argidx] == "-err_boxes") { + err_boxes = true; + box_level++; + continue; + } + if (args[argidx] == "-cmderr_boxes") { + cmderr_boxes = true; + box_level++; + continue; + } + if (args[argidx] == "-unboxed_only") { + unboxed_only = true; + continue; + } + if (args[argidx] == "-include_wb") { + include_wb = true; + continue; + } + break; + } + + if (whole_opts > 1) + log_cmd_error("Only one of -whole_only, -whole_warn, -whole_err, or -whole_cmderr may be selected.\n"); + + if (include_wb && !unboxed_only) + log_cmd_error("-include_wb option requires -unboxed_only.\n"); + + if (box_level > 0 && !unboxed_only) + log_cmd_error("-*_boxes options require -unboxed_only.\n"); + + if (box_level > 1) + log_cmd_error("Only one of -warn_boxes, -err_boxes, or -cmderr_boxes may be selected.\n"); + + extra_args(args, argidx, design, false); + + // construct enums + RTLIL::SelectPartials partials; + if (whole_only) + partials = RTLIL::SELECT_WHOLE_ONLY; + else if (whole_warn) + partials = RTLIL::SELECT_WHOLE_WARN; + else if (whole_err) + partials = RTLIL::SELECT_WHOLE_ERR; + else if (whole_cmderr) + partials = RTLIL::SELECT_WHOLE_CMDERR; + else + partials = RTLIL::SELECT_ALL; + + char boxes = RTLIL::SB_ALL; + if (warn_boxes) boxes |= RTLIL::SB_WARN; + if (err_boxes) boxes |= RTLIL::SB_ERR; + if (cmderr_boxes) boxes |= RTLIL::SB_CMDERR; + if (unboxed_only) boxes |= RTLIL::SB_UNBOXED_ONLY; + if (include_wb) boxes |= RTLIL::SB_INCL_WB; + + // get sub selection and store the results + auto sub_sel = design->selected_modules(partials, (RTLIL::SelectBoxes)boxes); + pool selected_modules; + dict> selected_members; + + for (auto *mod : sub_sel) { + if (mod->is_selected_whole()) { + log_debug(" Adding %s.\n", id2cstr(mod->name)); + selected_modules.insert(mod->name); + } else for (auto *memb : mod->selected_members()) { + log_debug(" Adding %s.%s.\n", id2cstr(mod->name), id2cstr(memb->name)); + selected_members[mod->name].insert(memb); + } + } + + // fully reset current selection + design->selection() = RTLIL::Selection::EmptySelection(design); + + // add back sub selection + for (auto modname : selected_modules) + design->selection().select(design->module(modname)); + for (auto &it : selected_members) { + auto mod = design->module(it.first); + for (auto memb : it.second) + design->selection().select(mod, memb); + } + + // optimize + design->selection().optimize(design); + } +} TestSelectPass; + +PRIVATE_NAMESPACE_END diff --git a/tests/select/internal_selects.ys b/tests/select/internal_selects.ys new file mode 100644 index 000000000..67b7cf63f --- /dev/null +++ b/tests/select/internal_selects.ys @@ -0,0 +1,165 @@ +read_verilog boxes.v + +## base case, no warnings ## +logger -expect-no-warnings + +select =* +select -assert-count 13 % +select -assert-mod-count 3 % +test_select # SELECT_ALL && SB_ALL +select -assert-count 13 % +select -assert-mod-count 3 % + +select =* +select -assert-mod-count 3 % +test_select -whole_only # SELECT_WHOLE_ONLY +select -assert-mod-count 3 % + +select =* +select -assert-count 13 % +test_select -unboxed_only -include_wb # SB_EXCL_BB_ONLY +select -assert-count 10 % + +select =* +select -assert-count 13 % +test_select -unboxed_only # SB_UNBOXED_ONLY +select -assert-count 5 % + +logger -check-expected + +## don't add to selection ## +select * +select -assert-count 5 % +select -assert-mod-count 1 % +test_select # SELECT_ALL && SB_ALL +select -assert-count 5 % +select -assert-mod-count 1 % + +select =wb/a %n +select -assert-mod-count 3 % +test_select -whole_only # SELECT_WHOLE_ONLY +select -assert-mod-count 2 % + +select =w:* +select -assert-mod-count 3 % +test_select -whole_only # SELECT_WHOLE_ONLY +select -assert-mod-count 1 % + +select =w:* +select -assert-mod-count 3 % +test_select -whole_only -unboxed_only # SELECT_WHOLE_ONLY && SB_UNBOXED_ONLY +select -assert-none % + +select =w:* %n +select -assert-mod-count 2 % +test_select -whole_only # SELECT_WHOLE_ONLY +select -assert-none % + +select =?b +select -assert-count 8 % +test_select -unboxed_only -include_wb # SB_EXCL_BB_ONLY +select -assert-count 5 % + +select =?b +select -assert-count 8 % +test_select -unboxed_only # SB_UNBOXED_ONLY +select -assert-none % + +select =wb %n +select -assert-count 8 % +test_select -unboxed_only -include_wb # SB_EXCL_BB_ONLY +select -assert-count 5 % + +select =top/a %n +select -assert-count 12 % +select -assert-mod-count 3 % +test_select -whole_only -unboxed_only -include_wb # SELECT_WHOLE_ONLY && SB_EXCL_BB_ONLY +select -assert-count 5 % +select -assert-mod-count 1 % + +## warnings work ## +logger -expect warning "Ignoring blackbox module bb." 1 +select =* +test_select -unboxed_only -include_wb -warn_boxes # SB_EXCL_BB_WARN +logger -check-expected + +logger -expect warning "Ignoring boxed module .b." 2 +select =* +test_select -unboxed_only -warn_boxes # SB_UNBOXED_WARN +logger -check-expected + +logger -expect warning "Ignoring partially selected module [wb|top]." 2 +select =w:* +test_select -whole_warn # SELECT_WHOLE_WARN +logger -check-expected + +logger -expect warning "Ignoring partially selected module [wb|top]." 2 +logger -expect warning "Ignoring blackbox module bb." 1 +select =w:* +test_select -whole_warn -unboxed_only -include_wb -warn_boxes # SELECT_WHOLE_WARN && SB_EXCL_BB_WARN +logger -check-expected + +logger -expect warning "Ignoring partially selected module [wb|top]." 2 +logger -expect warning "Ignoring boxed module bb." 1 +select =w:* +test_select -whole_warn -unboxed_only -warn_boxes # SELECT_WHOLE_WARN && SB_UNBOXED_WARN +logger -check-expected + +## partials warn before boxes ## +logger -expect warning "Ignoring partially selected module wb." 1 +select =wb/a +test_select -whole_warn -unboxed_only -warn_boxes # SELECT_WHOLE_WARN && SB_UNBOXED_WARN +logger -check-expected + +## boxes won't warn if they've been removed because of partial selection ## +logger -expect-no-warnings +select =wb/a +test_select -whole_only -unboxed_only -warn_boxes # SELECT_WHOLE_ONLY && SB_UNBOXED_WARN +logger -check-expected + +## boxes still warn if they're not partially selected +logger -expect warning "Ignoring boxed module wb." 1 +select =bb %n +test_select -whole_only -unboxed_only -warn_boxes # SELECT_WHOLE_ONLY && SB_UNBOXED_WARN +logger -check-expected + +## don't warn if it's not selected ## +logger -expect-no-warnings +select =bb %n +test_select -unboxed_only -include_wb -warn_boxes # SB_EXCL_BB_WARN +logger -check-expected + +logger -expect-no-warnings +select * +test_select -unboxed_only -warn_boxes # SB_UNBOXED_WARN +logger -check-expected + +logger -expect warning "Ignoring boxed module bb." 1 +select =wb %n +test_select -unboxed_only -warn_boxes # SB_UNBOXED_WARN +logger -check-expected + +## don't error if it's not selected ## +logger -expect-no-warnings + +select =bb %n +test_select -unboxed_only -include_wb -err_boxes # SB_EXCL_BB_ERR + +select =bb %n +test_select -unboxed_only -include_wb -cmderr_boxes # SB_EXCL_BB_CMDERR + +select * +test_select -unboxed_only -err_boxes # SB_UNBOXED_ERR + +select * +test_select -unboxed_only -cmderr_boxes # SB_UNBOXED_CMDERR + +select * +test_select -whole_err # SELECT_WHOLE_ERR + +select * +test_select -whole_cmderr # SELECT_WHOLE_CMDERR + +logger -check-expected + +## From 911a3ae759ef686f4958d2f5d06b53ad525c4e5d Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:34:48 +1200 Subject: [PATCH 20/55] setattr.cc: Use new selection helpers Also test they work as expected. --- passes/cmds/setattr.cc | 34 +++++------------------------- tests/select/boxes_setattr.ys | 39 +++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 29 deletions(-) create mode 100644 tests/select/boxes_setattr.ys diff --git a/passes/cmds/setattr.cc b/passes/cmds/setattr.cc index 710fa9ab4..f590c2fa9 100644 --- a/passes/cmds/setattr.cc +++ b/passes/cmds/setattr.cc @@ -96,32 +96,16 @@ struct SetattrPass : public Pass { } extra_args(args, argidx, design); - for (auto module : design->modules()) + for (auto module : design->all_selected_modules()) { if (flag_mod) { - if (design->selected_whole_module(module->name)) + if (module->is_selected_whole()) do_setunset(module->attributes, setunset_list); continue; } - if (!design->selected(module)) - continue; - - for (auto wire : module->wires()) - if (design->selected(module, wire)) - do_setunset(wire->attributes, setunset_list); - - for (auto &it : module->memories) - if (design->selected(module, it.second)) - do_setunset(it.second->attributes, setunset_list); - - for (auto cell : module->cells()) - if (design->selected(module, cell)) - do_setunset(cell->attributes, setunset_list); - - for (auto &it : module->processes) - if (design->selected(module, it.second)) - do_setunset(it.second->attributes, setunset_list); + for (auto memb : module->selected_members()) + do_setunset(memb->attributes, setunset_list); } } } SetattrPass; @@ -152,16 +136,8 @@ struct WbflipPass : public Pass { } extra_args(args, argidx, design); - for (Module *module : design->modules()) - { - if (!design->selected(module)) - continue; - - if (module->get_bool_attribute(ID::blackbox)) - continue; - + for (auto *module : design->selected_modules(RTLIL::SELECT_ALL, RTLIL::SB_EXCL_BB_ONLY)) module->set_bool_attribute(ID::whitebox, !module->get_bool_attribute(ID::whitebox)); - } } } WbflipPass; diff --git a/tests/select/boxes_setattr.ys b/tests/select/boxes_setattr.ys new file mode 100644 index 000000000..6a7383d58 --- /dev/null +++ b/tests/select/boxes_setattr.ys @@ -0,0 +1,39 @@ +read_verilog -specify boxes.v + +design -save read +select -assert-none =a:test_attr +select -assert-none =A:test_attr + +# setattr =* affects all modules +setattr -set test_attr 1 =* +select -assert-mod-count 3 =a:test_attr +select -assert-none =A:test_attr + +design -load read +setattr -mod -set test_attr 1 =* +select -assert-none =a:test_attr +select -assert-mod-count 3 =A:test_attr + +# setattr * doesn't affect boxed modules +design -load read +setattr -mod -set test_attr 1 * +select -assert-mod-count 1 =A:test_attr + +# setattr can set and unset whitebox attr +design -load read +setattr -mod -unset whitebox =wb +select -assert-mod-count 2 * +setattr -mod -set whitebox 1 wb +select -assert-mod-count 1 * + +# wbflip works on all non-bb in selection +design -load read +select -assert-mod-count 1 =A:whitebox +wbflip +select -assert-mod-count 2 =A:whitebox +wbflip +select -assert-mod-count 2 =A:whitebox +wbflip =wb +select -assert-mod-count 1 =A:whitebox +wbflip =bb +select -assert-mod-count 1 =A:whitebox From 237e4541315faacfcb449a7cccbe4db83dfd7e49 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 8 Apr 2025 16:35:12 +1200 Subject: [PATCH 21/55] design.cc: Fix selections when copying Use `Design::selected_modules()` directly, popping at the end instead of copying the selection. Also default to a complete selection so that boxes work as before. Simplify to using `RTLIL::SELECT_WHOLE_CMDERR` instead of doing it manually. Also add tests for importing selections with boxes. --- passes/cmds/design.cc | 17 ++++++----------- tests/select/boxes_import.ys | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 tests/select/boxes_import.ys diff --git a/passes/cmds/design.cc b/passes/cmds/design.cc index 910c9e366..efc72dfb7 100644 --- a/passes/cmds/design.cc +++ b/passes/cmds/design.cc @@ -213,22 +213,15 @@ struct DesignPass : public Pass { if (copy_from_design != design && argidx == args.size() && !import_mode) cmd_error(args, argidx, "Missing selection."); - RTLIL::Selection sel; if (argidx != args.size()) { handle_extra_select_args(this, args, argidx, args.size(), copy_from_design); - sel = copy_from_design->selection(); - copy_from_design->pop_selection(); argidx = args.size(); + } else { + copy_from_design->push_complete_selection(); } - for (auto mod : copy_from_design->modules()) { - if (sel.selected_whole_module(mod->name)) { - copy_src_modules.push_back(mod); - continue; - } - if (sel.selected_module(mod->name)) - log_cmd_error("Module %s is only partly selected.\n", log_id(mod->name)); - } + for (auto mod : copy_from_design->selected_modules(RTLIL::SELECT_WHOLE_CMDERR, RTLIL::SB_ALL)) + copy_src_modules.push_back(mod); if (import_mode) { std::vector candidates; @@ -246,6 +239,8 @@ struct DesignPass : public Pass { if (GetSize(candidates) == 1) copy_src_modules = std::move(candidates); } + + copy_from_design->pop_selection(); } extra_args(args, argidx, design, false); diff --git a/tests/select/boxes_import.ys b/tests/select/boxes_import.ys new file mode 100644 index 000000000..d5b414e05 --- /dev/null +++ b/tests/select/boxes_import.ys @@ -0,0 +1,29 @@ +read_verilog -specify boxes.v +design -save read + +logger -expect-no-warnings + +delete =bb %n +select -assert-mod-count 1 =* +design -stash just_bb + +design -import just_bb +select -assert-mod-count 0 * +select -assert-mod-count 1 =* +design -reset + +design -import just_bb -as new +select -assert-mod-count 0 * +select -assert-mod-count 1 =* +design -reset + +design -import read -as new_top top +design -import read -as new_bb =bb +select -assert-mod-count 1 * +select -assert-mod-count 2 =* + +logger -check-expected + +logger -expect warning "Selection .wb. did not match any module\." 1 +logger -expect error "No top module found in source design\." 1 +design -import read -as new_wb wb From 078602d711611c2823cf9a04071481103cde5385 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 8 Apr 2025 16:58:59 +1200 Subject: [PATCH 22/55] tests/arch/xilinx: Fix for warnings on boxes The two test scripts affected use boxed modules directly; under normal usage the warning shouldn't appear. --- tests/arch/xilinx/abc9_dff.ys | 1 + tests/arch/xilinx/dsp_abc9.ys | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/arch/xilinx/abc9_dff.ys b/tests/arch/xilinx/abc9_dff.ys index 79e5a322c..9101c6e4d 100644 --- a/tests/arch/xilinx/abc9_dff.ys +++ b/tests/arch/xilinx/abc9_dff.ys @@ -1,4 +1,5 @@ logger -nowarn "Yosys has only limited support for tri-state logic at the moment\. .*" +logger -nowarn "Ignoring boxed module .*\." read_verilog < Date: Wed, 9 Apr 2025 10:42:40 +0200 Subject: [PATCH 23/55] wheels: fix builds, add linux aarch64 to ci Essentially, something is attempting to build the Yosys EXE when you build libyosys.so now. With `ENABLE_PYTHON_CONFIG_EMBED=0`, the Yosys EXE will always fail to build. Furthermore, because `ENABLE_PYOSYS` now attempts to build a wheel, building a wheel has become recursive. This commit uses a supplementary set of libs for the EXE (EXE_LIBS) so it and libyosys.so can be built simultaneously, as well as a new Makefile flag, `ENABLE_WHEEL`, to prevent the aforementioned recursion. I also enabled aarch64 Linux in the CI because it's publicly available now. --- .github/workflows/wheels.yml | 16 ++++++---------- Makefile | 15 ++++++++++----- setup.py | 4 ++-- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index d66239a16..3a3d240c4 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -14,16 +14,12 @@ jobs: runner: "ubuntu-22.04", archs: "x86_64", }, - ## Aarch64 is disabled for now: GitHub is committing to EOY - ## for free aarch64 runners for open-source projects and - ## emulation times out: - ## https://github.com/orgs/community/discussions/19197#discussioncomment-10550689 - # { - # name: "Ubuntu 22.04", - # family: "linux", - # runner: "ubuntu-22.04", - # archs: "aarch64", - # }, + { + name: "Ubuntu 22.04", + family: "linux", + runner: "ubuntu-22.04-arm", + archs: "aarch64", + }, { name: "macOS 13", family: "macos", diff --git a/Makefile b/Makefile index 89723e99d..74951b8cb 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,7 @@ ENABLE_ZLIB := 1 # python wrappers ENABLE_PYOSYS := 0 +ENABLE_WHEEL := $(ENABLE_PYOSYS) # other configuration flags ENABLE_GCOV := 0 @@ -102,6 +103,7 @@ LIBS := $(LIBS) -lstdc++ -lm PLUGIN_LINKFLAGS := PLUGIN_LIBS := EXE_LINKFLAGS := +EXE_LIBS := ifeq ($(OS), MINGW) EXE_LINKFLAGS := -Wl,--export-all-symbols -Wl,--out-implib,libyosys_exe.a PLUGIN_LINKFLAGS += -L"$(LIBDIR)" @@ -209,11 +211,11 @@ PYTHON_VERSION_TESTCODE := "import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.versi PYTHON_VERSION := $(shell $(PYTHON_EXECUTABLE) -c ""$(PYTHON_VERSION_TESTCODE)"") PYTHON_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.) -ENABLE_PYTHON_CONFIG_EMBED ?= $(shell $(PYTHON_EXECUTABLE)-config --embed --libs > /dev/null && echo 1) -ifeq ($(ENABLE_PYTHON_CONFIG_EMBED),1) -PYTHON_CONFIG := $(PYTHON_EXECUTABLE)-config --embed -else PYTHON_CONFIG := $(PYTHON_EXECUTABLE)-config +PYTHON_CONFIG_FOR_EXE := $(PYTHON_CONFIG) +PYTHON_CONFIG_EMBED_AVAILABLE ?= $(shell $(PYTHON_EXECUTABLE)-config --embed --libs > /dev/null && echo 1) +ifeq ($(PYTHON_CONFIG_EMBED_AVAILABLE),1) +PYTHON_CONFIG_FOR_EXE := $(PYTHON_CONFIG) --embed endif PYTHON_DESTDIR := $(shell $(PYTHON_EXECUTABLE) -c "import site; print(site.getsitepackages()[-1]);") @@ -346,8 +348,11 @@ ifeq ($(ENABLE_PYOSYS),1) # python-config --ldflags includes -l and -L, but LINKFLAGS is only -L LINKFLAGS += $(filter-out -l%,$(shell $(PYTHON_CONFIG) --ldflags)) LIBS += $(shell $(PYTHON_CONFIG) --libs) +EXE_LIBS += $(filter-out $(LIBS),$(shell $(PYTHON_CONFIG_FOR_EXE) --libs)) CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON +ifeq ($(ENABLE_WHEEL),1) EXTRA_TARGETS += wheel +endif # Detect name of boost_python library. Some distros use boost_python-py, other boost_python, some only use the major version number, some a concatenation of major and minor version numbers CHECK_BOOST_PYTHON = (echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(LINKFLAGS) $(LIBS) -l$(1) - > /dev/null 2>&1 && echo "-l$(1)") @@ -735,7 +740,7 @@ share: $(EXTRA_TARGETS) @echo "" $(PROGRAM_PREFIX)yosys$(EXE): $(OBJS) - $(P) $(CXX) -o $(PROGRAM_PREFIX)yosys$(EXE) $(EXE_LINKFLAGS) $(LINKFLAGS) $(OBJS) $(LIBS) $(LIBS_VERIFIC) + $(P) $(CXX) -o $(PROGRAM_PREFIX)yosys$(EXE) $(EXE_LINKFLAGS) $(LINKFLAGS) $(OBJS) $(EXE_LIBS) $(LIBS) $(LIBS_VERIFIC) libyosys.so: $(filter-out kernel/driver.o,$(OBJS)) ifeq ($(OS), Darwin) diff --git a/setup.py b/setup.py index b39b579e0..8a37a91b3 100644 --- a/setup.py +++ b/setup.py @@ -40,8 +40,8 @@ class libyosys_so_ext(Extension): ) self.args = [ "ENABLE_PYOSYS=1", - # Wheel meant to be imported from interpreter - "ENABLE_PYTHON_CONFIG_EMBED=0", + # Prevent recursive wheel build + "ENABLE_WHEEL=0", # Would need to be installed separately by the user "ENABLE_TCL=0", "ENABLE_READLINE=0", From 414dc855730ce27b1b49bc50cc97ef4240b42ad4 Mon Sep 17 00:00:00 2001 From: Kelvin Chung Date: Thu, 10 Apr 2025 00:01:50 +0100 Subject: [PATCH 24/55] Correct and more test --- passes/techmap/constmap.cc | 2 +- tests/techmap/constmap.ys | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/passes/techmap/constmap.cc b/passes/techmap/constmap.cc index 59bffbeb6..8f3235991 100644 --- a/passes/techmap/constmap.cc +++ b/passes/techmap/constmap.cc @@ -77,6 +77,6 @@ struct ConstmapPass : public Pass { module->rewrite_sigspecs(constmap_worker); } } -} HilomapPass; +} ConstmapPass; PRIVATE_NAMESPACE_END diff --git a/tests/techmap/constmap.ys b/tests/techmap/constmap.ys index 2854f2e57..fbaca7662 100644 --- a/tests/techmap/constmap.ys +++ b/tests/techmap/constmap.ys @@ -16,6 +16,7 @@ design -reset read_verilog -lib << EOT module const_cell(O); +parameter value=0; output O; endmodule EOT @@ -33,8 +34,30 @@ endmodule EOT constmap -cell const_cell O value + select -assert-count 2 t:const_cell select -assert-count 1 r:value=16 select -assert-count 1 r:value=32 select -assert-count 1 test/out1 %ci* r:value=16 %i select -assert-count 1 test/out2 %ci* r:value=32 %i +select -assert-count 1 t:const_cell r:value=16 %i +select -assert-count 1 t:const_cell r:value=32 %i + +design -reset + +read_verilog << EOT + +module test(); + wire [31:0] in; + wire [31:0] out1; + wire [31:0] out2; + assign out1 = in + 16; + assign out2 = in + 32; +endmodule + +EOT + +constmap -cell const_cell O value + +select -assert-count 1 t:const_cell r:value=16 %i +select -assert-count 1 t:const_cell r:value=32 %i From 33c57937cd6417dd91d34d67d6158ff550544834 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 10 Apr 2025 00:22:42 +0000 Subject: [PATCH 25/55] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e6ac10348..deedbe17b 100644 --- a/Makefile +++ b/Makefile @@ -159,7 +159,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.52+0 +YOSYS_VER := 0.52+10 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From 583771ef5b8fd847c012c60f165ed9e1b8aa2db3 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 11 Apr 2025 04:12:34 +1200 Subject: [PATCH 26/55] cutpoint: Add -blackbox option Replace the contents of all blackboxes in the design with a formal cut point. Includes test script. --- passes/sat/cutpoint.cc | 27 ++++++++++++++++++++++-- tests/various/cutpoint_blackbox.ys | 33 ++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 tests/various/cutpoint_blackbox.ys diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc index 263a3a4c8..171ae060a 100644 --- a/passes/sat/cutpoint.cc +++ b/passes/sat/cutpoint.cc @@ -37,10 +37,15 @@ struct CutpointPass : public Pass { log(" set cutpoint nets to undef (x). the default behavior is to create\n"); log(" an $anyseq cell and drive the cutpoint net from that\n"); log("\n"); + log(" cutpoint -blackbox [options]\n"); + log("\n"); + log("Replace the contents of all blackboxes in the design with a formal cut point.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { - bool flag_undef = false; + bool flag_undef = false; + bool flag_blackbox = false; log_header(design, "Executing CUTPOINT pass.\n"); @@ -51,11 +56,25 @@ struct CutpointPass : public Pass { flag_undef = true; continue; } + if (args[argidx] == "-blackbox") { + flag_blackbox = true; + continue; + } break; } extra_args(args, argidx, design); - for (auto module : design->selected_modules()) + if (flag_blackbox) { + if (!design->full_selection()) + log_cmd_error("This command only operates on fully selected designs!\n"); + RTLIL::Selection module_boxes(false); + for (auto module : design->modules()) + if (module->get_blackbox_attribute()) + module_boxes.select(module); + design->selection_stack.push_back(module_boxes); + } + + for (auto module : design->all_selected_modules()) { if (module->is_selected_whole()) { log("Making all outputs of module %s cut points, removing module contents.\n", log_id(module)); @@ -68,6 +87,10 @@ struct CutpointPass : public Pass { output_wires.push_back(wire); for (auto wire : output_wires) module->connect(wire, flag_undef ? Const(State::Sx, GetSize(wire)) : module->Anyseq(NEW_ID, GetSize(wire))); + if (module->get_blackbox_attribute()) { + module->set_bool_attribute(ID::blackbox, false); + module->set_bool_attribute(ID::whitebox, false); + } continue; } diff --git a/tests/various/cutpoint_blackbox.ys b/tests/various/cutpoint_blackbox.ys new file mode 100644 index 000000000..78ee46db6 --- /dev/null +++ b/tests/various/cutpoint_blackbox.ys @@ -0,0 +1,33 @@ +read_verilog -specify << EOT +module top(input a, b, output o); + wire c, d; + bb bb1 (.a (a), .b (b), .o (c)); + wb wb1 (.a (a), .b (b), .o (d)); + some_mod some_inst (.a (c), .b (d), .o (o)); +endmodule + +(* blackbox *) +module bb(input a, b, output o); +assign o = a | b; +specify + (a => o) = 1; +endspecify +endmodule + +(* whitebox *) +module wb(input a, b, output o); +assign o = a ^ b; +endmodule + +module some_mod(input a, b, output o); +assign o = a & b; +endmodule +EOT + +select top + +select -assert-count 0 t:$anyseq +select -assert-count 2 =t:?b +cutpoint -blackbox =* +select -assert-count 2 t:$anyseq +select -assert-count 2 t:?b From ca57df89277edb4394dd140cabb5937bc2dfada4 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 11 Apr 2025 04:12:34 +1200 Subject: [PATCH 27/55] cutpoint: Add $scopeinfo cell Also adds "blackbox" as a valid TYPE. --- kernel/rtlil.cc | 2 +- passes/sat/cutpoint.cc | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index f835dd269..dd78b202d 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -2140,7 +2140,7 @@ namespace { param(ID::TYPE); check_expected(); std::string scope_type = cell->getParam(ID::TYPE).decode_string(); - if (scope_type != "module" && scope_type != "struct") + if (scope_type != "module" && scope_type != "struct" && scope_type != "blackbox") error(__LINE__); return; } diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc index 171ae060a..573dfbd0a 100644 --- a/passes/sat/cutpoint.cc +++ b/passes/sat/cutpoint.cc @@ -90,6 +90,8 @@ struct CutpointPass : public Pass { if (module->get_blackbox_attribute()) { module->set_bool_attribute(ID::blackbox, false); module->set_bool_attribute(ID::whitebox, false); + auto scopeinfo = module->addCell(NEW_ID, ID($scopeinfo)); + scopeinfo->setParam(ID::TYPE, RTLIL::Const("blackbox")); } continue; } From cf44a9124fdf4c674e8914dafbb31942fec371ac Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 11 Apr 2025 04:12:34 +1200 Subject: [PATCH 28/55] cutpoint: Test -blackbox with parameter Modify `cutpoint_blackbox.ys` to check that parameters on blackbox modules are maintained after the cutpoint. Also adjusts the test to check that each instance gets the `$anyseq` cell. --- tests/various/cutpoint_blackbox.ys | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/tests/various/cutpoint_blackbox.ys b/tests/various/cutpoint_blackbox.ys index 78ee46db6..ee7a18c6a 100644 --- a/tests/various/cutpoint_blackbox.ys +++ b/tests/various/cutpoint_blackbox.ys @@ -1,13 +1,14 @@ read_verilog -specify << EOT module top(input a, b, output o); - wire c, d; - bb bb1 (.a (a), .b (b), .o (c)); - wb wb1 (.a (a), .b (b), .o (d)); - some_mod some_inst (.a (c), .b (d), .o (o)); + wire c, d, e; + bb #(.SOME_PARAM(1)) bb1 (.a (a), .b (b), .o (c)); + bb #(.SOME_PARAM(2)) bb2 (.a (a), .b (b), .o (d)); + wb wb1 (.a (a), .b (b), .o (e)); + some_mod some_inst (.a (c), .b (d), .c (e), .o (o)); endmodule (* blackbox *) -module bb(input a, b, output o); +module bb #( parameter SOME_PARAM=0 ) (input a, b, output o); assign o = a | b; specify (a => o) = 1; @@ -19,15 +20,20 @@ module wb(input a, b, output o); assign o = a ^ b; endmodule -module some_mod(input a, b, output o); -assign o = a & b; +module some_mod(input a, b, c, output o); +assign o = a & (b | c); endmodule EOT -select top +hierarchy -top top select -assert-count 0 t:$anyseq -select -assert-count 2 =t:?b -cutpoint -blackbox =* -select -assert-count 2 t:$anyseq -select -assert-count 2 t:?b +select -assert-count 3 =t:?b +cutpoint -blackbox + +select -assert-count 3 =t:?b +select -assert-count 2 r:SOME_PARAM +select -assert-count 1 r:SOME_PARAM=1 + +flatten +select -assert-count 3 t:$anyseq From b705c546ea71e72906590578435d347144f84dff Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 11 Apr 2025 04:12:34 +1200 Subject: [PATCH 29/55] cutpoint: Add -blackbox -instances Replace module instances instead of module contents. This fixes parametrisable width mismatch with read_verilog frontend, but not verific frontend. --- passes/sat/cutpoint.cc | 49 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc index 573dfbd0a..58cb4a2aa 100644 --- a/passes/sat/cutpoint.cc +++ b/passes/sat/cutpoint.cc @@ -41,11 +41,15 @@ struct CutpointPass : public Pass { log("\n"); log("Replace the contents of all blackboxes in the design with a formal cut point.\n"); log("\n"); + log(" -instances\n"); + log(" replace instances of blackboxes instead of the modules\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { bool flag_undef = false; bool flag_blackbox = false; + bool flag_instances = false; log_header(design, "Executing CUTPOINT pass.\n"); @@ -60,23 +64,39 @@ struct CutpointPass : public Pass { flag_blackbox = true; continue; } + if (args[argidx] == "-instances") { + flag_instances = true; + continue; + } break; } extra_args(args, argidx, design); + if (flag_instances && !flag_blackbox) { + log_cmd_error("-instances flag only valid with -blackbox!\n"); + } + if (flag_blackbox) { if (!design->full_selection()) log_cmd_error("This command only operates on fully selected designs!\n"); - RTLIL::Selection module_boxes(false); + RTLIL::Selection boxes(false); for (auto module : design->modules()) - if (module->get_blackbox_attribute()) - module_boxes.select(module); - design->selection_stack.push_back(module_boxes); + if (flag_instances) { + for (auto cell : module->cells()) { + auto mod = design->module(cell->type); + if (mod != nullptr && mod->get_blackbox_attribute()) + boxes.select(module, cell); + } + } else { + if (module->get_blackbox_attribute()) + boxes.select(module); + } + design->selection_stack.push_back(boxes); } for (auto module : design->all_selected_modules()) { - if (module->is_selected_whole()) { + if (module->is_selected_whole() && !flag_instances) { log("Making all outputs of module %s cut points, removing module contents.\n", log_id(module)); module->new_connections(std::vector()); for (auto cell : vector(module->cells())) @@ -107,7 +127,26 @@ struct CutpointPass : public Pass { if (cell->output(conn.first)) module->connect(conn.second, flag_undef ? Const(State::Sx, GetSize(conn.second)) : module->Anyseq(NEW_ID, GetSize(conn.second))); } + + RTLIL::Cell *scopeinfo = nullptr; + auto cell_name = cell->name; + if (flag_instances && cell_name.isPublic()) { + auto scopeinfo = module->addCell(NEW_ID, ID($scopeinfo)); + scopeinfo->setParam(ID::TYPE, RTLIL::Const("blackbox")); + + for (auto const &attr : cell->attributes) + { + if (attr.first == ID::hdlname) + scopeinfo->attributes.insert(attr); + else + scopeinfo->attributes.emplace(stringf("\\cell_%s", RTLIL::unescape_id(attr.first).c_str()), attr.second); + } + } + module->remove(cell); + + if (scopeinfo != nullptr) + module->rename(scopeinfo, cell_name); } for (auto wire : module->selected_wires()) { From 44545653ef9313bc8edb28cd13a867b01a1aea12 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 11 Apr 2025 04:12:34 +1200 Subject: [PATCH 30/55] hierarchy: Ignore width mismatch from verific But only if it's also a blackbox module with parameters (i.e. it *could* be parametrizable width). --- passes/hierarchy/hierarchy.cc | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 8372c0339..d94b5cef6 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -978,6 +978,11 @@ struct HierarchyPass : public Pass { } } + bool verific_mod = false; +#ifdef YOSYS_ENABLE_VERIFIC + verific_mod = verific_import_pending; +#endif + if (top_mod == nullptr && !load_top_mod.empty()) { #ifdef YOSYS_ENABLE_VERIFIC if (verific_import_pending) { @@ -1418,13 +1423,18 @@ struct HierarchyPass : public Pass { if (m == nullptr) continue; - if (m->get_blackbox_attribute() && !cell->parameters.empty() && m->get_bool_attribute(ID::dynports)) { - IdString new_m_name = m->derive(design, cell->parameters, true); - if (new_m_name.empty()) - continue; - if (new_m_name != m->name) { - m = design->module(new_m_name); - blackbox_derivatives.insert(m); + bool boxed_params = false; + if (m->get_blackbox_attribute() && !cell->parameters.empty()) { + if (m->get_bool_attribute(ID::dynports)) { + IdString new_m_name = m->derive(design, cell->parameters, true); + if (new_m_name.empty()) + continue; + if (new_m_name != m->name) { + m = design->module(new_m_name); + blackbox_derivatives.insert(m); + } + } else { + boxed_params = true; } } @@ -1440,8 +1450,12 @@ struct HierarchyPass : public Pass { SigSpec sig = conn.second; - if (!keep_portwidths && GetSize(w) != GetSize(conn.second)) - { + bool resize_widths = !keep_portwidths && GetSize(w) != GetSize(conn.second); + if (resize_widths && verific_mod && boxed_params) + log_warning("Ignoring width mismatch on %s.%s.%s from verific, is port width parametrizable?\n", + log_id(module), log_id(cell), log_id(conn.first) + ); + else if (resize_widths) { if (GetSize(w) < GetSize(conn.second)) { int n = GetSize(conn.second) - GetSize(w); From 8b1cc6e05e6316e7af63d4cf9c5b9dd70298ac0b Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 11 Apr 2025 04:12:34 +1200 Subject: [PATCH 31/55] cutpoint: Use new selection helpers --- passes/sat/cutpoint.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc index 58cb4a2aa..bac699dec 100644 --- a/passes/sat/cutpoint.cc +++ b/passes/sat/cutpoint.cc @@ -79,19 +79,18 @@ struct CutpointPass : public Pass { if (flag_blackbox) { if (!design->full_selection()) log_cmd_error("This command only operates on fully selected designs!\n"); - RTLIL::Selection boxes(false); + design->push_empty_selection(); for (auto module : design->modules()) if (flag_instances) { for (auto cell : module->cells()) { auto mod = design->module(cell->type); if (mod != nullptr && mod->get_blackbox_attribute()) - boxes.select(module, cell); + design->select(module, cell); } } else { if (module->get_blackbox_attribute()) - boxes.select(module); + design->select(module); } - design->selection_stack.push_back(boxes); } for (auto module : design->all_selected_modules()) From 779a1fddf675635a2dafbbbf453a0a68f12a84bb Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 11 Apr 2025 04:12:34 +1200 Subject: [PATCH 32/55] Testing cutpoint with boxed selections --- tests/various/.gitignore | 1 + tests/various/cutpoint_blackbox.ys | 56 ++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/tests/various/.gitignore b/tests/various/.gitignore index 83e634820..3dbd50843 100644 --- a/tests/various/.gitignore +++ b/tests/various/.gitignore @@ -1,5 +1,6 @@ /*.log /*.out +/*.sel /write_gzip.v /write_gzip.v.gz /run-test.mk diff --git a/tests/various/cutpoint_blackbox.ys b/tests/various/cutpoint_blackbox.ys index ee7a18c6a..d2d823477 100644 --- a/tests/various/cutpoint_blackbox.ys +++ b/tests/various/cutpoint_blackbox.ys @@ -26,6 +26,7 @@ endmodule EOT hierarchy -top top +design -save hier select -assert-count 0 t:$anyseq select -assert-count 3 =t:?b @@ -37,3 +38,58 @@ select -assert-count 1 r:SOME_PARAM=1 flatten select -assert-count 3 t:$anyseq +select -assert-count 3 t:$scopeinfo n:*cutpoint.cc* %i + +# cutpoint -blackbox === cutpoint =A:whitebox =A:blackbox %u +# (simplified to =A:*box) +design -load hier +cutpoint -blackbox +rename -enumerate -pattern A_% t:$scopeinfo +rename -enumerate -pattern B_% t:$anyseq +rename -enumerate -pattern C_% w:*Anyseq* +design -save gold +select -write cutpoint.gold.sel =* + +design -load hier +cutpoint =A:*box +rename -enumerate -pattern A_% t:$scopeinfo +rename -enumerate -pattern B_% t:$anyseq +rename -enumerate -pattern C_% w:*Anyseq* +design -save gate +select -write cutpoint.gate.sel +select -read cutpoint.gold.sel +# nothing in gate but not gold +select -assert-none % %n + +design -load gold +select -read cutpoint.gate.sel +# nothing in gold but not gate +select -assert-none % %n + +# cutpoint -blackbox -instances !== cutpoint =A:whitebox =A:blackbox %u %C +# (simplified to =A:*box %C) +# because cutpoint -blackbox -instances adds $scopeinfo cells +design -load hier +cutpoint -blackbox -instances +rename -enumerate -pattern A_% t:$scopeinfo +rename -enumerate -pattern B_% t:$anyseq +rename -enumerate -pattern C_% w:*Anyseq* +design -save gold +select -write cutpoint.gold.sel =* + +design -load hier +cutpoint =A:*box %C +rename -enumerate -pattern A_% t:$scopeinfo +rename -enumerate -pattern B_% t:$anyseq +rename -enumerate -pattern C_% w:*Anyseq* +design -save gate +select -write cutpoint.gate.sel +select -read cutpoint.gold.sel +# nothing in gate but not gold +select -assert-none % %n + +design -load gold +select -read cutpoint.gate.sel +# 3 $scopeinfo in gold but not gate +select -assert-count 3 % %n +select -assert-count 3 t:$scopeinfo From 87d3b09988a486646aeb72fbf233484ebc7ed580 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 11 Apr 2025 04:12:35 +1200 Subject: [PATCH 33/55] cutpoint.cc: Fold -instances into -blackbox Replace `cutpoint -blackbox` behaviour with `cutpoint -blackbox -instances` behaviour. Drop `-instances` flag. Add `-noscopeinfo` flag. Use `RTLIL::Selection::boxed_module()` helper to shortcut blackbox check. Update `cutpoint_blackbox.ys` tests to match. --- passes/sat/cutpoint.cc | 59 ++++++++---------------------- tests/various/cutpoint_blackbox.ys | 51 +++++++------------------- 2 files changed, 29 insertions(+), 81 deletions(-) diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc index bac699dec..7f0dc2fcf 100644 --- a/passes/sat/cutpoint.cc +++ b/passes/sat/cutpoint.cc @@ -37,19 +37,20 @@ struct CutpointPass : public Pass { log(" set cutpoint nets to undef (x). the default behavior is to create\n"); log(" an $anyseq cell and drive the cutpoint net from that\n"); log("\n"); + log(" -noscopeinfo\n"); + log(" do not create '$scopeinfo' cells that preserve attributes of cells that\n"); + log(" were removed by this pass\n"); + log("\n"); log(" cutpoint -blackbox [options]\n"); log("\n"); - log("Replace the contents of all blackboxes in the design with a formal cut point.\n"); - log("\n"); - log(" -instances\n"); - log(" replace instances of blackboxes instead of the modules\n"); + log("Replace all instances of blackboxes in the design with a formal cut point.\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { bool flag_undef = false; + bool flag_scopeinfo = true; bool flag_blackbox = false; - bool flag_instances = false; log_header(design, "Executing CUTPOINT pass.\n"); @@ -60,61 +61,31 @@ struct CutpointPass : public Pass { flag_undef = true; continue; } - if (args[argidx] == "-blackbox") { - flag_blackbox = true; + if (args[argidx] == "-noscopeinfo") { + flag_scopeinfo = false; continue; } - if (args[argidx] == "-instances") { - flag_instances = true; + if (args[argidx] == "-blackbox") { + flag_blackbox = true; continue; } break; } extra_args(args, argidx, design); - if (flag_instances && !flag_blackbox) { - log_cmd_error("-instances flag only valid with -blackbox!\n"); - } - if (flag_blackbox) { if (!design->full_selection()) log_cmd_error("This command only operates on fully selected designs!\n"); design->push_empty_selection(); + auto &selection = design->selection(); for (auto module : design->modules()) - if (flag_instances) { - for (auto cell : module->cells()) { - auto mod = design->module(cell->type); - if (mod != nullptr && mod->get_blackbox_attribute()) - design->select(module, cell); - } - } else { - if (module->get_blackbox_attribute()) - design->select(module); - } + for (auto cell : module->cells()) + if (selection.boxed_module(cell->type)) + selection.select(module, cell); } for (auto module : design->all_selected_modules()) { - if (module->is_selected_whole() && !flag_instances) { - log("Making all outputs of module %s cut points, removing module contents.\n", log_id(module)); - module->new_connections(std::vector()); - for (auto cell : vector(module->cells())) - module->remove(cell); - vector output_wires; - for (auto wire : module->wires()) - if (wire->port_output) - output_wires.push_back(wire); - for (auto wire : output_wires) - module->connect(wire, flag_undef ? Const(State::Sx, GetSize(wire)) : module->Anyseq(NEW_ID, GetSize(wire))); - if (module->get_blackbox_attribute()) { - module->set_bool_attribute(ID::blackbox, false); - module->set_bool_attribute(ID::whitebox, false); - auto scopeinfo = module->addCell(NEW_ID, ID($scopeinfo)); - scopeinfo->setParam(ID::TYPE, RTLIL::Const("blackbox")); - } - continue; - } - SigMap sigmap(module); pool cutpoint_bits; @@ -129,7 +100,7 @@ struct CutpointPass : public Pass { RTLIL::Cell *scopeinfo = nullptr; auto cell_name = cell->name; - if (flag_instances && cell_name.isPublic()) { + if (flag_scopeinfo && cell_name.isPublic()) { auto scopeinfo = module->addCell(NEW_ID, ID($scopeinfo)); scopeinfo->setParam(ID::TYPE, RTLIL::Const("blackbox")); diff --git a/tests/various/cutpoint_blackbox.ys b/tests/various/cutpoint_blackbox.ys index d2d823477..ee479b968 100644 --- a/tests/various/cutpoint_blackbox.ys +++ b/tests/various/cutpoint_blackbox.ys @@ -28,20 +28,25 @@ EOT hierarchy -top top design -save hier -select -assert-count 0 t:$anyseq +select -assert-none t:$anyseq select -assert-count 3 =t:?b cutpoint -blackbox -select -assert-count 3 =t:?b -select -assert-count 2 r:SOME_PARAM -select -assert-count 1 r:SOME_PARAM=1 +select -assert-none =t:?b +select -assert-none r:SOME_PARAM -flatten select -assert-count 3 t:$anyseq +select -assert-count 3 t:$scopeinfo +select -assert-count 3 t:$scopeinfo r:TYPE=blackbox %i select -assert-count 3 t:$scopeinfo n:*cutpoint.cc* %i -# cutpoint -blackbox === cutpoint =A:whitebox =A:blackbox %u -# (simplified to =A:*box) +# -noscopeinfo works with -blackbox +design -load hier +cutpoint -blackbox -noscopeinfo +select -assert-none t:$scopeinfo + +# cutpoint -blackbox === cutpoint =A:whitebox =A:blackbox %u %C +# (simplified to =A:*box %C) design -load hier cutpoint -blackbox rename -enumerate -pattern A_% t:$scopeinfo @@ -50,33 +55,6 @@ rename -enumerate -pattern C_% w:*Anyseq* design -save gold select -write cutpoint.gold.sel =* -design -load hier -cutpoint =A:*box -rename -enumerate -pattern A_% t:$scopeinfo -rename -enumerate -pattern B_% t:$anyseq -rename -enumerate -pattern C_% w:*Anyseq* -design -save gate -select -write cutpoint.gate.sel -select -read cutpoint.gold.sel -# nothing in gate but not gold -select -assert-none % %n - -design -load gold -select -read cutpoint.gate.sel -# nothing in gold but not gate -select -assert-none % %n - -# cutpoint -blackbox -instances !== cutpoint =A:whitebox =A:blackbox %u %C -# (simplified to =A:*box %C) -# because cutpoint -blackbox -instances adds $scopeinfo cells -design -load hier -cutpoint -blackbox -instances -rename -enumerate -pattern A_% t:$scopeinfo -rename -enumerate -pattern B_% t:$anyseq -rename -enumerate -pattern C_% w:*Anyseq* -design -save gold -select -write cutpoint.gold.sel =* - design -load hier cutpoint =A:*box %C rename -enumerate -pattern A_% t:$scopeinfo @@ -90,6 +68,5 @@ select -assert-none % %n design -load gold select -read cutpoint.gate.sel -# 3 $scopeinfo in gold but not gate -select -assert-count 3 % %n -select -assert-count 3 t:$scopeinfo +# nothing in gold but not gate +select -assert-none % %n From 9d3d0a4336e3d84a8ea98dca613293eba380962f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 12 Apr 2025 00:22:23 +0000 Subject: [PATCH 34/55] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index deedbe17b..842a70283 100644 --- a/Makefile +++ b/Makefile @@ -159,7 +159,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.52+10 +YOSYS_VER := 0.52+32 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From 872a197e94c477727845c1f0518a554fbaaacbd5 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 14 Apr 2025 12:41:14 +0200 Subject: [PATCH 35/55] memory_libmap: fix MapWorker memory allocation --- passes/memory/memory_libmap.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/passes/memory/memory_libmap.cc b/passes/memory/memory_libmap.cc index b0d0498ea..a5b30c7ae 100644 --- a/passes/memory/memory_libmap.cc +++ b/passes/memory/memory_libmap.cc @@ -2232,11 +2232,11 @@ struct MemoryLibMapPass : public Pass { if (module->has_processes_warn()) continue; - MapWorker worker(module); + auto worker = std::make_unique(module); auto mems = Mem::get_selected_memories(module); for (auto &mem : mems) { - MemMapping map(worker, mem, lib, opts); + MemMapping map(*worker, mem, lib, opts); int idx = -1; int best = map.logic_cost; if (!map.logic_ok) { @@ -2259,7 +2259,7 @@ struct MemoryLibMapPass : public Pass { } else { map.emit(map.cfgs[idx]); // Rebuild indices after modifying module - worker = MapWorker(module); + worker = std::make_unique(module); } } } From 81f3369f248f84eeb81aa3a4b32ef93f32937ca8 Mon Sep 17 00:00:00 2001 From: Kelvin Chung Date: Mon, 14 Apr 2025 11:44:52 +0100 Subject: [PATCH 36/55] Add check at constmap and merge test --- passes/techmap/constmap.cc | 36 ++++++++++++++++++++++++++++++------ tests/techmap/constmap.ys | 22 +--------------------- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/passes/techmap/constmap.cc b/passes/techmap/constmap.cc index 8f3235991..f0757403d 100644 --- a/passes/techmap/constmap.cc +++ b/passes/techmap/constmap.cc @@ -33,9 +33,9 @@ void constmap_worker(RTLIL::SigSpec &sig) { if (sig.is_fully_const()){ value = module->addWire(NEW_ID, sig.size()); - RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype)); - cell->setParam(RTLIL::escape_id(cell_paramname), sig.as_const()); - cell->setPort(RTLIL::escape_id(cell_portname), value); + RTLIL::Cell *cell = module->addCell(NEW_ID, celltype); + cell->setParam(cell_paramname, sig.as_const()); + cell->setPort(cell_portname, value); sig = value; } } @@ -62,15 +62,39 @@ struct ConstmapPass : public Pass { for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-cell" && argidx+3 < args.size()){ - celltype = args[++argidx]; - cell_portname = args[++argidx]; - cell_paramname = args[++argidx]; + celltype = RTLIL::escape_id(args[++argidx]); + cell_portname = RTLIL::escape_id(args[++argidx]); + cell_paramname = RTLIL::escape_id(args[++argidx]); continue; } break; } extra_args(args, argidx, design); + + if (design->has(celltype)) { + Module *existing = design->module(celltype); + bool has_port = false; + for (auto &p : existing->ports){ + if (p == cell_portname){ + has_port = true; + break; + } + } + if (!has_port) + log_cmd_error("Cell type '%s' does not have port '%s'.\n", celltype.c_str(), cell_portname.c_str()); + + bool has_param = false; + for (auto &p : existing->avail_parameters){ + if (p == cell_paramname) + has_param = true; + } + + if (!has_param) + log_cmd_error("Cell type '%s' does not have parameter '%s'.\n", celltype.c_str(), cell_paramname.c_str()); + } + + for (auto mod : design->selected_modules()) { module = mod; diff --git a/tests/techmap/constmap.ys b/tests/techmap/constmap.ys index fbaca7662..6945cf514 100644 --- a/tests/techmap/constmap.ys +++ b/tests/techmap/constmap.ys @@ -9,8 +9,7 @@ endmodule EOT constmap -cell const_cell O value -select -assert-count 1 t:const_cell -select -assert-count 1 r:value=16 +select -assert-count 1 t:const_cell r:value=16 %i design -reset @@ -42,22 +41,3 @@ select -assert-count 1 test/out1 %ci* r:value=16 %i select -assert-count 1 test/out2 %ci* r:value=32 %i select -assert-count 1 t:const_cell r:value=16 %i select -assert-count 1 t:const_cell r:value=32 %i - -design -reset - -read_verilog << EOT - -module test(); - wire [31:0] in; - wire [31:0] out1; - wire [31:0] out2; - assign out1 = in + 16; - assign out2 = in + 32; -endmodule - -EOT - -constmap -cell const_cell O value - -select -assert-count 1 t:const_cell r:value=16 %i -select -assert-count 1 t:const_cell r:value=32 %i From c18f1310a534e84db70b19ebce238c19cb9ac73e Mon Sep 17 00:00:00 2001 From: Donn Date: Mon, 14 Apr 2025 14:45:41 +0200 Subject: [PATCH 37/55] Revert #4901 Partially reverts commit 9c5bffcf93183df0756e01f3c4774d2f83688b8e. The reasoning behind this is that setup.py is intended to strictly consume the Makefile and not be consumed by it. The attempt at using them recursively has caused a number of issues and has rendered Pyosys unusable to some users: See https://github.com/YosysHQ/yosys/issues/5012 Additionally, unlike the previous pyosys installation target, the wheel installation does not respect PREFIX=, only venvs. For installation inside a venv, the intended method should remain a user manually executing `pip3 install .` instead of relying on the Makefile. --- Makefile | 30 ++++++++++-------------------- passes/techmap/Makefile.inc | 2 +- setup.py | 5 ----- 3 files changed, 11 insertions(+), 26 deletions(-) diff --git a/Makefile b/Makefile index 74951b8cb..977e1cf40 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,6 @@ ENABLE_ZLIB := 1 # python wrappers ENABLE_PYOSYS := 0 -ENABLE_WHEEL := $(ENABLE_PYOSYS) # other configuration flags ENABLE_GCOV := 0 @@ -350,9 +349,6 @@ LINKFLAGS += $(filter-out -l%,$(shell $(PYTHON_CONFIG) --ldflags)) LIBS += $(shell $(PYTHON_CONFIG) --libs) EXE_LIBS += $(filter-out $(LIBS),$(shell $(PYTHON_CONFIG_FOR_EXE) --libs)) CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON -ifeq ($(ENABLE_WHEEL),1) -EXTRA_TARGETS += wheel -endif # Detect name of boost_python library. Some distros use boost_python-py, other boost_python, some only use the major version number, some a concatenation of major and minor version numbers CHECK_BOOST_PYTHON = (echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(LINKFLAGS) $(LIBS) -l$(1) - > /dev/null 2>&1 && echo "-l$(1)") @@ -971,20 +967,6 @@ unit-test: libyosys.so clean-unit-test: @$(MAKE) -C $(UNITESTPATH) clean -ifeq ($(ENABLE_PYOSYS),1) -wheel: $(TARGETS) - $(PYTHON_EXECUTABLE) -m pip wheel . - -install-wheel: wheel - $(PYTHON_EXECUTABLE) -m pip install pyosys-$(YOSYS_MAJOR).$(YOSYS_MINOR).$(YOSYS_COMMIT)-*.whl --force-reinstall -else -wheel: - $(error Pyosys is not enabled. Set ENABLE_PYOSYS=1 to enable it.) - -install-wheel: - $(error Pyosys is not enabled. Set ENABLE_PYOSYS=1 to enable it.) -endif - install: $(TARGETS) $(EXTRA_TARGETS) $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR) $(INSTALL_SUDO) cp $(filter-out libyosys.so,$(TARGETS)) $(DESTDIR)$(BINDIR) @@ -1004,7 +986,13 @@ ifeq ($(ENABLE_LIBYOSYS),1) $(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR)/ $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(LIBDIR)/libyosys.so ifeq ($(ENABLE_PYOSYS),1) - $(INSTALL_SUDO) @$(MAKE) install-wheel + $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys + $(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/libyosys.so + $(INSTALL_SUDO) cp -r share $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys +ifeq ($(ENABLE_ABC),1) + $(INSTALL_SUDO) cp yosys-abc $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/yosys-abc +endif + $(INSTALL_SUDO) cp misc/__init__.py $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/ endif endif ifeq ($(ENABLE_PLUGINS),1) @@ -1020,7 +1008,9 @@ uninstall: ifeq ($(ENABLE_LIBYOSYS),1) $(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.so ifeq ($(ENABLE_PYOSYS),1) - $(INSTALL_SUDO) $(PYTHON_EXECUTABLE) -m pip uninstall -y pyosys + $(INSTALL_SUDO) rm -vf $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/libyosys.so + $(INSTALL_SUDO) rm -vf $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/__init__.py + $(INSTALL_SUDO) rmdir $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys endif endif diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index e6a910a0d..f34faf37c 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -62,5 +62,5 @@ EXTRA_OBJS += passes/techmap/filterlib.o $(PROGRAM_PREFIX)yosys-filterlib$(EXE): passes/techmap/filterlib.o $(Q) mkdir -p $(dir $@) - $(P) $(CXX) -o $(PROGRAM_PREFIX)yosys-filterlib$(EXE) $(LINKFLAGS) $^ $(LIBS) + $(P) $(CXX) -o $(PROGRAM_PREFIX)yosys-filterlib$(EXE) $(LINKFLAGS) $^ $(EXE_LIBS) $(LIBS) endif diff --git a/setup.py b/setup.py index 8a37a91b3..a199530d0 100644 --- a/setup.py +++ b/setup.py @@ -40,8 +40,6 @@ class libyosys_so_ext(Extension): ) self.args = [ "ENABLE_PYOSYS=1", - # Prevent recursive wheel build - "ENABLE_WHEEL=0", # Would need to be installed separately by the user "ENABLE_TCL=0", "ENABLE_READLINE=0", @@ -87,9 +85,6 @@ class libyosys_so_ext(Extension): shutil.copytree("share", share_target) - # I don't know how debug info is getting here. - - class custom_build_ext(build_ext): def build_extension(self, ext) -> None: if not hasattr(ext, "custom_build"): From b7062143e1fa76d8adbbe6f6cea013e4f2fc807a Mon Sep 17 00:00:00 2001 From: Donn Date: Mon, 14 Apr 2025 14:54:38 +0200 Subject: [PATCH 38/55] hotfix: fix CHECK_BOOST_PYTHON missing libpython --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 977e1cf40..2c145edfc 100644 --- a/Makefile +++ b/Makefile @@ -351,7 +351,7 @@ EXE_LIBS += $(filter-out $(LIBS),$(shell $(PYTHON_CONFIG_FOR_EXE) --libs)) CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON # Detect name of boost_python library. Some distros use boost_python-py, other boost_python, some only use the major version number, some a concatenation of major and minor version numbers -CHECK_BOOST_PYTHON = (echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(LINKFLAGS) $(LIBS) -l$(1) - > /dev/null 2>&1 && echo "-l$(1)") +CHECK_BOOST_PYTHON = (echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(LINKFLAGS) $(EXE_LIBS) $(LIBS) -l$(1) - > /dev/null 2>&1 && echo "-l$(1)") BOOST_PYTHON_LIB ?= $(shell \ $(call CHECK_BOOST_PYTHON,boost_python-py$(subst .,,$(PYTHON_VERSION))) || \ $(call CHECK_BOOST_PYTHON,boost_python-py$(PYTHON_MAJOR_VERSION)) || \ From c79379db02b47c0c9b7c5bc8b79627a50122d1c3 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 14 Apr 2025 15:22:38 +0200 Subject: [PATCH 39/55] Run wheels each Sunday at 10AM --- .github/workflows/wheels.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 2e47bcfa9..b01ce6b3a 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -1,6 +1,10 @@ name: Build Wheels for PyPI + +# run every Sunday at 10 AM on: workflow_dispatch: + schedule: + - cron: '0 10 * * 0' jobs: build_wheels: From ea2c1e7ac73b81c4aa2b5dd1ae4948db1d75b12e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 15 Apr 2025 00:23:32 +0000 Subject: [PATCH 40/55] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7011ab537..c0fcdfe62 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.52+32 +YOSYS_VER := 0.52+45 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From 30486079f9b2c395f33c6604c6d3ddb68269cd9f Mon Sep 17 00:00:00 2001 From: Emily Schmidt Date: Wed, 9 Apr 2025 10:18:25 +0100 Subject: [PATCH 41/55] add dft_tag documentation --- .../more_scripting/data_flow_tracking.rst | 71 +++++++++++++++++++ .../using_yosys/more_scripting/index.rst | 1 + 2 files changed, 72 insertions(+) create mode 100644 docs/source/using_yosys/more_scripting/data_flow_tracking.rst diff --git a/docs/source/using_yosys/more_scripting/data_flow_tracking.rst b/docs/source/using_yosys/more_scripting/data_flow_tracking.rst new file mode 100644 index 000000000..e13dfac7d --- /dev/null +++ b/docs/source/using_yosys/more_scripting/data_flow_tracking.rst @@ -0,0 +1,71 @@ +Dataflow tracking +------------------- +Yosys can be used to answer questions such as "can this signal affect this other signal?" via its *dataflow tracking* support. +For this, four special cells, ``$get_tag``, ``$set_tag``, ``$overwrite_tag`` and ``$original_tag`` are inserted into the design and then the ``dft_tag`` is run, which converts these cells into ordinary logic. +Typically, one would then use ``sby`` to prove assertions involving these cells. + +Ordinarily in Yosys, the state of a bit is simply ``0`` or ``1`` (or one of the special values, ``z`` and ``x``). +During dataflow tracking they are augmented with a set of tags. +For example, the state of a bit could be ``0`` and the set of tags ``"KEY"`` and ``"OVERFLOW"``. + +In addition to their usual operations on the logical bits, Yosys operations must now also process the status of the tags. +For this, tags are simply *forwarded* or *propagated* (i.e. copied) from inputs to outputs, according to the following general rule: + + A tag is forwarded from an input to an output if the input can affect the output, for that particular state of all other inputs. + +For example, XOR, AND and OR cells propagate tags as follows: + +#. XOR simply forwards all tags from its inputs to its output, because inputs to XOR can always affect the output. +#. AND forwards tags only if the other input is ``1``. Because if one input is ``0``, the other input can never affect the output. +#. Similarly, OR forwards tags only if the other input is ``0``. + +There are two exceptions to this rule: +#. In general, propagation is only determined approximately. + For example, unless the ``dft_tag`` code knows about a cell, it simply assumes the worst-case behaviour that all inputs can affect all outputs. + It also does not try to determine whether the effect of a signal is cancelled out, for example ``A ^ A`` or ``A ^ (B ^ A)`` is independent of ``A``, but its tags would be propagated nonetheless. +#. If tag groups are used, the rules are modified (see below). + +Because of this propagation behaviour, we can answer questions about what signals are affected by a certain signal, by injecting a tag at that point in the circuit, and observing where the tag is visible. + +Example use cases +~~~~~~~~~~~~~~~~~~ +As an example use case, consider a cryptographic processor which is not supposed to expose its secret keys to the outside world. +We can tag all key bits with the ``"KEY"`` tag and use ``sby`` to formally verify that no external signal ever carries the ``"KEY"`` tag, meaning that key information is not visible to the outside. +As a caveat, we have to manually clear the ``"KEY"`` tag during cryptographic operations, as proving that the cryptographic operations themselves do not leak key information is beyond the ability of Yosys. +However we can still easily detect, if e.g. an engineer forgot to remove debugging code that allows reading back key data. + +As a different use case, we can modify all adders in the design to set the ``"OVERFLOW"`` tag on their output bits, if the addition overflowed, and then add asserts to all flip-flop inputs and output signals that they do not carry the ``"OVERFLOW"`` tag, i.e. that the results of overflowed additions never affect system state. +Note that in this particular example we use the ability of tag insertion to be conditional on logic, in this case the overflow condition of an adder. + +Semantics of dataflow tracking cells +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``$set_tag`` has inputs ``A``, ``SET``, ``CLR``, an output ``Y`` and a string parameter ``TAG``. +The logic value of ``A`` and all tags other than the one named by the ``TAG`` parameter are simply copied to ``Y``. +If ``SET`` is ``1``, then the named tag is added to ``Y``. +Otherwise, if ``CLR`` is ``1``, then the named tag is removed. +Otherwise, the tag is unchanged, i.e. it is present in ``Y`` if it is present in ``A``. + +``$get_tag`` has an input ``A`` and an output ``Y`` and a string parameter ``TAG``. +``$get_tag`` inspects ``A`` for the presence or absence of a tag of the given name and sets ``Y`` to ``1`` if the tag is present. +The logical value of ``A`` is completely ignored. + +``$overwrite_tag`` functions like ``$set_tag``, but lacks the ``Y`` output. +Instead of providing a modified version of the input signal, it modifies the signal ``A`` "in-place", i.e. if a signal is input to ``$overwrite_tag``, that is equivalent to interposing a ``$set_tag`` between its driver and all cells it is connected to. +The main purpose of ``$overwrite_tag`` is adding tags to signals produced within a module that cannot or should not be modified itself. + +``$original_tag`` functions identically to ``$get_tag``, but ignores ``$overwrite_tag``, i.e. when converting the ``$overwrite_tag`` to ``$set_tag`` as described above, it is equivalent to inserting the ``$get_tag`` *before* the ``$set_tag``. + +Tag groups +~~~~~~~~~~~~~~ +Tag groups are an advanced feature that modify the propagation rule discussed above. +To use tag groups, simply name tags according to the schema ``"group:name"``. +For example, ``"key:0"``, ``"key:a"``, ``"key:b"`` would be three tags in the ``"key"`` group. + +The propagation rule is then amended by + + Inputs cannot block the propagation of each other's tags for tags of the same group. + +For example, an AND gate will propagate a given tag on one input, if the other input is either 1 or carries a tag of the same group. +So if one input is ``0, "key:a"`` and the other is ``0, "key:b"`` the result would be ``0, "key:a", "key:b"``, rather than simply ``0``. +Note that if we add an unrelated ``"overflow"`` tag to the first input, it would still not be propagated. \ No newline at end of file diff --git a/docs/source/using_yosys/more_scripting/index.rst b/docs/source/using_yosys/more_scripting/index.rst index 090b9e0b9..6265e5d0e 100644 --- a/docs/source/using_yosys/more_scripting/index.rst +++ b/docs/source/using_yosys/more_scripting/index.rst @@ -12,5 +12,6 @@ More scripting selections interactive_investigation model_checking + data_flow_tracking .. troubleshooting From ea6e5b3c48c44ea17470803d21c4c8b83a09ace9 Mon Sep 17 00:00:00 2001 From: Emily Schmidt Date: Thu, 10 Apr 2025 08:23:07 +0100 Subject: [PATCH 42/55] add changes to dft docs suggested by Krystine --- .../using_yosys/more_scripting/data_flow_tracking.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/source/using_yosys/more_scripting/data_flow_tracking.rst b/docs/source/using_yosys/more_scripting/data_flow_tracking.rst index e13dfac7d..0bf5e567c 100644 --- a/docs/source/using_yosys/more_scripting/data_flow_tracking.rst +++ b/docs/source/using_yosys/more_scripting/data_flow_tracking.rst @@ -1,8 +1,11 @@ Dataflow tracking ------------------- + Yosys can be used to answer questions such as "can this signal affect this other signal?" via its *dataflow tracking* support. -For this, four special cells, ``$get_tag``, ``$set_tag``, ``$overwrite_tag`` and ``$original_tag`` are inserted into the design and then the ``dft_tag`` is run, which converts these cells into ordinary logic. -Typically, one would then use ``sby`` to prove assertions involving these cells. +For this, four special cells, `$get_tag`, `$set_tag``, `$overwrite_tag` and `$original_tag` are inserted into the design and then the `dft_tag` is run, which converts these cells into ordinary logic. +Typically, one would then use `SBY docs`_ to prove assertions involving these cells. + +.. _SBY docs: https://yosyshq.readthedocs.io/projects/sby Ordinarily in Yosys, the state of a bit is simply ``0`` or ``1`` (or one of the special values, ``z`` and ``x``). During dataflow tracking they are augmented with a set of tags. @@ -20,6 +23,7 @@ For example, XOR, AND and OR cells propagate tags as follows: #. Similarly, OR forwards tags only if the other input is ``0``. There are two exceptions to this rule: + #. In general, propagation is only determined approximately. For example, unless the ``dft_tag`` code knows about a cell, it simply assumes the worst-case behaviour that all inputs can affect all outputs. It also does not try to determine whether the effect of a signal is cancelled out, for example ``A ^ A`` or ``A ^ (B ^ A)`` is independent of ``A``, but its tags would be propagated nonetheless. @@ -29,6 +33,7 @@ Because of this propagation behaviour, we can answer questions about what signal Example use cases ~~~~~~~~~~~~~~~~~~ + As an example use case, consider a cryptographic processor which is not supposed to expose its secret keys to the outside world. We can tag all key bits with the ``"KEY"`` tag and use ``sby`` to formally verify that no external signal ever carries the ``"KEY"`` tag, meaning that key information is not visible to the outside. As a caveat, we have to manually clear the ``"KEY"`` tag during cryptographic operations, as proving that the cryptographic operations themselves do not leak key information is beyond the ability of Yosys. @@ -58,6 +63,7 @@ The main purpose of ``$overwrite_tag`` is adding tags to signals produced within Tag groups ~~~~~~~~~~~~~~ + Tag groups are an advanced feature that modify the propagation rule discussed above. To use tag groups, simply name tags according to the schema ``"group:name"``. For example, ``"key:0"``, ``"key:a"``, ``"key:b"`` would be three tags in the ``"key"`` group. From bfed96ad88a422408717cf6ea54dca377e1f9b86 Mon Sep 17 00:00:00 2001 From: Emily Schmidt Date: Thu, 10 Apr 2025 08:43:00 +0100 Subject: [PATCH 43/55] some fixes and rewordings of the dft docs --- .../more_scripting/data_flow_tracking.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/source/using_yosys/more_scripting/data_flow_tracking.rst b/docs/source/using_yosys/more_scripting/data_flow_tracking.rst index 0bf5e567c..e0714b7e2 100644 --- a/docs/source/using_yosys/more_scripting/data_flow_tracking.rst +++ b/docs/source/using_yosys/more_scripting/data_flow_tracking.rst @@ -2,10 +2,10 @@ Dataflow tracking ------------------- Yosys can be used to answer questions such as "can this signal affect this other signal?" via its *dataflow tracking* support. -For this, four special cells, `$get_tag`, `$set_tag``, `$overwrite_tag` and `$original_tag` are inserted into the design and then the `dft_tag` is run, which converts these cells into ordinary logic. -Typically, one would then use `SBY docs`_ to prove assertions involving these cells. +For this, four special cells, `$get_tag`, `$set_tag`, `$overwrite_tag` and `$original_tag` are inserted into the design (e.g. by a custom Yosys pass) and then the `dft_tag` is run, which converts these cells into ordinary logic. +Typically, one would then use `SBY`_ to prove assertions involving these cells. -.. _SBY docs: https://yosyshq.readthedocs.io/projects/sby +.. _SBY: https://yosyshq.readthedocs.io/projects/sby Ordinarily in Yosys, the state of a bit is simply ``0`` or ``1`` (or one of the special values, ``z`` and ``x``). During dataflow tracking they are augmented with a set of tags. @@ -19,14 +19,14 @@ For this, tags are simply *forwarded* or *propagated* (i.e. copied) from inputs For example, XOR, AND and OR cells propagate tags as follows: #. XOR simply forwards all tags from its inputs to its output, because inputs to XOR can always affect the output. -#. AND forwards tags only if the other input is ``1``. Because if one input is ``0``, the other input can never affect the output. +#. AND forwards tags on a given input only if the other input is ``1``. Because if one input is ``0``, the other input can never affect the output. #. Similarly, OR forwards tags only if the other input is ``0``. There are two exceptions to this rule: #. In general, propagation is only determined approximately. For example, unless the ``dft_tag`` code knows about a cell, it simply assumes the worst-case behaviour that all inputs can affect all outputs. - It also does not try to determine whether the effect of a signal is cancelled out, for example ``A ^ A`` or ``A ^ (B ^ A)`` is independent of ``A``, but its tags would be propagated nonetheless. + Further, the code also does not consider that, when a signal affects multiple inputs of a cell, the resulting simultaneous changes of the inputs can cancel each other out, for example ``A ^ A`` or ``A ^ (B ^ A)`` is independent of ``A``, but its tags would be propagated nonetheless. #. If tag groups are used, the rules are modified (see below). Because of this propagation behaviour, we can answer questions about what signals are affected by a certain signal, by injecting a tag at that point in the circuit, and observing where the tag is visible. @@ -35,7 +35,7 @@ Example use cases ~~~~~~~~~~~~~~~~~~ As an example use case, consider a cryptographic processor which is not supposed to expose its secret keys to the outside world. -We can tag all key bits with the ``"KEY"`` tag and use ``sby`` to formally verify that no external signal ever carries the ``"KEY"`` tag, meaning that key information is not visible to the outside. +We can tag all key bits with the ``"KEY"`` tag and use `SBY`_ to formally verify that no external signal ever carries the ``"KEY"`` tag, meaning that key information is not visible to the outside. As a caveat, we have to manually clear the ``"KEY"`` tag during cryptographic operations, as proving that the cryptographic operations themselves do not leak key information is beyond the ability of Yosys. However we can still easily detect, if e.g. an engineer forgot to remove debugging code that allows reading back key data. From 19845be85c1fd2385dcfc07cdc39c2c6aae26a7f Mon Sep 17 00:00:00 2001 From: Emily Schmidt Date: Thu, 10 Apr 2025 08:43:21 +0100 Subject: [PATCH 44/55] reflow dft docs to 80 cols --- .../more_scripting/data_flow_tracking.rst | 115 ++++++++++++------ 1 file changed, 76 insertions(+), 39 deletions(-) diff --git a/docs/source/using_yosys/more_scripting/data_flow_tracking.rst b/docs/source/using_yosys/more_scripting/data_flow_tracking.rst index e0714b7e2..aa13a2e69 100644 --- a/docs/source/using_yosys/more_scripting/data_flow_tracking.rst +++ b/docs/source/using_yosys/more_scripting/data_flow_tracking.rst @@ -1,77 +1,114 @@ Dataflow tracking ------------------- -Yosys can be used to answer questions such as "can this signal affect this other signal?" via its *dataflow tracking* support. -For this, four special cells, `$get_tag`, `$set_tag`, `$overwrite_tag` and `$original_tag` are inserted into the design (e.g. by a custom Yosys pass) and then the `dft_tag` is run, which converts these cells into ordinary logic. -Typically, one would then use `SBY`_ to prove assertions involving these cells. +Yosys can be used to answer questions such as "can this signal affect this other +signal?" via its *dataflow tracking* support. For this, four special cells, +`$get_tag`, `$set_tag`, `$overwrite_tag` and `$original_tag` are inserted into +the design (e.g. by a custom Yosys pass) and then the `dft_tag` is run, which +converts these cells into ordinary logic. Typically, one would then use `SBY`_ +to prove assertions involving these cells. .. _SBY: https://yosyshq.readthedocs.io/projects/sby -Ordinarily in Yosys, the state of a bit is simply ``0`` or ``1`` (or one of the special values, ``z`` and ``x``). -During dataflow tracking they are augmented with a set of tags. -For example, the state of a bit could be ``0`` and the set of tags ``"KEY"`` and ``"OVERFLOW"``. +Ordinarily in Yosys, the state of a bit is simply ``0`` or ``1`` (or one of the +special values, ``z`` and ``x``). During dataflow tracking they are augmented +with a set of tags. For example, the state of a bit could be ``0`` and the set +of tags ``"KEY"`` and ``"OVERFLOW"``. -In addition to their usual operations on the logical bits, Yosys operations must now also process the status of the tags. -For this, tags are simply *forwarded* or *propagated* (i.e. copied) from inputs to outputs, according to the following general rule: +In addition to their usual operations on the logical bits, Yosys operations must +now also process the status of the tags. For this, tags are simply *forwarded* +or *propagated* (i.e. copied) from inputs to outputs, according to the following +general rule: - A tag is forwarded from an input to an output if the input can affect the output, for that particular state of all other inputs. + A tag is forwarded from an input to an output if the input can affect the + output, for that particular state of all other inputs. For example, XOR, AND and OR cells propagate tags as follows: -#. XOR simply forwards all tags from its inputs to its output, because inputs to XOR can always affect the output. -#. AND forwards tags on a given input only if the other input is ``1``. Because if one input is ``0``, the other input can never affect the output. +#. XOR simply forwards all tags from its inputs to its output, because inputs to + XOR can always affect the output. +#. AND forwards tags on a given input only if the other input is ``1``. Because + if one input is ``0``, the other input can never affect the output. #. Similarly, OR forwards tags only if the other input is ``0``. There are two exceptions to this rule: -#. In general, propagation is only determined approximately. - For example, unless the ``dft_tag`` code knows about a cell, it simply assumes the worst-case behaviour that all inputs can affect all outputs. - Further, the code also does not consider that, when a signal affects multiple inputs of a cell, the resulting simultaneous changes of the inputs can cancel each other out, for example ``A ^ A`` or ``A ^ (B ^ A)`` is independent of ``A``, but its tags would be propagated nonetheless. +#. In general, propagation is only determined approximately. For example, unless + the ``dft_tag`` code knows about a cell, it simply assumes the worst-case + behaviour that all inputs can affect all outputs. Further, the code also does + not consider that, when a signal affects multiple inputs of a cell, the + resulting simultaneous changes of the inputs can cancel each other out, for + example ``A ^ A`` or ``A ^ (B ^ A)`` is independent of ``A``, but its tags + would be propagated nonetheless. #. If tag groups are used, the rules are modified (see below). -Because of this propagation behaviour, we can answer questions about what signals are affected by a certain signal, by injecting a tag at that point in the circuit, and observing where the tag is visible. +Because of this propagation behaviour, we can answer questions about what +signals are affected by a certain signal, by injecting a tag at that point in +the circuit, and observing where the tag is visible. Example use cases ~~~~~~~~~~~~~~~~~~ -As an example use case, consider a cryptographic processor which is not supposed to expose its secret keys to the outside world. -We can tag all key bits with the ``"KEY"`` tag and use `SBY`_ to formally verify that no external signal ever carries the ``"KEY"`` tag, meaning that key information is not visible to the outside. -As a caveat, we have to manually clear the ``"KEY"`` tag during cryptographic operations, as proving that the cryptographic operations themselves do not leak key information is beyond the ability of Yosys. -However we can still easily detect, if e.g. an engineer forgot to remove debugging code that allows reading back key data. +As an example use case, consider a cryptographic processor which is not supposed +to expose its secret keys to the outside world. We can tag all key bits with the +``"KEY"`` tag and use `SBY`_ to formally verify that no external signal ever +carries the ``"KEY"`` tag, meaning that key information is not visible to the +outside. As a caveat, we have to manually clear the ``"KEY"`` tag during +cryptographic operations, as proving that the cryptographic operations +themselves do not leak key information is beyond the ability of Yosys. However +we can still easily detect, if e.g. an engineer forgot to remove debugging code +that allows reading back key data. -As a different use case, we can modify all adders in the design to set the ``"OVERFLOW"`` tag on their output bits, if the addition overflowed, and then add asserts to all flip-flop inputs and output signals that they do not carry the ``"OVERFLOW"`` tag, i.e. that the results of overflowed additions never affect system state. -Note that in this particular example we use the ability of tag insertion to be conditional on logic, in this case the overflow condition of an adder. +As a different use case, we can modify all adders in the design to set the +``"OVERFLOW"`` tag on their output bits, if the addition overflowed, and then +add asserts to all flip-flop inputs and output signals that they do not carry +the ``"OVERFLOW"`` tag, i.e. that the results of overflowed additions never +affect system state. Note that in this particular example we use the ability of +tag insertion to be conditional on logic, in this case the overflow condition of +an adder. Semantics of dataflow tracking cells ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -``$set_tag`` has inputs ``A``, ``SET``, ``CLR``, an output ``Y`` and a string parameter ``TAG``. -The logic value of ``A`` and all tags other than the one named by the ``TAG`` parameter are simply copied to ``Y``. -If ``SET`` is ``1``, then the named tag is added to ``Y``. -Otherwise, if ``CLR`` is ``1``, then the named tag is removed. -Otherwise, the tag is unchanged, i.e. it is present in ``Y`` if it is present in ``A``. +``$set_tag`` has inputs ``A``, ``SET``, ``CLR``, an output ``Y`` and a string +parameter ``TAG``. The logic value of ``A`` and all tags other than the one +named by the ``TAG`` parameter are simply copied to ``Y``. If ``SET`` is ``1``, +then the named tag is added to ``Y``. Otherwise, if ``CLR`` is ``1``, then the +named tag is removed. Otherwise, the tag is unchanged, i.e. it is present in +``Y`` if it is present in ``A``. -``$get_tag`` has an input ``A`` and an output ``Y`` and a string parameter ``TAG``. -``$get_tag`` inspects ``A`` for the presence or absence of a tag of the given name and sets ``Y`` to ``1`` if the tag is present. -The logical value of ``A`` is completely ignored. +``$get_tag`` has an input ``A`` and an output ``Y`` and a string parameter +``TAG``. ``$get_tag`` inspects ``A`` for the presence or absence of a tag of the +given name and sets ``Y`` to ``1`` if the tag is present. The logical value of +``A`` is completely ignored. ``$overwrite_tag`` functions like ``$set_tag``, but lacks the ``Y`` output. -Instead of providing a modified version of the input signal, it modifies the signal ``A`` "in-place", i.e. if a signal is input to ``$overwrite_tag``, that is equivalent to interposing a ``$set_tag`` between its driver and all cells it is connected to. -The main purpose of ``$overwrite_tag`` is adding tags to signals produced within a module that cannot or should not be modified itself. +Instead of providing a modified version of the input signal, it modifies the +signal ``A`` "in-place", i.e. if a signal is input to ``$overwrite_tag``, that +is equivalent to interposing a ``$set_tag`` between its driver and all cells it +is connected to. The main purpose of ``$overwrite_tag`` is adding tags to +signals produced within a module that cannot or should not be modified itself. -``$original_tag`` functions identically to ``$get_tag``, but ignores ``$overwrite_tag``, i.e. when converting the ``$overwrite_tag`` to ``$set_tag`` as described above, it is equivalent to inserting the ``$get_tag`` *before* the ``$set_tag``. +``$original_tag`` functions identically to ``$get_tag``, but ignores +``$overwrite_tag``, i.e. when converting the ``$overwrite_tag`` to ``$set_tag`` +as described above, it is equivalent to inserting the ``$get_tag`` *before* the +``$set_tag``. Tag groups ~~~~~~~~~~~~~~ -Tag groups are an advanced feature that modify the propagation rule discussed above. -To use tag groups, simply name tags according to the schema ``"group:name"``. -For example, ``"key:0"``, ``"key:a"``, ``"key:b"`` would be three tags in the ``"key"`` group. +Tag groups are an advanced feature that modify the propagation rule discussed +above. To use tag groups, simply name tags according to the schema +``"group:name"``. For example, ``"key:0"``, ``"key:a"``, ``"key:b"`` would be +three tags in the ``"key"`` group. The propagation rule is then amended by - Inputs cannot block the propagation of each other's tags for tags of the same group. + Inputs cannot block the propagation of each other's tags for tags of the same + group. -For example, an AND gate will propagate a given tag on one input, if the other input is either 1 or carries a tag of the same group. -So if one input is ``0, "key:a"`` and the other is ``0, "key:b"`` the result would be ``0, "key:a", "key:b"``, rather than simply ``0``. -Note that if we add an unrelated ``"overflow"`` tag to the first input, it would still not be propagated. \ No newline at end of file +For example, an AND gate will propagate a given tag on one input, if the other +input is either 1 or carries a tag of the same group. So if one input is ``0, +"key:a"`` and the other is ``0, "key:b"`` the result would be ``0, "key:a", +"key:b"``, rather than simply ``0``. Note that if we add an unrelated +``"overflow"`` tag to the first input, it would still not be propagated. \ No newline at end of file From 6dff9e778744ad538c55946eb7b5ab97ee99d5fd Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Mon, 14 Apr 2025 15:41:30 +0200 Subject: [PATCH 45/55] share: Restrict activation patterns to potentially relevant signals In case the two sets of activation patterns are mutually exclusive without considering the logic feeding into the activation signals, an activation condition can only be relevant if present in both sets with opposite polarity. This detects pattern-only mutual exclusion by running an additional SAT query before importing the input cone logic. If that is already UNSAT, we remove all non-relevant condition and re-simplify the remaining patterns. In cases of pattern-only mutual exclusion, this will often produce much smaller selection logic and avoid the more costly SAT query that includes the input cones. --- passes/opt/share.cc | 95 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 77 insertions(+), 18 deletions(-) diff --git a/passes/opt/share.cc b/passes/opt/share.cc index 0820bad12..7ffe26b2b 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -1000,6 +1000,53 @@ struct ShareWorker } } + pool> pattern_bits(const pool &activation_patterns) + { + pool> bits; + for (auto const &pattern : activation_patterns) { + for (int i = 0; i < GetSize(pattern.second); ++i) { + SigBit bit = pattern.first[i]; + State val = pattern.second[i]; + bits.emplace(bit, val); + } + } + return bits; + } + + void onesided_restrict_activiation_patterns( + pool &activation_patterns, const pool> &other_bits) + { + pool new_activiation_patterns; + + for (auto const &pattern : activation_patterns) { + ssc_pair_t new_pair; + for (int i = 0; i < GetSize(pattern.second); ++i) { + SigBit bit = pattern.first[i]; + State val = pattern.second[i]; + if (other_bits.count({bit, val == State::S0 ? State::S1 : State::S0})) { + new_pair.first.append(bit); + new_pair.second.append(val); + } + } + new_activiation_patterns.emplace(std::move(new_pair)); + } + + activation_patterns = std::move(new_activiation_patterns); + } + + // Only valid if the patterns on their own (i.e. without considering their input cone) are mutually exclusive! + void restrict_activiation_patterns(pool &activation_patterns, pool &other_activation_patterns) + { + pool> bits = pattern_bits(activation_patterns); + pool> other_bits = pattern_bits(other_activation_patterns); + + onesided_restrict_activiation_patterns(activation_patterns, other_bits); + onesided_restrict_activiation_patterns(other_activation_patterns, bits); + + optimize_activation_patterns(activation_patterns); + optimize_activation_patterns(other_activation_patterns); + } + RTLIL::SigSpec make_cell_activation_logic(const pool &activation_patterns, pool &supercell_aux) { RTLIL::Wire *all_cases_wire = module->addWire(NEW_ID, 0); @@ -1299,17 +1346,18 @@ struct ShareWorker other_cell_active.push_back(qcsat.ez->vec_eq(qcsat.importSig(p.first), qcsat.importSig(p.second))); all_ctrl_signals.append(p.first); } + int sub1 = qcsat.ez->expression(qcsat.ez->OpOr, cell_active); + int sub2 = qcsat.ez->expression(qcsat.ez->OpOr, other_cell_active); + bool pattern_only_solve = qcsat.ez->solve(qcsat.ez->AND(sub1, sub2)); qcsat.prepare(); - int sub1 = qcsat.ez->expression(qcsat.ez->OpOr, cell_active); if (!qcsat.ez->solve(sub1)) { log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(cell)); cells_to_remove.insert(cell); break; } - int sub2 = qcsat.ez->expression(qcsat.ez->OpOr, other_cell_active); if (!qcsat.ez->solve(sub2)) { log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(other_cell)); cells_to_remove.insert(other_cell); @@ -1317,28 +1365,39 @@ struct ShareWorker continue; } - qcsat.ez->non_incremental(); + if (pattern_only_solve) { + qcsat.ez->non_incremental(); - all_ctrl_signals.sort_and_unify(); - std::vector sat_model = qcsat.importSig(all_ctrl_signals); - std::vector sat_model_values; + all_ctrl_signals.sort_and_unify(); + std::vector sat_model = qcsat.importSig(all_ctrl_signals); + std::vector sat_model_values; - qcsat.ez->assume(qcsat.ez->AND(sub1, sub2)); + qcsat.ez->assume(qcsat.ez->AND(sub1, sub2)); - log(" Size of SAT problem: %zu cells, %d variables, %d clauses\n", - qcsat.imported_cells.size(), qcsat.ez->numCnfVariables(), qcsat.ez->numCnfClauses()); + log(" Size of SAT problem: %zu cells, %d variables, %d clauses\n", + qcsat.imported_cells.size(), qcsat.ez->numCnfVariables(), qcsat.ez->numCnfClauses()); - if (qcsat.ez->solve(sat_model, sat_model_values)) { - log(" According to the SAT solver this pair of cells can not be shared.\n"); - log(" Model from SAT solver: %s = %d'", log_signal(all_ctrl_signals), GetSize(sat_model_values)); - for (int i = GetSize(sat_model_values)-1; i >= 0; i--) - log("%c", sat_model_values[i] ? '1' : '0'); - log("\n"); - continue; + if (qcsat.ez->solve(sat_model, sat_model_values)) { + log(" According to the SAT solver this pair of cells can not be shared.\n"); + log(" Model from SAT solver: %s = %d'", log_signal(all_ctrl_signals), GetSize(sat_model_values)); + for (int i = GetSize(sat_model_values)-1; i >= 0; i--) + log("%c", sat_model_values[i] ? '1' : '0'); + log("\n"); + continue; + } + + log(" According to the SAT solver this pair of cells can be shared.\n"); + } else { + log(" According to the SAT solver this pair of cells can be shared. (Pattern only case)\n"); + restrict_activiation_patterns(filtered_cell_activation_patterns, filtered_other_cell_activation_patterns); + + for (auto &p : filtered_cell_activation_patterns) + log(" Simplified activation pattern for cell %s: %s = %s\n", log_id(cell), log_signal(p.first), log_signal(p.second)); + + for (auto &p : filtered_other_cell_activation_patterns) + log(" Simplified activation pattern for cell %s: %s = %s\n", log_id(other_cell), log_signal(p.first), log_signal(p.second)); } - log(" According to the SAT solver this pair of cells can be shared.\n"); - if (find_in_input_cone(cell, other_cell)) { log(" Sharing not possible: %s is in input cone of %s.\n", log_id(other_cell), log_id(cell)); continue; From 27ed77ea2428433e4efc1ff29d0ebc36753f9458 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Tue, 15 Apr 2025 10:25:39 +0200 Subject: [PATCH 46/55] share: Keep filtered activation patterns for the supercell The previous commit introduced code that optimizes the activation patterns to be able to generate smaller activation logic. The resulting supercell was then enqueued as shareable using those optimized activation patterns. The condition represented by the optimized patterns is an over-approximation of the actual activiation condition. This means using it as activiation for the supercell loses precision and pessimises sharing of the supercell with further cells, breaking the sat/share test. This commit fixes that by using the optimized activiation patterns only for the generation of activation logic and using the original patterns for enqueuing the supercell. --- passes/opt/share.cc | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/passes/opt/share.cc b/passes/opt/share.cc index 7ffe26b2b..eb6476d57 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -1365,6 +1365,9 @@ struct ShareWorker continue; } + pool optimized_cell_activation_patterns = filtered_cell_activation_patterns; + pool optimized_other_cell_activation_patterns = filtered_other_cell_activation_patterns; + if (pattern_only_solve) { qcsat.ez->non_incremental(); @@ -1389,12 +1392,12 @@ struct ShareWorker log(" According to the SAT solver this pair of cells can be shared.\n"); } else { log(" According to the SAT solver this pair of cells can be shared. (Pattern only case)\n"); - restrict_activiation_patterns(filtered_cell_activation_patterns, filtered_other_cell_activation_patterns); + restrict_activiation_patterns(optimized_cell_activation_patterns, optimized_other_cell_activation_patterns); - for (auto &p : filtered_cell_activation_patterns) + for (auto &p : optimized_cell_activation_patterns) log(" Simplified activation pattern for cell %s: %s = %s\n", log_id(cell), log_signal(p.first), log_signal(p.second)); - for (auto &p : filtered_other_cell_activation_patterns) + for (auto &p : optimized_other_cell_activation_patterns) log(" Simplified activation pattern for cell %s: %s = %s\n", log_id(other_cell), log_signal(p.first), log_signal(p.second)); } @@ -1413,20 +1416,20 @@ struct ShareWorker int cell_select_score = 0; int other_cell_select_score = 0; - for (auto &p : filtered_cell_activation_patterns) + for (auto &p : optimized_cell_activation_patterns) cell_select_score += p.first.size(); - for (auto &p : filtered_other_cell_activation_patterns) + for (auto &p : optimized_other_cell_activation_patterns) other_cell_select_score += p.first.size(); RTLIL::Cell *supercell; pool supercell_aux; if (cell_select_score <= other_cell_select_score) { - RTLIL::SigSpec act = make_cell_activation_logic(filtered_cell_activation_patterns, supercell_aux); + RTLIL::SigSpec act = make_cell_activation_logic(optimized_cell_activation_patterns, supercell_aux); supercell = make_supercell(cell, other_cell, act, supercell_aux); log(" Activation signal for %s: %s\n", log_id(cell), log_signal(act)); } else { - RTLIL::SigSpec act = make_cell_activation_logic(filtered_other_cell_activation_patterns, supercell_aux); + RTLIL::SigSpec act = make_cell_activation_logic(optimized_other_cell_activation_patterns, supercell_aux); supercell = make_supercell(other_cell, cell, act, supercell_aux); log(" Activation signal for %s: %s\n", log_id(other_cell), log_signal(act)); } From 7593b5b224e54c871d5c48484eceac315b82ea9b Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Tue, 15 Apr 2025 12:02:09 +0200 Subject: [PATCH 47/55] share: Only print optimized activation patterns when different This removes redundant information from the log and makes it easier to spot where the new optimization had an effect. --- passes/opt/share.cc | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/passes/opt/share.cc b/passes/opt/share.cc index eb6476d57..580549e4d 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -1013,11 +1013,13 @@ struct ShareWorker return bits; } - void onesided_restrict_activiation_patterns( + bool onesided_restrict_activiation_patterns( pool &activation_patterns, const pool> &other_bits) { pool new_activiation_patterns; + bool simplified = false; + for (auto const &pattern : activation_patterns) { ssc_pair_t new_pair; for (int i = 0; i < GetSize(pattern.second); ++i) { @@ -1026,25 +1028,31 @@ struct ShareWorker if (other_bits.count({bit, val == State::S0 ? State::S1 : State::S0})) { new_pair.first.append(bit); new_pair.second.append(val); + } else { + simplified = true; } } new_activiation_patterns.emplace(std::move(new_pair)); } activation_patterns = std::move(new_activiation_patterns); + return simplified; } // Only valid if the patterns on their own (i.e. without considering their input cone) are mutually exclusive! - void restrict_activiation_patterns(pool &activation_patterns, pool &other_activation_patterns) + bool restrict_activiation_patterns(pool &activation_patterns, pool &other_activation_patterns) { pool> bits = pattern_bits(activation_patterns); pool> other_bits = pattern_bits(other_activation_patterns); - onesided_restrict_activiation_patterns(activation_patterns, other_bits); - onesided_restrict_activiation_patterns(other_activation_patterns, bits); + bool simplified = false; + simplified |= onesided_restrict_activiation_patterns(activation_patterns, other_bits); + simplified |= onesided_restrict_activiation_patterns(other_activation_patterns, bits); optimize_activation_patterns(activation_patterns); optimize_activation_patterns(other_activation_patterns); + + return simplified; } RTLIL::SigSpec make_cell_activation_logic(const pool &activation_patterns, pool &supercell_aux) @@ -1392,13 +1400,14 @@ struct ShareWorker log(" According to the SAT solver this pair of cells can be shared.\n"); } else { log(" According to the SAT solver this pair of cells can be shared. (Pattern only case)\n"); - restrict_activiation_patterns(optimized_cell_activation_patterns, optimized_other_cell_activation_patterns); - for (auto &p : optimized_cell_activation_patterns) - log(" Simplified activation pattern for cell %s: %s = %s\n", log_id(cell), log_signal(p.first), log_signal(p.second)); + if (restrict_activiation_patterns(optimized_cell_activation_patterns, optimized_other_cell_activation_patterns)) { + for (auto &p : optimized_cell_activation_patterns) + log(" Simplified activation pattern for cell %s: %s = %s\n", log_id(cell), log_signal(p.first), log_signal(p.second)); - for (auto &p : optimized_other_cell_activation_patterns) - log(" Simplified activation pattern for cell %s: %s = %s\n", log_id(other_cell), log_signal(p.first), log_signal(p.second)); + for (auto &p : optimized_other_cell_activation_patterns) + log(" Simplified activation pattern for cell %s: %s = %s\n", log_id(other_cell), log_signal(p.first), log_signal(p.second)); + } } if (find_in_input_cone(cell, other_cell)) { From 4b273a4ae9e4fc43b9580de82015a56d4095c85b Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Tue, 15 Apr 2025 12:04:09 +0200 Subject: [PATCH 48/55] share: Cleanup and additional testing Fixes a typo and adds another test case that triggers the fallback behavior as the existing tests all trigger the new optimization. --- passes/opt/share.cc | 16 ++++++++-------- tests/sat/share.v | 23 +++++++++++++++++++++++ tests/sat/share.ys | 6 +++++- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/passes/opt/share.cc b/passes/opt/share.cc index 580549e4d..1d7ba7d98 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -1013,10 +1013,10 @@ struct ShareWorker return bits; } - bool onesided_restrict_activiation_patterns( + bool onesided_restrict_activation_patterns( pool &activation_patterns, const pool> &other_bits) { - pool new_activiation_patterns; + pool new_activation_patterns; bool simplified = false; @@ -1032,22 +1032,22 @@ struct ShareWorker simplified = true; } } - new_activiation_patterns.emplace(std::move(new_pair)); + new_activation_patterns.emplace(std::move(new_pair)); } - activation_patterns = std::move(new_activiation_patterns); + activation_patterns = std::move(new_activation_patterns); return simplified; } // Only valid if the patterns on their own (i.e. without considering their input cone) are mutually exclusive! - bool restrict_activiation_patterns(pool &activation_patterns, pool &other_activation_patterns) + bool restrict_activation_patterns(pool &activation_patterns, pool &other_activation_patterns) { pool> bits = pattern_bits(activation_patterns); pool> other_bits = pattern_bits(other_activation_patterns); bool simplified = false; - simplified |= onesided_restrict_activiation_patterns(activation_patterns, other_bits); - simplified |= onesided_restrict_activiation_patterns(other_activation_patterns, bits); + simplified |= onesided_restrict_activation_patterns(activation_patterns, other_bits); + simplified |= onesided_restrict_activation_patterns(other_activation_patterns, bits); optimize_activation_patterns(activation_patterns); optimize_activation_patterns(other_activation_patterns); @@ -1401,7 +1401,7 @@ struct ShareWorker } else { log(" According to the SAT solver this pair of cells can be shared. (Pattern only case)\n"); - if (restrict_activiation_patterns(optimized_cell_activation_patterns, optimized_other_cell_activation_patterns)) { + if (restrict_activation_patterns(optimized_cell_activation_patterns, optimized_other_cell_activation_patterns)) { for (auto &p : optimized_cell_activation_patterns) log(" Simplified activation pattern for cell %s: %s = %s\n", log_id(cell), log_signal(p.first), log_signal(p.second)); diff --git a/tests/sat/share.v b/tests/sat/share.v index e06fc8f1e..29e423137 100644 --- a/tests/sat/share.v +++ b/tests/sat/share.v @@ -30,3 +30,26 @@ module test_2( end endmodule + +module test_3( + input [3:0] s, + input [7:0] a, b, c, + output reg [7:0] y0, + output reg [7:0] y1, + output reg [7:0] y2, + output reg [7:0] y3, +); + wire is_onehot = s & (s - 1); + + always @* begin + y0 <= 0; + y1 <= 0; + y2 <= 0; + y3 <= 0; + if (s < 3) y0 <= b / c; + if (3 <= s && s < 6) y1 <= c / b; + if (6 <= s && s < 9) y2 <= a / b; + if (9 <= s && s < 12) y3 <= b / a; + end +endmodule + diff --git a/tests/sat/share.ys b/tests/sat/share.ys index f2f5d649d..ef88d55c3 100644 --- a/tests/sat/share.ys +++ b/tests/sat/share.ys @@ -3,11 +3,13 @@ proc;; copy test_1 gold_1 copy test_2 gold_2 -share test_1 test_2;; +copy test_3 gold_3 +share test_1 test_2 test_3;; select -assert-count 1 test_1/t:$mul select -assert-count 1 test_2/t:$mul select -assert-count 1 test_2/t:$div +select -assert-count 1 test_3/t:$div miter -equiv -flatten -make_outputs -make_outcmp gold_1 test_1 miter_1 sat -verify -prove trigger 0 -show-inputs -show-outputs miter_1 @@ -15,3 +17,5 @@ sat -verify -prove trigger 0 -show-inputs -show-outputs miter_1 miter -equiv -flatten -make_outputs -make_outcmp gold_2 test_2 miter_2 sat -verify -prove trigger 0 -show-inputs -show-outputs miter_2 +miter -equiv -flatten -make_outputs -make_outcmp gold_3 test_3 miter_3 +sat -verify -prove trigger 0 -show-inputs -show-outputs miter_3 From c894685f26850a7313dcbf800c306dbab8681517 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 16 Apr 2025 00:23:21 +0000 Subject: [PATCH 49/55] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c0fcdfe62..3f7c8aa38 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.52+45 +YOSYS_VER := 0.52+50 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From 04098933c724075cf350a79c3dc1f0dda6ec546b Mon Sep 17 00:00:00 2001 From: David Sawatzke Date: Wed, 16 Apr 2025 13:26:50 +0200 Subject: [PATCH 50/55] cxxrtl: Add internal cell "bwmux" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors the implementation for the smt2 backend Co-authored-by: Martin Povišer --- backends/cxxrtl/cxxrtl_backend.cc | 10 +++++++++- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 5 +++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index b9958c5fb..052699ad6 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -200,7 +200,7 @@ bool is_extending_cell(RTLIL::IdString type) bool is_inlinable_cell(RTLIL::IdString type) { return is_unary_cell(type) || is_binary_cell(type) || type.in( - ID($mux), ID($concat), ID($slice), ID($pmux), ID($bmux), ID($demux)); + ID($mux), ID($concat), ID($slice), ID($pmux), ID($bmux), ID($demux), ID($bwmux)); } bool is_ff_cell(RTLIL::IdString type) @@ -1198,6 +1198,14 @@ struct CxxrtlWorker { f << ">("; dump_sigspec_rhs(cell->getPort(ID::S), for_debug); f << ").val()"; + // Bitwise muxes + } else if (cell->type == ID($bwmux)) { + dump_sigspec_rhs(cell->getPort(ID::A), for_debug); + f << ".bwmux("; + dump_sigspec_rhs(cell->getPort(ID::B), for_debug); + f << ","; + dump_sigspec_rhs(cell->getPort(ID::S), for_debug); + f << ").val()"; // Demuxes } else if (cell->type == ID($demux)) { dump_sigspec_rhs(cell->getPort(ID::A), for_debug); diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index 9634b833b..37c84895f 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -498,6 +498,11 @@ struct value : public expr_base> { return result; } + CXXRTL_ALWAYS_INLINE + value bwmux(const value &b, const value &s) const { + return (bit_and(s.bit_not())).bit_or(b.bit_and(s)); + } + template value demux(const value &sel) const { static_assert(Bits << SelBits == ResultBits, "invalid sizes used in demux()"); From 5101b9fcba8f892a45bb5371e6f25f4d2ae9a46d Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 16 Apr 2025 18:52:49 +0200 Subject: [PATCH 51/55] liberty: Fix handling of non-ascii characters Use an `unsigned char` buffer to ensure characters cast to an `int` are in the range 0 <= c <= 255. --- passes/techmap/libparse.cc | 2 +- passes/techmap/libparse.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index bc7758cbd..97a94ece0 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -75,7 +75,7 @@ bool LibertyInputStream::extend_buffer_once() buffer.resize(buf_end + chunk_size); } - size_t read_size = f.rdbuf()->sgetn(buffer.data() + buf_end, chunk_size); + size_t read_size = f.rdbuf()->sgetn((char *)buffer.data() + buf_end, chunk_size); buf_end += read_size; if (read_size < chunk_size) eof = true; diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index 3ba381fdf..f834044ae 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -92,7 +92,7 @@ namespace Yosys class LibertyInputStream { std::istream &f; - std::vector buffer; + std::vector buffer; size_t buf_pos = 0; size_t buf_end = 0; bool eof = false; @@ -107,7 +107,7 @@ namespace Yosys LibertyInputStream(std::istream &f) : f(f) {} size_t buffered_size() { return buf_end - buf_pos; } - const char *buffered_data() { return buffer.data() + buf_pos; } + const unsigned char *buffered_data() { return buffer.data() + buf_pos; } int get() { if (buf_pos == buf_end) From 418e795235a0b2b8ebd5a39d8b0fa32c8707f0e7 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 16 Apr 2025 19:03:05 +0200 Subject: [PATCH 52/55] liberty: Error when a read liberty file has no nodes --- passes/techmap/libparse.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index f834044ae..e52f91e77 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -176,6 +176,14 @@ namespace Yosys LibertyParser(std::istream &f) : f(f), line(1) { shared_ast.reset(parse()); ast = shared_ast.get(); + if (!ast) { +#ifdef FILTERLIB + fprintf(stderr, "No entries found in liberty file.\n"); + exit(1); +#else + log_error("No entries found in liberty file.\n"); +#endif + } } #ifndef FILTERLIB @@ -186,6 +194,9 @@ namespace Yosys LibertyAstCache::instance.parsed_ast(fname, shared_ast); } ast = shared_ast.get(); + if (!ast) { + log_error("No entries found in liberty file `%s'.\n", fname.c_str()); + } } #endif }; From ce74404890242d8a45bccb6ee2fefd302316efcf Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 16 Apr 2025 19:12:01 +0200 Subject: [PATCH 53/55] liberty: Error on unclosed curly braces This is an indication that the liberty file was truncated, which shouldn't be silently ignored. --- passes/techmap/libparse.cc | 23 +++++++++++++++++++---- passes/techmap/libparse.h | 6 +++--- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 97a94ece0..5594d5443 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -436,6 +436,9 @@ void LibertyParser::report_unexpected_token(int tok) eReport += "'."; error(eReport); break; + case EOF: + error("Unexpected end of file"); + break; default: eReport = "Unexpected token: "; eReport += static_cast(tok); @@ -484,7 +487,7 @@ void LibertyParser::parse_vector_range(int tok) } } -LibertyAst *LibertyParser::parse() +LibertyAst *LibertyParser::parse(bool top_level) { std::string str; @@ -498,7 +501,13 @@ LibertyAst *LibertyParser::parse() while ((tok == 'n') || (tok == ';')) tok = lexer(str); - if (tok == '}' || tok < 0) + if (tok == EOF) { + if (top_level) + return NULL; + report_unexpected_token(tok); + } + + if (tok == '}') return NULL; if (tok != 'v') { @@ -571,12 +580,18 @@ LibertyAst *LibertyParser::parse() } if (tok == '{') { + bool terminated = false; while (1) { - LibertyAst *child = parse(); - if (child == NULL) + LibertyAst *child = parse(false); + if (child == NULL) { + terminated = true; break; + } ast->children.push_back(child); } + if (!terminated) { + report_unexpected_token(EOF); + } break; } diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index e52f91e77..61dc83867 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -165,7 +165,7 @@ namespace Yosys void report_unexpected_token(int tok); void parse_vector_range(int tok); - LibertyAst *parse(); + LibertyAst *parse(bool top_level); void error() const; void error(const std::string &str) const; @@ -174,7 +174,7 @@ namespace Yosys const LibertyAst *ast = nullptr; LibertyParser(std::istream &f) : f(f), line(1) { - shared_ast.reset(parse()); + shared_ast.reset(parse(true)); ast = shared_ast.get(); if (!ast) { #ifdef FILTERLIB @@ -190,7 +190,7 @@ namespace Yosys LibertyParser(std::istream &f, const std::string &fname) : f(f), line(1) { shared_ast = LibertyAstCache::instance.cached_ast(fname); if (!shared_ast) { - shared_ast.reset(parse()); + shared_ast.reset(parse(true)); LibertyAstCache::instance.parsed_ast(fname, shared_ast); } ast = shared_ast.get(); From c555add2310db3556db09a2e1e15864917a93fc3 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Thu, 17 Apr 2025 00:17:06 +0200 Subject: [PATCH 54/55] liberty: Test non-ascii characters --- tests/liberty/non-ascii.lib | 14 ++++++++++++++ tests/liberty/non-ascii.lib.filtered.ok | 12 ++++++++++++ tests/liberty/non-ascii.lib.verilogsim.ok | 5 +++++ 3 files changed, 31 insertions(+) create mode 100644 tests/liberty/non-ascii.lib create mode 100644 tests/liberty/non-ascii.lib.filtered.ok create mode 100644 tests/liberty/non-ascii.lib.verilogsim.ok diff --git a/tests/liberty/non-ascii.lib b/tests/liberty/non-ascii.lib new file mode 100644 index 000000000..1ac636e34 --- /dev/null +++ b/tests/liberty/non-ascii.lib @@ -0,0 +1,14 @@ +// The parser used to choke on the copyright symbol even in comments +// © ® ø Φ +library(dummy) { + cell(buffer) { + area : 1 ; + pin(A) { + direction : input ; + } + pin(Y) { + direction : output ; + function : "A" ; + } + } +} \ No newline at end of file diff --git a/tests/liberty/non-ascii.lib.filtered.ok b/tests/liberty/non-ascii.lib.filtered.ok new file mode 100644 index 000000000..f03f725f7 --- /dev/null +++ b/tests/liberty/non-ascii.lib.filtered.ok @@ -0,0 +1,12 @@ +library(dummy) { + cell(buffer) { + area : 1 ; + pin(A) { + direction : input ; + } + pin(Y) { + direction : output ; + function : "A" ; + } + } +} diff --git a/tests/liberty/non-ascii.lib.verilogsim.ok b/tests/liberty/non-ascii.lib.verilogsim.ok new file mode 100644 index 000000000..de5e2e5f3 --- /dev/null +++ b/tests/liberty/non-ascii.lib.verilogsim.ok @@ -0,0 +1,5 @@ +module buffer (A, Y); + input A; + output Y; + assign Y = A; // "A" +endmodule From 08b3a9fc7b21aa6b4ccce0faa4c6f666f7a426c9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 17 Apr 2025 00:22:57 +0000 Subject: [PATCH 55/55] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3f7c8aa38..76ac4bb18 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.52+50 +YOSYS_VER := 0.52+63 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2)