Set trigger vector in whole words (#5857)

Having many triggers still hits a bottleneck in LLVM leading to long
compile times.

Instead of setting triggers bit-wise, set them as a whole 64-bit word
when possible. This improves C++ compile times by ~4x on some large
designs and has minor run-time performance benefit.
This commit is contained in:
Geza Lore 2025-03-14 14:06:51 +00:00 committed by GitHub
parent aca3b1636a
commit 59cb53cfbc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 135 additions and 11 deletions

View File

@ -184,8 +184,11 @@ public:
// Word at given 'wordIndex'
uint64_t word(size_t wordIndex) const { return m_flags[wordIndex]; }
// Set specified flag to given value
void set(size_t index, bool value) {
// Set specified word to given value
void setWord(size_t wordIndex, uint64_t value) { m_flags[wordIndex] = value; }
// Set specified bit to given value
void setBit(size_t index, bool value) {
uint64_t& w = m_flags[index / 64];
const size_t bitIndex = index % 64;
w &= ~(1ULL << bitIndex);

View File

@ -3018,7 +3018,8 @@ void AstCMethodHard::setPurity() {
{"resume", false},
{"reverse", false},
{"rsort", false},
{"set", false},
{"setBit", false},
{"setWord", false},
{"set_randmode", false},
{"shuffle", false},
{"size", true},

View File

@ -501,7 +501,7 @@ struct TriggerKit final {
void addFirstIterationTriggerAssignment(AstVarScope* flagp, uint32_t index) const {
FileLine* const flp = flagp->fileline();
AstVarRef* const vrefp = new AstVarRef{flp, m_vscp, VAccess::WRITE};
AstCMethodHard* const callp = new AstCMethodHard{flp, vrefp, "set"};
AstCMethodHard* const callp = new AstCMethodHard{flp, vrefp, "setBit"};
callp->addPinsp(new AstConst{flp, index});
callp->addPinsp(new AstVarRef{flp, flagp, VAccess::READ});
callp->dtypeSetVoid();
@ -512,7 +512,7 @@ struct TriggerKit final {
void addExtraTriggerAssignment(AstVarScope* extraTriggerVscp, uint32_t index) const {
FileLine* const flp = extraTriggerVscp->fileline();
AstVarRef* const vrefp = new AstVarRef{flp, m_vscp, VAccess::WRITE};
AstCMethodHard* const callp = new AstCMethodHard{flp, vrefp, "set"};
AstCMethodHard* const callp = new AstCMethodHard{flp, vrefp, "setBit"};
callp->addPinsp(new AstConst{flp, index});
callp->addPinsp(new AstVarRef{flp, extraTriggerVscp, VAccess::READ});
callp->dtypeSetVoid();
@ -651,9 +651,9 @@ const TriggerKit createTriggers(AstNetlist* netlistp, AstCFunc* const initFuncp,
}
// Set the given trigger to the given value
const auto setTrig = [&](uint32_t index, AstNodeExpr* valp) {
const auto setTrigBit = [&](uint32_t index, AstNodeExpr* valp) {
AstVarRef* const vrefp = new AstVarRef{flp, vscp, VAccess::WRITE};
AstCMethodHard* const callp = new AstCMethodHard{flp, vrefp, "set"};
AstCMethodHard* const callp = new AstCMethodHard{flp, vrefp, "setBit"};
callp->addPinsp(new AstConst{flp, index});
callp->addPinsp(valp);
callp->dtypeSetVoid();
@ -694,9 +694,14 @@ const TriggerKit createTriggers(AstNetlist* netlistp, AstCFunc* const initFuncp,
// Add trigger computation
uint32_t triggerNumber = extraTriggers.size();
uint32_t triggerBitIdx = triggerNumber;
AstNodeStmt* initialTrigsp = nullptr;
std::vector<uint32_t> senItemIndex2TriggerIndex;
senItemIndex2TriggerIndex.reserve(senItemps.size());
constexpr uint32_t TRIG_VEC_WORD_SIZE_LOG2 = 6; // 64-bits
constexpr uint32_t TRIG_VEC_WORD_SIZE = 1 << TRIG_VEC_WORD_SIZE_LOG2;
std::vector<AstNodeExpr*> trigExprps;
trigExprps.reserve(TRIG_VEC_WORD_SIZE);
for (const AstSenItem* const senItemp : senItemps) {
UASSERT_OBJ(senItemp->isClocked() || senItemp->isHybrid(), senItemp,
"Cannot create trigger expression for non-clocked sensitivity");
@ -706,12 +711,12 @@ const TriggerKit createTriggers(AstNetlist* netlistp, AstCFunc* const initFuncp,
// Add the trigger computation
const auto& pair = senExprBuilder.build(senItemp);
funcp->addStmtsp(setTrig(triggerNumber, pair.first));
trigExprps.emplace_back(pair.first);
// Add initialization time trigger
if (pair.second || v3Global.opt.xInitialEdge()) {
initialTrigsp
= AstNode::addNext(initialTrigsp, setTrig(triggerNumber, new AstConst{flp, 1}));
= AstNode::addNext(initialTrigsp, setTrigBit(triggerNumber, new AstConst{flp, 1}));
}
// Add a debug statement for this trigger
@ -723,7 +728,47 @@ const TriggerKit createTriggers(AstNetlist* netlistp, AstCFunc* const initFuncp,
//
++triggerNumber;
// Add statements on every word boundary
if (triggerNumber % TRIG_VEC_WORD_SIZE == 0) {
if (triggerBitIdx % TRIG_VEC_WORD_SIZE != 0) {
// Set leading triggers bit-wise
for (AstNodeExpr* const exprp : trigExprps) {
funcp->addStmtsp(setTrigBit(triggerBitIdx++, exprp));
}
} else {
// Set whole word as a unit
UASSERT_OBJ(triggerNumber == triggerBitIdx + TRIG_VEC_WORD_SIZE, senItemp,
"Mismatched index");
UASSERT_OBJ(trigExprps.size() == TRIG_VEC_WORD_SIZE, senItemp,
"There should be TRIG_VEC_WORD_SIZE expressions");
// Concatenate all bits in a tree
for (uint32_t level = 0; level < TRIG_VEC_WORD_SIZE_LOG2; ++level) {
const uint32_t stride = 1 << level;
for (uint32_t i = 0; i < TRIG_VEC_WORD_SIZE; i += 2 * stride) {
trigExprps[i] = new AstConcat{trigExprps[i]->fileline(),
trigExprps[i + stride], trigExprps[i]};
trigExprps[i + stride] = nullptr;
}
}
// Set the whole word in the trigger vector
AstVarRef* const vrefp = new AstVarRef{flp, vscp, VAccess::WRITE};
AstCMethodHard* const callp = new AstCMethodHard{flp, vrefp, "setWord"};
callp->addPinsp(new AstConst{flp, triggerBitIdx / TRIG_VEC_WORD_SIZE});
callp->addPinsp(trigExprps[0]);
callp->dtypeSetVoid();
funcp->addStmtsp(callp->makeStmt());
triggerBitIdx += TRIG_VEC_WORD_SIZE;
}
UASSERT_OBJ(triggerNumber == triggerBitIdx, senItemp, "Mismatched index");
trigExprps.clear();
}
}
// Set trailing triggers bit-wise
for (AstNodeExpr* const exprp : trigExprps) {
funcp->addStmtsp(setTrigBit(triggerBitIdx++, exprp));
}
trigExprps.clear();
// Construct the map from old SenTrees to new SenTrees
for (const AstSenTree* const senTreep : senTreeps) {

View File

@ -993,7 +993,7 @@
"stmtsp": [
{"type":"STMTEXPR","name":"","addr":"(CP)","loc":"d,11:8,11:9",
"exprp": [
{"type":"CMETHODHARD","name":"set","addr":"(DP)","loc":"d,11:8,11:9","dtypep":"(CB)",
{"type":"CMETHODHARD","name":"setBit","addr":"(DP)","loc":"d,11:8,11:9","dtypep":"(CB)",
"fromp": [
{"type":"VARREF","name":"__VactTriggered","addr":"(EP)","loc":"d,11:8,11:9","dtypep":"(NB)","access":"WR","varp":"(U)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"}
],

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.sim_time = 100000
test.scenarios('simulator')
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,55 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
module t(/*AUTOARG*/
// Inputs
clk
);
input clk;
localparam int ITERATIONS = 5;
localparam int N = 227;
logic [N-1:0] gclk = {N{1'b0}};
// Not actually used, but creates an extra internal trigger
export "DPI-C" function toggle;
function void toggle();
gclk = ~gclk;
endfunction
int cyc = 0;
always @(posedge clk) begin
if (~|gclk) begin
gclk[0] = 1'b1;
end else begin
gclk = {gclk[N-2:0], gclk[N-1]};
end
cyc <= cyc + 32'd1;
if (cyc == ITERATIONS*N - 1) begin
$display("cyc");
$write("*-* All Finished *-*\n");
$finish;
end
end
for (genvar n = 0; n < N; n++) begin : gen
int cnt = 0;
always @(posedge gclk[n]) cnt <= cnt + 1;
wire int cnt_plus_one = cnt + 1;
final begin
`checkh(cnt_plus_one, ITERATIONS + 1);
end
end
endmodule

View File

@ -601,7 +601,7 @@
<cfunc loc="a,0,0,0,0" name="_eval_settle"/>
<cfunc loc="a,0,0,0,0" name="_eval_triggers__act">
<stmtexpr loc="d,11,8,11,9">
<cmethodhard loc="d,11,8,11,9" name="set" dtype_id="7">
<cmethodhard loc="d,11,8,11,9" name="setBit" dtype_id="7">
<varref loc="d,11,8,11,9" name="__VactTriggered" dtype_id="9"/>
<const loc="d,11,8,11,9" name="32&apos;h0" dtype_id="14"/>
<and loc="d,61,14,61,21" dtype_id="9">