Support randsequence production function ports (#7522)

This commit is contained in:
Yilou Wang 2026-05-04 17:28:17 +02:00 committed by GitHub
parent 4e349971d3
commit fc49811fd7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 164 additions and 28 deletions

View File

@ -2164,9 +2164,12 @@ class LinkDotFindVisitor final : public VNVisitor {
if (nodep->fvarp())
nodep->fvarp()->v3warn(E_UNSUPPORTED,
"Unsupported: randsequence production function variable");
if (nodep->portsp())
nodep->portsp()->v3warn(E_UNSUPPORTED,
"Unsupported: randsequence production function ports");
// Mark formal ports as port-checked so the primary resolve pass does not
// flag them with "does not appear in port list" -- V3RandSequence will
// later move them onto the generated task as real input ports.
for (AstNode* itp = nodep->portsp(); itp; itp = itp->nextp()) {
VN_AS(itp, Var)->user4(true);
}
iterateChildren(nodep);
}

View File

@ -56,6 +56,7 @@ class LinkParseVisitor final : public VNVisitor {
bool m_inInterface = false; // True when inside interface declaration
AstNodeProcedure* m_procedurep = nullptr; // Current procedure
AstNodeFTask* m_ftaskp = nullptr; // Current task
AstRSProd* m_rsProdp = nullptr; // Current randsequence production
AstNodeBlock* m_blockp = nullptr; // Current AstNodeBlock
AstNodeStmt* m_blockAddAutomaticStmtp = nullptr; // Initial statements to add to block
AstNodeStmt* m_blockAddStaticStmtp = nullptr; // Initial statements to add to block
@ -498,8 +499,10 @@ class LinkParseVisitor final : public VNVisitor {
// Earlier moved any valuep() under the duplicate to the IO declaration
UINFO(9, "VarInit case0 " << nodep);
} else if (nodep->isParam() || nodep->isGenVar()
|| (m_ftaskp && (nodep->isNonOutput() || nodep->isFuncReturn()))) {
// 1. Parameters and function inputs: It's a default to use if not overridden
|| (m_ftaskp && (nodep->isNonOutput() || nodep->isFuncReturn()))
|| (m_rsProdp && nodep->isNonOutput())) {
// 1. Parameters, function inputs, and randsequence production formal
// ports (IEEE 1800-2023 18.17.7): default to use if not overridden
UINFO(9, "VarInit case1 " << nodep);
} else if (!m_ftaskp && !VN_IS(m_modp, Class) && nodep->isNonOutput()
&& !nodep->isInput()) {
@ -889,6 +892,11 @@ class LinkParseVisitor final : public VNVisitor {
}
iterateChildren(nodep);
}
void visit(AstRSProd* nodep) override {
VL_RESTORER(m_rsProdp);
m_rsProdp = nodep;
iterateChildren(nodep);
}
void visit(AstNodeBlock* nodep) override {
VL_RESTORER(m_blockAddAutomaticStmtp);
m_blockAddAutomaticStmtp = nullptr;

View File

@ -141,26 +141,32 @@ class RandSequenceVisitor final : public VNVisitor {
taskp->addStmtsp(breakVarp);
// Call the start production's task
taskp->addStmtsp(newProdFuncRef(nodep, m_startProdp, breakVarp));
taskp->addStmtsp(newProdFuncRef(nodep, m_startProdp, breakVarp, nullptr));
UINFOTREE(9, taskp, "newStart", "");
return taskp;
}
AstNode* newProdFuncRef(AstNode* nodep, AstRSProd* prodp, AstVar* breakVarp) {
AstNode* newProdFuncRef(AstNode* nodep, AstRSProd* prodp, AstVar* breakVarp,
AstArg* userArgsp) {
auto it = m_prodFuncps.find(prodp);
UASSERT_OBJ(it != m_prodFuncps.end(), nodep, "No production function made");
AstNodeFTask* const prodFuncp = it->second;
FileLine* const fl = nodep->fileline();
AstArg* const argsp
= new AstArg{fl, breakVarp->name(), new AstVarRef{fl, breakVarp, VAccess::WRITE}};
// V3Width already ran before V3RandSequence, so VarRefs we create here
// need dtype set explicitly, V3Broken later checks width == widthMin.
AstVarRef* const breakRefp = new AstVarRef{fl, breakVarp, VAccess::WRITE};
breakRefp->dtypeFrom(breakVarp);
AstArg* const argsp = new AstArg{fl, breakVarp->name(), breakRefp};
for (const auto& itr : m_localizeNames) {
const AstVar* const lvarp = itr.second;
AstVar* const iovarp = m_localizeRemaps[lvarp];
UASSERT_OBJ(iovarp, nodep, "No new port variable for local variable" << lvarp);
argsp->addNext(new AstArg{nodep->fileline(), "__Vrsarg_" + lvarp->name(),
new AstVarRef{fl, iovarp, VAccess::READWRITE}});
AstVarRef* const refp = new AstVarRef{fl, iovarp, VAccess::READWRITE};
refp->dtypeFrom(iovarp);
argsp->addNext(new AstArg{nodep->fileline(), "__Vrsarg_" + lvarp->name(), refp});
}
if (userArgsp) argsp->addNext(userArgsp);
AstNode* const newp
= new AstStmtExpr{fl, new AstTaskRef{fl, VN_AS(prodFuncp, Task), argsp}};
return newp;
@ -480,6 +486,19 @@ class RandSequenceVisitor final : public VNVisitor {
m_breakVarp = newBreakVar(nodep->fileline(), true);
m_prodFuncp->addStmtsp(m_breakVarp);
// Production formal ports become real input ports on the generated task.
// Must be added after m_breakVarp / localize ports so positional argument
// order in newProdFuncRef stays consistent: [break, localize..., user...].
if (AstNode* const portsp = nodep->portsp()) {
portsp->unlinkFrBackWithNext();
m_prodFuncp->addStmtsp(portsp);
for (AstNode* itp = portsp; itp; itp = itp->nextp()) {
AstVar* const portVarp = VN_AS(itp, Var);
portVarp->funcLocal(true);
portVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
}
}
// Put JumpBlock immediately under the new function to support
// a future break/return. V3Const will rip it out if unneeded.
VL_RESTORER(m_jumpBlockp);
@ -489,9 +508,6 @@ class RandSequenceVisitor final : public VNVisitor {
if (nodep->fvarp())
nodep->fvarp()->v3warn(E_UNSUPPORTED,
"Unsupported: randsequence production function variable");
if (nodep->portsp())
nodep->portsp()->v3warn(E_UNSUPPORTED,
"Unsupported: randsequence production function ports");
// Move children into m_prodFuncp, and iterate there
if (!nodep->rulesp()) { // Nothing to do
@ -579,8 +595,11 @@ class RandSequenceVisitor final : public VNVisitor {
UASSERT_OBJ(m_prodFuncp, nodep, "RSProdItem not under production");
AstRSProd* const foundp = nodep->prodp();
UASSERT_OBJ(foundp, nodep, "Unlinked production reference");
// Pass through caller-side argument list (IEEE 1800-2023 18.17.7).
AstArg* const userArgsp
= nodep->argsp() ? nodep->argsp()->unlinkFrBackWithNext() : nullptr;
// Convert to task call
AstNode* const newp = newProdFuncRef(nodep, foundp, m_breakVarp);
AstNode* const newp = newProdFuncRef(nodep, foundp, m_breakVarp, userArgsp);
// The production might have done a "break;", skip other steps if so
newp->addNext(new AstIf{nodep->fileline(),
new AstVarRef{nodep->fileline(), m_breakVarp, VAccess::READ},

View File

@ -1,9 +0,0 @@
%Error-UNSUPPORTED: t/t_randsequence_func.v:33:23: Unsupported: randsequence production function ports
33 | void func(int n) : { counts[1] += n; };
| ^
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: t/t_randsequence_func.v:33:23: Input/output/inout does not appear in port list: 'n'
33 | void func(int n) : { counts[1] += n; };
| ^
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -11,9 +11,8 @@ import vltest_bootstrap
test.scenarios('simulator')
test.compile(fails=test.vlt_all, expect_filename=test.golden_filename)
test.compile()
if not test.vlt_all:
test.execute()
test.execute()
test.passes()

View File

@ -16,13 +16,14 @@ module t;
int seq;
int counts[8];
int sum, hits;
task prep();
for (int i = 0; i < COUNT; ++i) counts[i] = 0;
endtask
initial begin
// functions
// Single-port and no-port productions
prep();
for (int i = 0; i < COUNT; ++i) begin
randsequence(main)
@ -37,6 +38,42 @@ module t;
`checkd(counts[1], COUNT * (10 + 20));
`checkd(counts[2], COUNT * 1 / 1); // return
// Multi-port, repeat-with-arg, if-prod-with-arg, nested-prod-with-arg
sum = 0;
hits = 0;
for (int i = 0; i < COUNT; ++i) begin
// verilog_format: off
randsequence(main)
main : multi nested ifcall reps;
multi : add2(3, 4);
nested : leaf(7);
ifcall : if (1) add2(1, 2) else add2(0, 0);
reps : repeat (3) add2(2, 0);
void add2(int a, int b) : { sum = sum + a + b; hits = hits + 1; };
void leaf(int v) : { sum = sum + v; hits = hits + 1; };
endsequence
// verilog_format: on
end
`checkd(sum, COUNT * (7 + 7 + 3 + 3 * 2));
`checkd(hits, COUNT * (1 + 1 + 1 + 3));
// Default port values (IEEE 1800-2023 18.17.7)
sum = 0;
hits = 0;
for (int i = 0; i < COUNT; ++i) begin
// verilog_format: off
randsequence(main)
main : useDefault override1 override2;
useDefault : add_def;
override1 : add_def(50);
override2 : add_def(100);
void add_def(int n = 7) : { sum = sum + n; hits = hits + 1; };
endsequence
// verilog_format: on
end
`checkd(sum, COUNT * (7 + 50 + 100));
`checkd(hits, COUNT * 3);
$write("*-* All Finished *-*\n");
$finish;
end

View File

@ -0,0 +1,23 @@
%Error: t/t_randsequence_func_bad.v:13:21: Too many arguments in call to task 't.__Vrs0_add'
13 | main : add(1, 2);
| ^~~
: ... Location of task 't.__Vrs0_add' declaration:
14 | void add(int y) : { $display(y); };
| ^~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: t/t_randsequence_func_bad.v:19:14: Missing argument on non-defaulted argument 'y' in function call to TASK 't.__Vrs1_add'
19 | main : add();
| ^~~
%Error: t/t_randsequence_func_bad.v:25:19: No such argument 'bogus' in call to task 't.__Vrs2_add'
25 | main : add(.bogus(1));
| ^~~
: ... Location of task 't.__Vrs2_add' declaration
26 | void add(int y) : { $display(y); };
| ^~~
%Error: t/t_randsequence_func_bad.v:25:14: Missing argument on non-defaulted argument 'y' in function call to TASK 't.__Vrs2_add'
25 | main : add(.bogus(1));
| ^~~
%Error: t/t_randsequence_func_bad.v:31:26: Duplicate argument 'y' in function call to TASK 't.__Vrs3_add'
31 | main : add(.y(1), .y(2));
| ^
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/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('linter')
test.lint(verilator_flags=["--lint-only"], fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,40 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 PlanV GmbH
// SPDX-License-Identifier: CC0-1.0
module t;
// verilog_format: off
initial begin
// Too many actuals.
randsequence(main)
main : add(1, 2);
void add(int y) : { $display(y); };
endsequence
// Too few actuals (non-defaulted formal).
randsequence(main)
main : add();
void add(int y) : { $display(y); };
endsequence
// Named argument with non-existent name.
randsequence(main)
main : add(.bogus(1));
void add(int y) : { $display(y); };
endsequence
// Duplicate named argument.
randsequence(main)
main : add(.y(1), .y(2));
void add(int y) : { $display(y); };
endsequence
$write("*-* All Finished *-*\n");
$finish;
end
// verilog_format: on
endmodule