From ba8700f99d312f59479c01e75b6468d38b0fd92d Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 28 Jan 2023 14:05:26 -0500 Subject: [PATCH] Support $changed_gclk, $fell_gclk, $rose_gclk, $stable_gclk --- src/V3AssertPre.cpp | 21 ++++++++++------ src/V3AstNodeExpr.h | 9 ++++--- src/V3Width.cpp | 3 +++ src/verilog.l | 4 +++ src/verilog.y | 38 +++++++++++++++++++++-------- test_regress/t/t_past_funcs.v | 17 +++++++++++++ test_regress/t/t_past_unsup_bad.out | 20 --------------- test_regress/t/t_past_unsup_bad.pl | 19 --------------- test_regress/t/t_past_unsup_bad.v | 20 --------------- 9 files changed, 72 insertions(+), 79 deletions(-) delete mode 100644 test_regress/t/t_past_unsup_bad.out delete mode 100755 test_regress/t/t_past_unsup_bad.pl delete mode 100644 test_regress/t/t_past_unsup_bad.v diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index b27a36c1b..8221ee0ab 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -63,9 +63,10 @@ private: // METHODS - AstSenTree* newSenTree(AstNode* nodep) { + AstSenTree* newSenTree(AstNode* nodep, AstSenTree* useTreep = nullptr) { // Create sentree based on clocked or default clock // Return nullptr for always + if (useTreep) return useTreep; AstSenTree* newp = nullptr; AstSenItem* senip = m_senip; if (!senip && m_defaultClockingp) senip = m_defaultClockingp->sensesp(); @@ -362,17 +363,19 @@ private: clearAssertInfo(); } void visit(AstFell* nodep) override { - if (nodep->sentreep()) return; // Already processed + if (nodep->user1SetOnce()) return; iterateChildren(nodep); FileLine* const fl = nodep->fileline(); AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack(); if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1}; + AstSenTree* sentreep = nodep->sentreep(); + if (sentreep) sentreep->unlinkFrBack(); AstNodeExpr* const past = new AstPast{fl, exprp, nullptr}; past->dtypeFrom(exprp); exprp = new AstAnd{fl, past, new AstNot{fl, exprp->cloneTree(false)}}; exprp->dtypeSetBit(); nodep->replaceWith(exprp); - nodep->sentreep(newSenTree(nodep)); + nodep->sentreep(newSenTree(nodep, sentreep)); VL_DO_DANGLING(pushDeletep(nodep), nodep); } void visit(AstPast* nodep) override { @@ -381,30 +384,34 @@ private: nodep->sentreep(newSenTree(nodep)); } void visit(AstRose* nodep) override { - if (nodep->sentreep()) return; // Already processed + if (nodep->user1SetOnce()) return; iterateChildren(nodep); FileLine* const fl = nodep->fileline(); AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack(); if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1}; + AstSenTree* sentreep = nodep->sentreep(); + if (sentreep) sentreep->unlinkFrBack(); AstNodeExpr* const past = new AstPast{fl, exprp, nullptr}; past->dtypeFrom(exprp); exprp = new AstAnd{fl, new AstNot{fl, past}, exprp->cloneTree(false)}; exprp->dtypeSetBit(); nodep->replaceWith(exprp); - nodep->sentreep(newSenTree(nodep)); + nodep->sentreep(newSenTree(nodep, sentreep)); VL_DO_DANGLING(pushDeletep(nodep), nodep); } void visit(AstStable* nodep) override { - if (nodep->sentreep()) return; // Already processed + if (nodep->user1SetOnce()) return; iterateChildren(nodep); FileLine* const fl = nodep->fileline(); AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack(); + AstSenTree* sentreep = nodep->sentreep(); + if (sentreep) sentreep->unlinkFrBack(); AstNodeExpr* const past = new AstPast{fl, exprp, nullptr}; past->dtypeFrom(exprp); exprp = new AstEq{fl, past, exprp->cloneTree(false)}; exprp->dtypeSetBit(); nodep->replaceWith(exprp); - nodep->sentreep(newSenTree(nodep)); + nodep->sentreep(newSenTree(nodep, sentreep)); VL_DO_DANGLING(pushDeletep(nodep), nodep); } diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 243029f39..4eedb1177 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -1183,9 +1183,10 @@ class AstFell final : public AstNodeExpr { // @astgen op1 := exprp : AstNodeExpr // @astgen op2 := sentreep : Optional[AstSenTree] public: - AstFell(FileLine* fl, AstNodeExpr* exprp) + AstFell(FileLine* fl, AstNodeExpr* exprp, AstSenTree* sentreep) : ASTGEN_SUPER_Fell(fl) { this->exprp(exprp); + this->sentreep(sentreep); } ASTGEN_MEMBERS_AstFell; string emitVerilog() override { return "$fell(%l)"; } @@ -1536,9 +1537,10 @@ class AstRose final : public AstNodeExpr { // @astgen op1 := exprp : AstNodeExpr // @astgen op2 := sentreep : Optional[AstSenTree] public: - AstRose(FileLine* fl, AstNodeExpr* exprp) + AstRose(FileLine* fl, AstNodeExpr* exprp, AstSenTree* sentreep) : ASTGEN_SUPER_Rose(fl) { this->exprp(exprp); + this->sentreep(sentreep); } ASTGEN_MEMBERS_AstRose; string emitVerilog() override { return "$rose(%l)"; } @@ -1754,9 +1756,10 @@ class AstStable final : public AstNodeExpr { // @astgen op1 := exprp : AstNodeExpr // @astgen op2 := sentreep : Optional[AstSenTree] public: - AstStable(FileLine* fl, AstNodeExpr* exprp) + AstStable(FileLine* fl, AstNodeExpr* exprp, AstSenTree* sentreep) : ASTGEN_SUPER_Stable(fl) { this->exprp(exprp); + this->sentreep(sentreep); } ASTGEN_MEMBERS_AstStable; string emitVerilog() override { return "$stable(%l)"; } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 52cec557d..52a7610f4 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1177,6 +1177,7 @@ private: void visit(AstFell* nodep) override { if (m_vup->prelim()) { iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); + userIterate(nodep->sentreep(), nullptr); nodep->dtypeSetBit(); } } @@ -1207,6 +1208,7 @@ private: void visit(AstRose* nodep) override { if (m_vup->prelim()) { iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); + userIterate(nodep->sentreep(), nullptr); nodep->dtypeSetBit(); } } @@ -1221,6 +1223,7 @@ private: void visit(AstStable* nodep) override { if (m_vup->prelim()) { iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); + userIterate(nodep->sentreep(), nullptr); nodep->dtypeSetBit(); } } diff --git a/src/verilog.l b/src/verilog.l index a1ccdce0b..af6e72311 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -460,12 +460,14 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} /* System Tasks */ "$bits" { FL; return yD_BITS; } "$changed" { FL; return yD_CHANGED; } + "$changed_gclk" { FL; return yD_CHANGED_GCLK; } "$countbits" { FL; return yD_COUNTBITS; } "$countones" { FL; return yD_COUNTONES; } "$dimensions" { FL; return yD_DIMENSIONS; } "$error" { FL; return yD_ERROR; } "$fatal" { FL; return yD_FATAL; } "$fell" { FL; return yD_FELL; } + "$fell_gclk" { FL; return yD_FELL_GCLK; } "$high" { FL; return yD_HIGH; } "$increment" { FL; return yD_INCREMENT; } "$info" { FL; return yD_INFO; } @@ -479,8 +481,10 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "$right" { FL; return yD_RIGHT; } "$root" { FL; return yD_ROOT; } "$rose" { FL; return yD_ROSE; } + "$rose_gclk" { FL; return yD_ROSE_GCLK; } "$size" { FL; return yD_SIZE; } "$stable" { FL; return yD_STABLE; } + "$stable_gclk" { FL; return yD_STABLE_GCLK; } "$unpacked_dimensions" { FL; return yD_UNPACKED_DIMENSIONS; } "$warning" { FL; return yD_WARNING; } /* SV2005 Keywords */ diff --git a/src/verilog.y b/src/verilog.y index 11fc699b7..f544e31cf 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -168,6 +168,15 @@ public: return new AstGatePin{rangep->fileline(), exprp, rangep->cloneTree(true)}; } } + AstSenTree* createClockSenTree(FileLine* fl, AstNodeExpr* exprp) { + return new AstSenTree{fl, new AstSenItem{fl, VEdgeType::ET_CHANGED, exprp}}; + } + AstNodeExpr* createGlobalClockParseRef(FileLine* fl) { + return new AstParseRef{fl, VParseRefExp::PX_TEXT, "__024global_clock", nullptr, nullptr}; + } + AstSenTree* createGlobalClockSenTree(FileLine* fl) { + return createClockSenTree(fl, createGlobalClockParseRef(fl)); + } AstNode* createTypedef(FileLine* fl, const string& name, AstNode* attrsp, AstNodeDType* basep, AstNodeRange* rangep) { AstNode* const nodep = new AstTypedef{fl, name, attrsp, VFlagChildDType{}, @@ -784,6 +793,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yD_CAST "$cast" %token yD_CEIL "$ceil" %token yD_CHANGED "$changed" +%token yD_CHANGED_GCLK "$changed_gclk" %token yD_CLOG2 "$clog2" %token yD_COS "$cos" %token yD_COSH "$cosh" @@ -819,6 +829,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yD_FDISPLAYH "$fdisplayh" %token yD_FDISPLAYO "$fdisplayo" %token yD_FELL "$fell" +%token yD_FELL_GCLK "$fell_gclk" %token yD_FEOF "$feof" %token yD_FERROR "$ferror" %token yD_FFLUSH "$fflush" @@ -876,6 +887,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yD_RIGHT "$right" %token yD_ROOT "$root" %token yD_ROSE "$rose" +%token yD_ROSE_GCLK "$rose_gclk" %token yD_RTOI "$rtoi" %token yD_SAMPLED "$sampled" %token yD_SFORMAT "$sformat" @@ -888,6 +900,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yD_SQRT "$sqrt" %token yD_SSCANF "$sscanf" %token yD_STABLE "$stable" +%token yD_STABLE_GCLK "$stable_gclk" %token yD_STACKTRACE "$stacktrace" %token yD_STIME "$stime" %token yD_STOP "$stop" @@ -4067,8 +4080,11 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task | yD_BITSTOREAL '(' expr ')' { $$ = new AstBitsToRealD{$1, $3}; } | yD_BITSTOSHORTREAL '(' expr ')' { $$ = new AstBitsToRealD{$1, $3}; UNSUPREAL($1); } | yD_CEIL '(' expr ')' { $$ = new AstCeilD{$1, $3}; } - | yD_CHANGED '(' expr ')' { $$ = new AstLogNot{$1, new AstStable{$1, $3}}; } - | yD_CHANGED '(' expr ',' expr ')' { $$ = $3; BBUNSUP($1, "Unsupported: $changed and clock arguments"); } + | yD_CHANGED '(' expr ')' { $$ = new AstLogNot{$1, new AstStable{$1, $3, nullptr}}; } + | yD_CHANGED '(' expr ',' expr ')' + { $$ = new AstLogNot{$1, new AstStable{$1, $3, GRAMMARP->createClockSenTree($1, $5)}}; } + | yD_CHANGED_GCLK '(' expr ')' + { $$ = new AstLogNot{$1, new AstStable{$1, $3, GRAMMARP->createGlobalClockSenTree($1)}}; } | yD_CLOG2 '(' expr ')' { $$ = new AstCLog2{$1, $3}; } | yD_COS '(' expr ')' { $$ = new AstCosD{$1, $3}; } | yD_COSH '(' expr ')' { $$ = new AstCoshD{$1, $3}; } @@ -4088,8 +4104,9 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task | yD_DIST_T '(' expr ',' expr ')' { $$ = new AstDistT{$1, $3, $5}; } | yD_DIST_UNIFORM '(' expr ',' expr ',' expr ')' { $$ = new AstDistUniform{$1, $3, $5, $7}; } | yD_EXP '(' expr ')' { $$ = new AstExpD{$1, $3}; } - | yD_FELL '(' expr ')' { $$ = new AstFell{$1, $3}; } - | yD_FELL '(' expr ',' expr ')' { $$ = $3; BBUNSUP($1, "Unsupported: $fell and clock arguments"); } + | yD_FELL '(' expr ')' { $$ = new AstFell{$1, $3, nullptr}; } + | yD_FELL '(' expr ',' expr ')' { $$ = new AstFell{$1, $3, GRAMMARP->createClockSenTree($1, $5)}; } + | yD_FELL_GCLK '(' expr ')' { $$ = new AstFell{$1, $3, GRAMMARP->createGlobalClockSenTree($1)}; } | yD_FEOF '(' expr ')' { $$ = new AstFEof{$1, $3}; } | yD_FERROR '(' idClassSel ',' idClassSel ')' { $$ = new AstFError{$1, $3, $5}; } | yD_FGETC '(' expr ')' { $$ = new AstFGetC{$1, $3}; } @@ -4102,8 +4119,7 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task | yD_FSCANF '(' expr ',' str commaVRDListE ')' { $$ = new AstFScanF{$1, *$5, $3, $6}; } | yD_FSEEK '(' idClassSel ',' expr ',' expr ')' { $$ = new AstFSeek{$1, $3, $5, $7}; } | yD_FTELL '(' idClassSel ')' { $$ = new AstFTell{$1, $3}; } - | yD_GLOBAL_CLOCK parenE - { $$ = new AstParseRef{$1, VParseRefExp::PX_TEXT, "__024global_clock", nullptr, nullptr}; } + | yD_GLOBAL_CLOCK parenE { $$ = GRAMMARP->createGlobalClockParseRef($1); } | yD_HIGH '(' exprOrDataType ')' { $$ = new AstAttrOf{$1, VAttrType::DIM_HIGH, $3, nullptr}; } | yD_HIGH '(' exprOrDataType ',' expr ')' { $$ = new AstAttrOf{$1, VAttrType::DIM_HIGH, $3, $5}; } | yD_HYPOT '(' expr ',' expr ')' { $$ = new AstHypotD{$1, $3, $5}; } @@ -4134,8 +4150,9 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task | yD_REWIND '(' idClassSel ')' { $$ = new AstFSeek{$1, $3, new AstConst{$1, 0}, new AstConst{$1, 0}}; } | yD_RIGHT '(' exprOrDataType ')' { $$ = new AstAttrOf{$1, VAttrType::DIM_RIGHT, $3, nullptr}; } | yD_RIGHT '(' exprOrDataType ',' expr ')' { $$ = new AstAttrOf{$1, VAttrType::DIM_RIGHT, $3, $5}; } - | yD_ROSE '(' expr ')' { $$ = new AstRose{$1, $3}; } - | yD_ROSE '(' expr ',' expr ')' { $$ = $3; BBUNSUP($1, "Unsupported: $rose and clock arguments"); } + | yD_ROSE '(' expr ')' { $$ = new AstRose{$1, $3, nullptr}; } + | yD_ROSE '(' expr ',' expr ')' { $$ = new AstRose{$1, $3, GRAMMARP->createClockSenTree($1, $5)}; } + | yD_ROSE_GCLK '(' expr ')' { $$ = new AstRose{$1, $3, GRAMMARP->createGlobalClockSenTree($1)}; } | yD_RTOI '(' expr ')' { $$ = new AstRToIS{$1, $3}; } | yD_SAMPLED '(' expr ')' { $$ = new AstSampled{$1, $3}; } | yD_SFORMATF '(' exprDispList ')' { $$ = new AstSFormatF{$1, AstSFormatF::NoFormat{}, $3, 'd', false}; } @@ -4149,8 +4166,9 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task | yD_SSCANF '(' expr ',' str commaVRDListE ')' { $$ = new AstSScanF{$1, *$5, $3, $6}; } | yD_STIME parenE { $$ = new AstSel{$1, new AstTime{$1, VTimescale{VTimescale::NONE}}, 0, 32}; } - | yD_STABLE '(' expr ')' { $$ = new AstStable{$1, $3}; } - | yD_STABLE '(' expr ',' expr ')' { $$ = $3; BBUNSUP($1, "Unsupported: $stable and clock arguments"); } + | yD_STABLE '(' expr ')' { $$ = new AstStable{$1, $3, nullptr}; } + | yD_STABLE '(' expr ',' expr ')' { $$ = new AstStable{$1, $3, GRAMMARP->createClockSenTree($1, $5)}; } + | yD_STABLE_GCLK '(' expr ')' { $$ = new AstStable{$1, $3, GRAMMARP->createGlobalClockSenTree($1)}; } | yD_TAN '(' expr ')' { $$ = new AstTanD{$1, $3}; } | yD_TANH '(' expr ')' { $$ = new AstTanhD{$1, $3}; } | yD_TESTPLUSARGS '(' expr ')' { $$ = new AstTestPlusArgs{$1, $3}; } diff --git a/test_regress/t/t_past_funcs.v b/test_regress/t/t_past_funcs.v index 07a4b3dd2..74ba1dc76 100644 --- a/test_regress/t/t_past_funcs.v +++ b/test_regress/t/t_past_funcs.v @@ -77,6 +77,23 @@ module Test (/*AUTOARG*/ global clocking @(posedge clk); endclocking always @ ($global_clock) $display("%d", in); + // + assert property (@(posedge clk) $rose(dly0, $global_clock) || dly0%2==0); + assert property (@(posedge clk) $fell(dly1, $global_clock) || dly1%2==1); + assert property (@(posedge clk) !$stable(dly2, $global_clock)); + assert property (@(posedge clk) $changed(dly2, $global_clock)); + // + assert property (@(posedge clk) $rose_gclk(dly0) || dly0%2==0); + assert property (@(posedge clk) $fell_gclk(dly1) || dly1%2==1); + assert property (@(posedge clk) !$stable_gclk(dly2)); + assert property (@(posedge clk) $changed_gclk(dly2)); + + // global_clocking_future_functions are not supported yet: + // $changing_gclk global_clocking_future_function + // $falling_gclk global_clocking_future_function + // $future_gclk global_clocking_future_function + // $rising_gclk global_clocking_future_function + // $steady_gclk global_clocking_future_function endmodule diff --git a/test_regress/t/t_past_unsup_bad.out b/test_regress/t/t_past_unsup_bad.out deleted file mode 100644 index e55f867f2..000000000 --- a/test_regress/t/t_past_unsup_bad.out +++ /dev/null @@ -1,20 +0,0 @@ -%Error-UNSUPPORTED: t/t_past_unsup_bad.v:13:11: Unsupported: $past expr2 and clock arguments - 13 | if ($past(d, 0, 0)) $stop; - | ^~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_past_unsup_bad.v:14:11: Unsupported: $past expr2 and clock arguments - 14 | if ($past(d, 0, 0, clk)) $stop; - | ^~~~~ -%Error-UNSUPPORTED: t/t_past_unsup_bad.v:15:11: Unsupported: $fell and clock arguments - 15 | if ($fell(d, clk)) $stop; - | ^~~~~ -%Error-UNSUPPORTED: t/t_past_unsup_bad.v:16:11: Unsupported: $rose and clock arguments - 16 | if ($rose(d, clk)) $stop; - | ^~~~~ -%Error-UNSUPPORTED: t/t_past_unsup_bad.v:17:11: Unsupported: $stable and clock arguments - 17 | if ($stable(d, clk)) $stop; - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_past_unsup_bad.v:18:11: Unsupported: $changed and clock arguments - 18 | if ($changed(d, clk)) $stop; - | ^~~~~~~~ -%Error: Exiting due to diff --git a/test_regress/t/t_past_unsup_bad.pl b/test_regress/t/t_past_unsup_bad.pl deleted file mode 100755 index a5846c699..000000000 --- a/test_regress/t/t_past_unsup_bad.pl +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env perl -if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } -# DESCRIPTION: Verilator: Verilog Test driver/expect definition -# -# Copyright 2003 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 - -scenarios(vlt => 1); - -lint( - fails => 1, - expect_filename => $Self->{golden_filename}, - ); - -ok(1); -1; diff --git a/test_regress/t/t_past_unsup_bad.v b/test_regress/t/t_past_unsup_bad.v deleted file mode 100644 index edcc5c490..000000000 --- a/test_regress/t/t_past_unsup_bad.v +++ /dev/null @@ -1,20 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain, for -// any use, without warranty, 2018 by Wilson Snyder. -// SPDX-License-Identifier: CC0-1.0 - -module t (d, clk); - input d; - input clk; - - always @ (posedge clk) begin - // Unsupported - if ($past(d, 0, 0)) $stop; - if ($past(d, 0, 0, clk)) $stop; - if ($fell(d, clk)) $stop; - if ($rose(d, clk)) $stop; - if ($stable(d, clk)) $stop; - if ($changed(d, clk)) $stop; - end -endmodule