Fix missing VlProcess handle in coroutines with splits (#5623) (#5650)

Signed-off-by: Bartłomiej Chmiel <bchmiel@antmicro.com>
This commit is contained in:
Bartłomiej Chmiel 2024-12-02 11:43:26 +01:00 committed by GitHub
parent 94fd17e4f7
commit a668b7c658
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 139 additions and 7 deletions

View File

@ -291,6 +291,7 @@ AstCFunc* splitCheckCreateNewSubFunc(AstCFunc* ofuncp) {
subFuncp->isLoose(true);
subFuncp->slow(ofuncp->slow());
subFuncp->declPrivate(ofuncp->declPrivate());
if (ofuncp->needProcess()) subFuncp->setNeedProcess();
return subFuncp;
};

View File

@ -368,7 +368,9 @@ void transformForks(AstNetlist* const netlistp) {
// Start with children, so later we only find awaits that are actually in this begin
m_beginHasAwaits = false;
iterateChildrenConst(nodep);
if (m_beginHasAwaits || nodep->needProcess()) {
if (!nodep->stmtsp()) {
nodep->unlinkFrBack();
} else if (m_beginHasAwaits || nodep->needProcess()) {
UASSERT_OBJ(!nodep->name().empty(), nodep, "Begin needs a name");
// Create a function to put this begin's statements in
FileLine* const flp = nodep->fileline();
@ -407,11 +409,7 @@ void transformForks(AstNetlist* const netlistp) {
} else {
// The begin has neither awaits nor a process::self call, just inline the
// statements
if (nodep->stmtsp()) {
nodep->replaceWith(nodep->stmtsp()->unlinkFrBackWithNext());
} else {
nodep->unlinkFrBack();
}
nodep->replaceWith(nodep->stmtsp()->unlinkFrBackWithNext());
}
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}

View File

@ -15,7 +15,8 @@
// - a function taking VlProcess argument shared between a process that
// allocates VlProcess, and one that doesnt,
// - a function that has a delay and obtains VlProcess argument,
// - a function that has a delay and doesn't obtain it.
// - a function that has a delay and doesn't obtain it,
// - an empty fork with disable fork.
//
// Blocks below contain info on whether they should (YES) or shouldn't (NO)
// be emitted as functions with a VlProcess argument.
@ -38,6 +39,13 @@ class Cls;
task delay_func; /*NO*/
fork /*NO*/ #1 $write("Finished *-*\n"); join_none
endtask
task empty_fork;
fork
begin
end
join_none
disable fork;
endtask
endclass
module t;
@ -47,6 +55,7 @@ module t;
fork /*YES*/ cls.common_func(); join_none
cls.fork_func();
cls.disable_fork_func();
cls.empty_fork();
cls.print();
end

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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.scenarios('simulator')
test.top_filename = "t_disable_fork2.v"
# Validate if splitted functions get vlProcess handle
test.compile(verilator_flags2=["--timing --output-split-cfuncs 1"])
test.passes()

View File

@ -0,0 +1,106 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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.scenarios('simulator')
test.top_filename = test.obj_dir + "/t_vlprocess_missing.v"
# Number of tests to generate
NUM_TESTS = 200
# Testbench header template
HEADER = """\
module Testbench;
logic clk;
logic reset;
// Clock driver
initial begin
clk = 0;
forever begin
#5 clk = ~clk;
end
end
task automatic advance_clock(int n = 1);
repeat (n) @(posedge clk);
endtask
"""
# Test task template
TEST_TASK_TEMPLATE = """
task automatic test_{num}();
int counter = 0;
int expected_value = {num};
// Timeout wait
fork
begin
advance_clock(10000);
$error("Timeout");
end
join_none
wait (counter == expected_value);
disable fork;
while (counter < expected_value) begin
advance_clock();
counter++;
end
endtask
"""
# Testbench footer template
FOOTER = " initial begin"
# Call template for invoking each test task
CALL_TEMPLATE = " test_{num}();\n"
# Footer end
FOOTER_END = """
$finish;
end
endmodule
"""
def gen(filename, num_tests):
"""
Generates a SystemVerilog testbench with the specified number of tests.
Args:
filename (str): The output file name for the generated testbench.
num_tests (int): The number of test tasks to generate.
"""
with open(filename, 'w', encoding="utf-8") as fh:
fh.write("// Generated by t_vlprocess_missing.py\n")
# Write the header
fh.write(HEADER)
# Generate the test tasks
for i in range(1, num_tests + 1):
fh.write(TEST_TASK_TEMPLATE.format(num=i))
# Write the initial block with test calls
fh.write(FOOTER)
for i in range(1, num_tests + 1):
fh.write(CALL_TEMPLATE.format(num=i))
fh.write(FOOTER_END)
gen(test.top_filename, NUM_TESTS)
test.compile(verilator_flags2=["--binary"])
test.passes()