diff --git a/docs/guide/extensions.rst b/docs/guide/extensions.rst index 7f4948c71..21df64361 100644 --- a/docs/guide/extensions.rst +++ b/docs/guide/extensions.rst @@ -156,7 +156,7 @@ or "`ifdef`"'s may break other tools. .. t_dist_docs_style restart_sort -.. option:: $c([string], ...); +.. option:: $c([string], ...);, $cpure([string], ...); The string will be embedded directly in the output C++ code at the point where the surrounding Verilog code is compiled. It may either be a @@ -196,6 +196,11 @@ or "`ifdef`"'s may break other tools. compatibility with other simulators, which require a differently named PLI function name for each different output width. + `$cpure` is similar to `$c` except that it indicates the + expression is pure, versus `$c` which is assumed impure. + `$cpure` is for internal use only, and it might change + without notice in any future version or Verilator. + .. option:: $display, $write, $fdisplay, $fwrite, $sformat, $swrite Format arguments may use C fprintf sizes after the % escape. Per the diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 9ff15e003..066e2be69 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -603,23 +603,30 @@ public: void add(AstNode* nodep) { addNodesp(nodep); } }; class AstCExprUser final : public AstNodeExpr { - // User '$c' statement - Like AstCStmt, with text tracking and optimizations disabled. + // User '$c' or '$cpure' expression - Like AstCStmt, with text tracking and optimizations + // disabled. // // Use AstCExpr instead, unless the text is from user input. // // @astgen op1 := nodesp : List[AstNode] + const bool m_pure; // Whether the function is pure public: + class Pure {}; AstCExprUser(FileLine* fl) - : ASTGEN_SUPER_CExprUser(fl) {} + : ASTGEN_SUPER_CExprUser(fl) + , m_pure{false} {} + AstCExprUser(FileLine* fl, Pure) + : ASTGEN_SUPER_CExprUser(fl) + , m_pure{true} {} ASTGEN_MEMBERS_AstCExprUser; // METHODS bool cleanOut() const override { return false; } std::string emitC() override { V3ERROR_NA_RETURN(""); } std::string emitVerilog() override { V3ERROR_NA_RETURN(""); } - bool isGateOptimizable() const override { return false; } + bool isGateOptimizable() const override { return m_pure; } bool isOutputter() override { return true; } - bool isPredictOptimizable() const override { return false; } - bool isPure() override { return false; } + bool isPredictOptimizable() const override { return m_pure; } + bool isPure() override { return m_pure; } bool sameNode(const AstNode* /*samep*/) const override { return true; } // Add some text, or a node to this expression void add(const std::string& text) { addNodesp(new AstText{fileline(), text}); } diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 5dde189a9..b04556c38 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -1376,7 +1376,9 @@ public: putnbs(nodep, ""); ofp()->putsNoTracking("\n"); if (/* is always from $c */ v3Global.opt.decoration() && !v3Global.opt.protectIds()) { - ofp()->putsNoTracking("// $c expression at " + nodep->fileline()->ascii() + "\n"); + ofp()->putsNoTracking( + (nodep->isPure() ? "// $cpure expression at " : "// $c expression at ") + + nodep->fileline()->ascii() + "\n"); } emitNodesWithText(nodep->nodesp(), m_useSelfForThis, false, ""); puts("\n"); diff --git a/src/verilog.l b/src/verilog.l index 35e98feb0..49651ec30 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -174,7 +174,8 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} {ws} { FL_FWD; FL_BRK; } /* otherwise ignore white-space */ {crnl} { FL_FWD; FL_BRK; } /* Count line numbers */ /* Extensions to Verilog set, some specified by PSL */ - "$c"[0-9]* { FL; return yD_C; } /*Verilator only*/ + "$c"[0-9]* { FL; return yD_C; } /*Verilator only*/ + "$cpure"[0-9]* { FL; return yD_CPURE; } /*Verilator only*/ /* System Tasks */ "$acos" { FL; return yD_ACOS; } "$acosh" { FL; return yD_ACOSH; } diff --git a/src/verilog.y b/src/verilog.y index 734dea2ce..8ab960153 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -611,6 +611,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yD_BITSTOREAL "$bitstoreal" %token yD_BITSTOSHORTREAL "$bitstoshortreal" %token yD_C "$c" +%token yD_CPURE "$cpure" %token yD_CAST "$cast" %token yD_CEIL "$ceil" %token yD_CHANGED "$changed" @@ -4101,7 +4102,7 @@ system_t_call: // IEEE: system_tf_call (as task) } $$ = cstmtp; } -| yD_SDF_ANNOTATE '(' exprEListE ')' { $$ = nullptr; $1->v3warn(SPECIFYIGN, "Ignoring unsupported: $sdf_annotate"); DEL($3); } + | yD_SDF_ANNOTATE '(' exprEListE ')' { $$ = nullptr; $1->v3warn(SPECIFYIGN, "Ignoring unsupported: $sdf_annotate"); DEL($3); } | yD_STACKTRACE parenE { $$ = new AstStackTraceT{$1}; } | yD_SYSTEM '(' expr ')' { $$ = new AstSystemT{$1, $3}; } // @@ -4268,6 +4269,14 @@ system_f_call: // IEEE: system_tf_call (as func) } $$ = cexprp; } + | yD_CPURE '(' cStrList ')' { + AstCExprUser* cexprp = nullptr; + if (!v3Global.opt.ignc()) { + cexprp = new AstCExprUser{$1, AstCExprUser::Pure{}}; + cexprp->add($3); + } + $$ = cexprp; + } | yD_CAST '(' expr ',' expr ')' { $$ = new AstCastDynamic{$1, $5, $3}; } | yD_STACKTRACE parenE { $$ = new AstStackTraceF{$1}; } | yD_SYSTEM '(' expr ')' { $$ = new AstSystemF{$1, $3}; } diff --git a/test_regress/t/t_cpure.py b/test_regress/t/t_cpure.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_cpure.py @@ -0,0 +1,18 @@ +#!/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 + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_cpure.v b/test_regress/t/t_cpure.v new file mode 100644 index 000000000..1fab00cbc --- /dev/null +++ b/test_regress/t/t_cpure.v @@ -0,0 +1,20 @@ +// DESCRIPTION: Verilator: Verilog Test module for SystemVerilog 'alias' +// +// Simple bi-directional transitive alias test. +// +// 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 + +function int func(); + static int someVar = 12; + return $cpure(someVar, "+ 6"); +endfunction + +module t; + initial begin + if (func() != 18) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule