This commit is contained in:
Bartłomiej Chmiel 2025-12-23 19:42:58 -05:00 committed by GitHub
commit 82a29c33fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 173 additions and 0 deletions

View File

@ -197,6 +197,7 @@ class GateBuildVisitor final : public VNVisitorConst {
AstActive* m_activep = nullptr; // Current active
bool m_inClockedActive = false; // Underneath clocked active
bool m_inSenItem = false; // Underneath AstSenItem; any varrefs are clocks
bool m_inTraceDecl = false; // Underneath AstTraceDecl
// METHODS
void checkNode(AstNode* nodep) {
@ -269,6 +270,11 @@ class GateBuildVisitor final : public VNVisitorConst {
iterateLogic(nodep, false, nullptr, "senItem");
}
}
void visit(AstTraceDecl* nodep) override {
VL_RESTORER(m_inTraceDecl);
m_inTraceDecl = true;
iterateChildrenConst(nodep);
}
void visit(AstNodeVarRef* nodep) override {
if (!m_logicVertexp) return;
@ -287,6 +293,9 @@ class GateBuildVisitor final : public VNVisitorConst {
}
}
// To reduce code size, clear reducible for non-primaryIO signals in trace
if (m_inTraceDecl && !vscp->varp()->isPrimaryIO()) vVtxp->clearReducible("TraceDecl");
// We use weight of one; if we ref the var more than once, when we simplify,
// the weight will increase
if (nodep->access().isWriteOrRW()) m_graphp->addEdge(m_logicVertexp, vVtxp, 1);

View File

@ -71,6 +71,12 @@ class InstVisitor final : public VNVisitor {
if (nodep->modVarp()->isInout()) {
nodep->v3fatalSrc("Unsupported: Verilator is a 2-state simulator");
} else if (nodep->modVarp()->isWritable()) {
// If the destination is a simple VarRef to a primaryIO signal,
// mark the source port as primaryIO too (for trace optimization)
if (const AstNodeVarRef* const dstRefp = VN_CAST(exprp, NodeVarRef)) {
if (dstRefp->varp()->isPrimaryIO()) { nodep->modVarp()->primaryIO(true); }
}
AstNodeExpr* const rhsp = new AstVarXRef{exprp->fileline(), nodep->modVarp(),
m_cellp->name(), VAccess::READ};
AstAssignW* const assp = new AstAssignW{exprp->fileline(), exprp, rhsp};
@ -78,6 +84,13 @@ class InstVisitor final : public VNVisitor {
} else if (nodep->modVarp()->isNonOutput()) {
// Don't bother moving constants now,
// we'll be pushing the const down to the cell soon enough.
// If the source is a simple VarRef to a primaryIO signal,
// mark the target port as primaryIO too (for trace optimization)
if (const AstNodeVarRef* const srcRefp = VN_CAST(exprp, NodeVarRef)) {
if (srcRefp->varp()->isPrimaryIO()) { nodep->modVarp()->primaryIO(true); }
}
AstAssignW* const assp
= new AstAssignW{exprp->fileline(),
new AstVarXRef{exprp->fileline(), nodep->modVarp(),

View File

@ -0,0 +1,97 @@
#!/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
import re
import os
test.scenarios('simulator')
test.compile(verilator_flags2=['--trace'])
test.execute()
# Test that expressions are NOT inlined into trace update functions to avoid code repetition
trace_full_file = test.obj_dir + "/" + test.vm_prefix + "__Trace__0__Slow.cpp"
trace_chg_file = test.obj_dir + "/" + test.vm_prefix + "__Trace__0.cpp"
if test.vlt_all:
if os.path.exists(trace_full_file):
with open(trace_full_file, 'r', encoding="utf-8") as f:
trace_content = f.read()
# Search for inlined expressions, e.g.: bufp->fullBit(oldp+X,((1U & (~ (IData)(vlSelfRef.clk)))));
inlined_not = re.findall(r'fullBit\([^)]*\(\(1U & \(\~', trace_content)
inlined_arithmetic = re.findall(r'fullCData\([^)]*\(\(.*\+.*\)', trace_content)
complex_inlined = re.findall(r'full(?:CData|SData)\([^)]*\([^)]*\+[^)]*\)\s*\*',
trace_content)
deeply_nested_inlined = re.findall(r'full(?:IData|QData)\([^)]*\{[^}]*\{[^}]*\}',
trace_content)
if len(inlined_not) > 0:
test.error(f"Found {len(inlined_not)} inlined NOT expressions in trace full functions")
if len(inlined_arithmetic) > 0:
test.error(
f"Found {len(inlined_arithmetic)} inlined arithmetic expressions in trace full functions"
)
if len(complex_inlined) > 0:
test.error(
f"Found {len(complex_inlined)} complex inlined multiplication expressions in trace full functions"
)
if len(deeply_nested_inlined) > 0:
test.error(
f"Found {len(deeply_nested_inlined)} deeply nested inlined expressions in trace full functions"
)
# Search for variable references
var_refs_simple = re.findall(
r'fullBit\(oldp\+\d+,\(vlSelfRef\.t__DOT__.*__DOT__simple_not\)\)', trace_content)
var_refs_add = re.findall(
r'fullCData\(oldp\+\d+,\(vlSelfRef\.t__DOT__.*__DOT__add_result\)', trace_content)
var_refs_mul = re.findall(
r'fullCData\(oldp\+\d+,\(vlSelfRef\.t__DOT__.*__DOT__mul_result\)', trace_content)
if len(var_refs_simple) == 0:
test.error(
"No variable references found for simple_not signal in trace code - expressions are probably inlined instead"
)
if len(var_refs_add) == 0:
test.error(
"No variable references found for add_result signal in trace code - expressions are probably inlined instead"
)
if len(var_refs_mul) == 0:
test.error(
"No variable references found for mul_result signal in trace code - expressions are probably inlined instead"
)
# Check `chg` functions
if os.path.exists(trace_chg_file):
with open(trace_chg_file, 'r', encoding='utf-8') as f:
trace_chg_content = f.read()
# Search for inlined expressions, e.g.: chgBit(oldp+X,((1U & (~ ...))))
chg_inlined_not = re.findall(r'chgBit\([^)]*\(\(1U & \(\~', trace_chg_content)
chg_inlined_arith = re.findall(r'chgCData\([^)]*\(\(.*\+.*\)', trace_chg_content)
if len(chg_inlined_not) > 0:
test.error(
f"Found {len(chg_inlined_not)} inlined NOT expressions in trace chg functions")
if len(chg_inlined_arith) > 0:
test.error(
f"Found {len(chg_inlined_arith)} inlined arithmetic expressions in trace chg functions"
)
test.passes()

View File

@ -0,0 +1,54 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Antmicro.
// SPDX-License-Identifier: CC0-1.0
parameter INSTANCES = 20;
module t (clk);
input clk;
generate
for (genvar i = 0; i < INSTANCES; ++i) sub sub (.*);
endgenerate
always @(posedge clk) begin
$write("*-* All Finished *-*\n");
$finish;
end
endmodule
module sub(input clk);
logic simple_not;
assign simple_not = ~clk;
logic [7:0] counter;
logic [7:0] add_result;
logic [7:0] mul_result;
assign add_result = counter + 8'd5;
assign mul_result = add_result * 8'd3;
logic [15:0] complex_a, complex_b;
logic [31:0] complex_result;
assign complex_result = {16'd0, {add_result, mul_result}} ^
({{16'd0}, {complex_a[7:0], complex_a[15:8]}} +
{{16'd0}, {complex_b[7:0], complex_b[15:8]}});
logic [15:0] conditional_result;
assign conditional_result = (counter > 8'd100) ?
({add_result, mul_result} & 16'hFF00) :
({mul_result, add_result} | 16'h00FF);
// Multi-level logic
logic stage1, stage2, stage3;
assign stage1 = (counter[0] ^ counter[1]) & clk;
assign stage2 = (stage1 | counter[2]) ^ counter[3];
assign stage3 = stage2 & (counter[4] | ~counter[5]);
always @(posedge clk) begin
counter <= counter + 1;
complex_a <= complex_a + 16'd7;
complex_b <= complex_b + 16'd13;
end
endmodule