Optimize reading selected words on forced wide (#7391 repair) (#7554 partial) (#7572)

This commit is contained in:
Krzysztof Bieganski 2026-05-14 13:38:42 +02:00 committed by GitHub
parent de4f743d0e
commit c518abd22a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 149 additions and 12 deletions

View File

@ -158,22 +158,21 @@ private:
return static_cast<T>((cur & ~mask) | (rhsBits & mask));
}
template <typename T>
static typename std::enable_if<VlIsVlWide<T>::value, T>::type applyEntry(T result,
const Entry& entry) {
EData* const reswp = result.data();
const int lword = VL_BITWORD_E(entry.m_lsb);
const int hword = VL_BITWORD_E(entry.m_msb);
static void applyEntry(WDataOutP reswp, const Entry& entry, int entryLsb, int entryMsb,
int lsbOffset) {
const int resLsb = entryLsb - lsbOffset;
const int resMsb = entryMsb - lsbOffset;
const int lword = VL_BITWORD_E(resLsb);
const int hword = VL_BITWORD_E(resMsb);
for (int word = lword; word <= hword; ++word) {
const int wordLsb = word * VL_EDATASIZE;
const int segLsb = std::max(entry.m_lsb, wordLsb);
const int segMsb = std::min(entry.m_msb, wordLsb + VL_EDATASIZE - 1);
const int segLsb = std::max(resLsb, wordLsb);
const int segMsb = std::min(resMsb, wordLsb + VL_EDATASIZE - 1);
const int segWidth = segMsb - segLsb + 1;
const int bitOffset = segLsb - wordLsb;
const int rhsLsb = segLsb - entry.m_rhsLsb;
const int rhsLsb = lsbOffset + segLsb - entry.m_rhsLsb;
reswp[word] = applyBits(reswp[word], entry, bitOffset, segWidth, rhsLsb);
}
return result;
}
template <typename T>
@ -196,6 +195,29 @@ private:
return *static_cast<const VlForceBaseType<T>*>(entry.m_rhsDatap);
}
template <typename T>
typename std::enable_if<VlIsVlWide<T>::value>::type applyEntries(T& val) const {
for (const auto& entry : m_entries) {
applyEntry(val.data(), entry, entry.m_lsb, entry.m_msb, 0);
}
}
template <typename T>
typename std::enable_if<!VlIsVlWide<T>::value>::type applyEntries(T& val) const {
for (const auto& entry : m_entries) val = applyEntry(val, entry);
}
void readSel(int lbits, WDataInP valp, WDataOutP reswp, int lsb, int width) const {
VL_SEL_WWII(width, lbits, reswp, valp, lsb, width);
const int msb = lsb + width - 1;
auto it = std::lower_bound(m_entries.begin(), m_entries.end(), lsb,
[](const Entry& e, int bit) { return e.m_msb < bit; });
while (it != m_entries.end() && it->m_lsb <= msb) {
applyEntry(reswp, *it, std::max(it->m_lsb, lsb), std::min(it->m_msb, msb), lsb);
++it;
}
}
public:
VlForceVec() = default;
@ -219,8 +241,7 @@ public:
}
return val;
}
for (const auto& entry : m_entries) { val = applyEntry(val, entry); }
applyEntries(val);
return val;
}
@ -238,6 +259,29 @@ public:
return origVal;
}
IData readSelI(int lbits, WDataInP valp, int lsb, int width) const {
IData result;
readSel(lbits, valp, &result, lsb, width);
result &= VL_MASK_I(width);
return result;
}
QData readSelQ(int lbits, WDataInP valp, int lsb, int width) const {
QData result;
readSel(lbits, valp, reinterpret_cast<WDataOutP>(&result), lsb, width);
result &= VL_MASK_Q(width);
return result;
}
template <std::size_t N_Words>
VlWide<N_Words> readSelW(int lbits, WDataInP valp, int lsb, int width) const {
VlWide<N_Words> result;
WDataOutP const reswp = result.data();
readSel(lbits, valp, reswp, lsb, width);
reswp[N_Words - 1] &= VL_MASK_E(width);
return result;
}
void addForce(int lsb, int msb, const void* rhsDatap, int rhsLsb) {
assert(lsb <= msb);
assert(rhsDatap);

View File

@ -815,6 +815,7 @@ public:
FORCE_ADD,
FORCE_READ,
FORCE_READ_INDEX,
FORCE_READ_SEL,
FORCE_RELEASE,
FORCE_TOUCH,
FORK_DONE,
@ -968,6 +969,7 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) {
{FORCE_ADD, "addForce", false}, \
{FORCE_READ, "read", true}, \
{FORCE_READ_INDEX, "readIndex", true}, \
{FORCE_READ_SEL, "readSel", true}, \
{FORCE_RELEASE, "release", false}, \
{FORCE_TOUCH, "touch", false}, \
{FORK_DONE, "done", false}, \

View File

@ -723,6 +723,10 @@ public:
iterateConst(nodep->fromp());
putns(nodep, nodep->usePtr() ? "->" : ".");
putns(nodep, nodep->name());
if (nodep->method() == VCMethod::FORCE_READ_SEL) {
emitIQW(nodep);
if (nodep->isWide()) puts("<" + cvtToStr(nodep->dtypep()->widthWords()) + ">");
}
puts("(");
bool comma = false;
int argNum = 0;

View File

@ -1088,6 +1088,33 @@ class ForceReplaceVisitor final : public VNVisitor {
iterateLogic(nodep);
}
void visit(AstSenItem* nodep) override { iterateLogic(nodep); }
void visit(AstSel* nodep) override {
// Replace Sel on a wide with readSelI/Q/W to avoid materializing the full value
if (AstVarRef* const refp = VN_CAST(nodep->fromp(), VarRef)) {
if (!ForceState::isNotReplaceable(refp) && refp->access().isReadOnly()) {
AstVar* const varp = refp->varp();
const ForceState::VarForceInfo* const varInfo = m_state.getVarInfo(varp);
if (varInfo && !varInfo->m_forceRdVscp && !varInfo->m_forces.empty()
&& ForceState::isBitwiseDType(varp) && varp->dtypep()->isWide()) {
FileLine* const flp = nodep->fileline();
ForceState::markNonReplaceable(refp);
AstVarRef* const refClonep = refp->cloneTreePure(false);
ForceState::markNonReplaceable(refClonep);
AstCMethodHard* const callp = new AstCMethodHard{
flp, new AstVarRef{flp, varInfo->m_forceVecVscp, VAccess::READ},
VCMethod::FORCE_READ_SEL, ForceState::makeConst32(flp, varp->width())};
callp->addPinsp(refClonep);
callp->addPinsp(nodep->lsbp()->cloneTreePure(false));
callp->addPinsp(ForceState::makeConst32(flp, nodep->width()));
callp->dtypeFrom(nodep);
nodep->replaceWith(callp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
}
}
visit(static_cast<AstNode*>(nodep));
}
void visit(AstArraySel* nodep) override {
if (nodep->backp() && VN_IS(nodep->backp(), ArraySel)) {
// Only the outermost unpacked array selection should become a force-aware read;

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,42 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
// verilog_format: on
module t (
input clk
);
integer cyc = 0;
logic [127:0] sig;
always @(posedge clk) begin
cyc <= cyc + 1;
sig <= '0;
sig[127:64] <= '0; // write path
if (cyc == 1) begin
force sig[31] = 1'b1;
force sig[32] = 1'b1;
end
else if (cyc == 3) begin
`checkh(sig[33:26], 8'h60); // width <= 8
`checkh(sig[39:24], 16'h180); // 8 < width <= 16
`checkh(sig[40:20], 21'h1800); // 16 < width <= 32
`checkh(sig[51:20], 32'h1800);
`checkh(sig[29:0], 30'h0);
`checkh(sig[50:10], 41'h600000); // 32 < width <= 64
`checkh(sig[73:10], 64'h600000);
`checkh(sig[100:5], (96'h1 << 26) | (96'h1 << 27)); // width > 64
`checkh(sig[70:6], (65'h1 << 25) | (65'h1 << 26));
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule