From a668b7c6586b436d7af367ff589cd78857bb8037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Chmiel?= Date: Mon, 2 Dec 2024 11:43:26 +0100 Subject: [PATCH] Fix missing VlProcess handle in coroutines with splits (#5623) (#5650) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bartłomiej Chmiel --- src/V3Sched.cpp | 1 + src/V3SchedTiming.cpp | 10 +-- test_regress/t/t_disable_fork2.v | 11 ++- test_regress/t/t_disable_fork2_split.py | 18 ++++ test_regress/t/t_vlprocess_missing.py | 106 ++++++++++++++++++++++++ 5 files changed, 139 insertions(+), 7 deletions(-) create mode 100755 test_regress/t/t_disable_fork2_split.py create mode 100755 test_regress/t/t_vlprocess_missing.py diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index 46fb79354..d85cf93b8 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -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; }; diff --git a/src/V3SchedTiming.cpp b/src/V3SchedTiming.cpp index 13e2f0b88..7e46ca8ec 100644 --- a/src/V3SchedTiming.cpp +++ b/src/V3SchedTiming.cpp @@ -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); } diff --git a/test_regress/t/t_disable_fork2.v b/test_regress/t/t_disable_fork2.v index 118e3e7f5..6e52acac8 100644 --- a/test_regress/t/t_disable_fork2.v +++ b/test_regress/t/t_disable_fork2.v @@ -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 diff --git a/test_regress/t/t_disable_fork2_split.py b/test_regress/t/t_disable_fork2_split.py new file mode 100755 index 000000000..3e66b3894 --- /dev/null +++ b/test_regress/t/t_disable_fork2_split.py @@ -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() diff --git a/test_regress/t/t_vlprocess_missing.py b/test_regress/t/t_vlprocess_missing.py new file mode 100755 index 000000000..35076ad18 --- /dev/null +++ b/test_regress/t/t_vlprocess_missing.py @@ -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()