Support randsequence production function ports (#7522)
This commit is contained in:
parent
4e349971d3
commit
fc49811fd7
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue