Optimize read selects with no overlapping forces with regular reads (#7594)
This commit is contained in:
parent
4a1f17e75f
commit
fb617e49dd
|
|
@ -37,6 +37,7 @@
|
|||
#include "V3Force.h"
|
||||
|
||||
#include "V3AstUserAllocator.h"
|
||||
#include "V3Stats.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
|
@ -611,6 +612,13 @@ public:
|
|||
return it2->second;
|
||||
}
|
||||
|
||||
static bool selOverlapsAnyForce(const VarForceInfo& varInfo, int selLsb, int selMsb) {
|
||||
for (const auto& pair : varInfo.m_forces) {
|
||||
if (pair.second.m_rangeLsb <= selMsb && pair.second.m_rangeMsb >= selLsb) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
AstNodeExpr* createForceReadExpression(const VarForceInfo& varInfo,
|
||||
AstVarRef* originalRefp) const {
|
||||
FileLine* const flp = originalRefp->fileline();
|
||||
|
|
@ -1044,6 +1052,7 @@ public:
|
|||
|
||||
class ForceReplaceVisitor final : public VNVisitor {
|
||||
const ForceState& m_state;
|
||||
VDouble0 m_nonOverlappingForceSels; // Statistic tracking
|
||||
AstNodeStmt* m_stmtp = nullptr;
|
||||
bool m_inLogic = false;
|
||||
|
||||
|
|
@ -1090,30 +1099,45 @@ class ForceReplaceVisitor final : public VNVisitor {
|
|||
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;
|
||||
}
|
||||
AstVarRef* const refp = VN_CAST(nodep->fromp(), VarRef);
|
||||
if (!refp || ForceState::isNotReplaceable(refp) || !refp->access().isReadOnly()) {
|
||||
visit(static_cast<AstNode*>(nodep));
|
||||
return;
|
||||
}
|
||||
|
||||
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()) {
|
||||
visit(static_cast<AstNode*>(nodep));
|
||||
return;
|
||||
}
|
||||
|
||||
if (const AstConst* const lsbConstp = VN_CAST(nodep->lsbp(), Const)) {
|
||||
const int selLsb = lsbConstp->toSInt();
|
||||
const int selMsb = selLsb + nodep->width() - 1;
|
||||
if (!varp->isSigPublic()
|
||||
&& !ForceState::selOverlapsAnyForce(*varInfo, selLsb, selMsb)) {
|
||||
m_nonOverlappingForceSels++;
|
||||
ForceState::markNonReplaceable(refp);
|
||||
visit(static_cast<AstNode*>(nodep));
|
||||
return;
|
||||
}
|
||||
}
|
||||
visit(static_cast<AstNode*>(nodep));
|
||||
|
||||
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);
|
||||
}
|
||||
void visit(AstArraySel* nodep) override {
|
||||
if (nodep->backp() && VN_IS(nodep->backp(), ArraySel)) {
|
||||
|
|
@ -1236,6 +1260,9 @@ public:
|
|||
: m_state{state} {
|
||||
iterateAndNextNull(nodep->modulesp());
|
||||
}
|
||||
~ForceReplaceVisitor() override {
|
||||
V3Stats::addStat("Non-overlapping force sels", m_nonOverlappingForceSels);
|
||||
}
|
||||
};
|
||||
//######################################################################
|
||||
//
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile()
|
||||
test.compile(verilator_flags2=["--stats"])
|
||||
|
||||
test.file_grep(test.stats, r'Non-overlapping force sels\s+(\d+)', 2)
|
||||
|
||||
test.execute()
|
||||
|
||||
|
|
|
|||
|
|
@ -14,16 +14,20 @@ module t (
|
|||
);
|
||||
integer cyc = 0;
|
||||
logic [127:0] sig;
|
||||
logic [127:0] publicSig /*verilator public_flat_rw*/;
|
||||
|
||||
always @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
sig <= '0;
|
||||
publicSig <= '0;
|
||||
|
||||
sig[127:64] <= '0; // write path
|
||||
|
||||
if (cyc == 1) begin
|
||||
force sig[31] = 1'b1;
|
||||
force sig[32] = 1'b1;
|
||||
force publicSig[31] = 1'b1;
|
||||
force publicSig[32] = 1'b1;
|
||||
end
|
||||
else if (cyc == 3) begin
|
||||
`checkh(sig[33:26], 8'h60); // width <= 8
|
||||
|
|
@ -35,6 +39,16 @@ module t (
|
|||
`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));
|
||||
|
||||
`checkh(publicSig[33:26], 8'h60); // width <= 8
|
||||
`checkh(publicSig[39:24], 16'h180); // 8 < width <= 16
|
||||
`checkh(publicSig[40:20], 21'h1800); // 16 < width <= 32
|
||||
`checkh(publicSig[51:20], 32'h1800);
|
||||
`checkh(publicSig[29:0], 30'h0);
|
||||
`checkh(publicSig[50:10], 41'h600000); // 32 < width <= 64
|
||||
`checkh(publicSig[73:10], 64'h600000);
|
||||
`checkh(publicSig[100:5], (96'h1 << 26) | (96'h1 << 27)); // width > 64
|
||||
`checkh(publicSig[70:6], (65'h1 << 25) | (65'h1 << 26));
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue