From 73ca2ab99739fdb748fa53f3a74e48c452829d35 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 1 Jul 2025 18:00:04 -0400 Subject: [PATCH] Support `$past_gclk` --- src/V3AssertPre.cpp | 8 ++-- src/V3AstNodeExpr.h | 4 +- src/V3EmitV.cpp | 6 ++- src/V3Width.cpp | 1 + src/verilog.l | 1 + src/verilog.y | 4 +- test_regress/t/t_past_funcs.v | 87 +++++++++++++++++------------------ 7 files changed, 60 insertions(+), 51 deletions(-) diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index 8d1bc1e5a..1a680d828 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -460,7 +460,7 @@ private: 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}; + AstNodeExpr* const past = new AstPast{fl, exprp}; past->dtypeFrom(exprp); exprp = new AstAnd{fl, past, new AstNot{fl, exprp->cloneTreePure(false)}}; exprp->dtypeSetBit(); @@ -481,7 +481,7 @@ private: 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}; + AstNodeExpr* const past = new AstPast{fl, exprp}; past->dtypeFrom(exprp); exprp = new AstAnd{fl, new AstNot{fl, past}, exprp->cloneTreePure(false)}; exprp->dtypeSetBit(); @@ -496,7 +496,7 @@ private: AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack(); AstSenTree* sentreep = nodep->sentreep(); if (sentreep) sentreep->unlinkFrBack(); - AstNodeExpr* const past = new AstPast{fl, exprp, nullptr}; + AstNodeExpr* const past = new AstPast{fl, exprp}; past->dtypeFrom(exprp); exprp = new AstEq{fl, past, exprp->cloneTreePure(false)}; exprp->dtypeSetBit(); @@ -514,7 +514,7 @@ private: if (m_disablep) lhsp = new AstAnd{fl, new AstNot{fl, m_disablep}, lhsp}; - AstNodeExpr* const past = new AstPast{fl, lhsp, nullptr}; + AstNodeExpr* const past = new AstPast{fl, lhsp}; past->dtypeFrom(lhsp); AstNodeExpr* const exprp = new AstOr{fl, new AstNot{fl, past}, rhsp}; exprp->dtypeSetBit(); diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index f21b2b178..48bba5822 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -1732,10 +1732,12 @@ class AstPast final : public AstNodeExpr { // @astgen op2 := ticksp : Optional[AstNode] // @astgen op3 := sentreep : Optional[AstSenTree] public: - AstPast(FileLine* fl, AstNodeExpr* exprp, AstNode* ticksp) + AstPast(FileLine* fl, AstNodeExpr* exprp, AstNode* ticksp = nullptr, + AstSenTree* sentreep = nullptr) : ASTGEN_SUPER_Past(fl) { this->exprp(exprp); this->ticksp(ticksp); + this->sentreep(sentreep); } ASTGEN_MEMBERS_AstPast; string emitVerilog() override { V3ERROR_NA_RETURN(""); } diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 794c64b73..df122080b 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -395,9 +395,13 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { void visit(AstPast* nodep) override { putfs(nodep, "$past("); iterateAndNextConstNull(nodep->exprp()); - if (nodep->ticksp()) { + if (nodep->ticksp() || nodep->sentreep()) { puts(", "); iterateAndNextConstNull(nodep->ticksp()); + if (nodep->sentreep()) { + puts(", "); + iterateAndNextConstNull(nodep->sentreep()); + } } puts(")"); } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 8ebdac773..3d0b01b6a 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1356,6 +1356,7 @@ class WidthVisitor final : public VNVisitor { } } } + userIterate(nodep->sentreep(), nullptr); } } void visit(AstRose* nodep) override { diff --git a/src/verilog.l b/src/verilog.l index c22bf9987..754368e1c 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -488,6 +488,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "$onehot" { FL; return yD_ONEHOT; } "$onehot0" { FL; return yD_ONEHOT0; } "$past" { FL; return yD_PAST; } + "$past_gclk" { FL; return yD_PAST_GCLK; } "$right" { FL; return yD_RIGHT; } "$root" { FL; return yD_ROOT; } "$rose" { FL; return yD_ROSE; } diff --git a/src/verilog.y b/src/verilog.y index 8de5f392e..2149b4d3e 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -911,6 +911,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yD_ONEHOT "$onehot" %token yD_ONEHOT0 "$onehot0" %token yD_PAST "$past" +%token yD_PAST_GCLK "$past_gclk" %token yD_POW "$pow" %token yD_PRINTTIMESCALE "$printtimescale" %token yD_RANDOM "$random" @@ -4491,7 +4492,7 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task | yD_LOW '(' exprOrDataType ',' expr ')' { $$ = new AstAttrOf{$1, VAttrType::DIM_LOW, $3, $5}; } | yD_ONEHOT '(' expr ')' { $$ = new AstOneHot{$1, $3}; } | yD_ONEHOT0 '(' expr ')' { $$ = new AstOneHot0{$1, $3}; } - | yD_PAST '(' expr ')' { $$ = new AstPast{$1, $3, nullptr}; } + | yD_PAST '(' expr ')' { $$ = new AstPast{$1, $3}; } | yD_PAST '(' expr ',' exprE ')' { $$ = new AstPast{$1, $3, $5}; } | yD_PAST '(' expr ',' exprE ',' exprE ')' { if ($7) BBUNSUP($1, "Unsupported: $past expr2 and/or clock arguments"); @@ -4499,6 +4500,7 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task | yD_PAST '(' expr ',' exprE ',' exprE ',' clocking_eventE ')' { if ($7 || $9) BBUNSUP($1, "Unsupported: $past expr2 and/or clock arguments"); $$ = new AstPast{$1, $3, $5}; } + | yD_PAST_GCLK '(' expr ')' { $$ = new AstPast{$1, $3, nullptr, GRAMMARP->createGlobalClockSenTree($1)}; } | yD_POW '(' expr ',' expr ')' { $$ = new AstPowD{$1, $3, $5}; } | yD_RANDOM '(' expr ')' { $$ = new AstRand{$1, $3, false}; } | yD_RANDOM parenE { $$ = new AstRand{$1, nullptr, false}; } diff --git a/test_regress/t/t_past_funcs.v b/test_regress/t/t_past_funcs.v index 74ba1dc76..78969cd0d 100644 --- a/test_regress/t/t_past_funcs.v +++ b/test_regress/t/t_past_funcs.v @@ -5,9 +5,9 @@ // SPDX-License-Identifier: Unlicense module t (/*AUTOARG*/ - // Inputs - clk - ); + // Inputs + clk + ); input clk; integer cyc; initial cyc=1; @@ -41,16 +41,16 @@ module t (/*AUTOARG*/ endmodule module Test (/*AUTOARG*/ - // Inputs - clk, in - ); + // Inputs + clk, in + ); input clk; input [31:0] in; - reg [31:0] dly0 = 0; - reg [31:0] dly1 = 1; - reg [31:0] dly2 = -1; + reg [31:0] dly0 = 0; + reg [31:0] dly1 = 0; + reg [31:0] dly2 = 0; // If called in an assertion, sequence, or property, the appropriate clocking event. // Otherwise, if called in a disable condition or a clock expression in an assertion, sequence, or prop, explicit. @@ -60,33 +60,34 @@ module Test (/*AUTOARG*/ always @(posedge clk) begin dly0 <= in; - dly1 <= in; - dly2 <= in; + dly1 <= dly0; + dly2 <= dly1; // In clock expression $write("in=%0d, dly0=%0d, rose=%0d, past=%0d\n", in, dly0, $rose(dly0), $past(dly0)); if ($rose(dly0[4])) $stop; - if ($fell(dly1[4])) $stop; - if ($stable(dly2)) $stop; - if (!$changed(dly2)) $stop; + if ($fell(dly0[4])) $stop; + if (!$stable(dly0[4])) $stop; + if ($changed(dly0[4])) $stop; end - assert property (@(posedge clk) $rose(dly0) || dly0%2==0); - assert property (@(posedge clk) $fell(dly1) || dly1%2==1); - assert property (@(posedge clk) !$stable(dly2)); - assert property (@(posedge clk) $changed(dly2)); + assert property (@(posedge clk) $rose(dly0) || dly0%2==0 || dly2 < 3); + assert property (@(posedge clk) $fell(dly1) || dly1%2==1 || dly2 < 3); + assert property (@(posedge clk) !$stable(dly2) || dly2 < 3); + assert property (@(posedge clk) $changed(dly2) || dly2 < 3); global clocking @(posedge clk); endclocking - always @ ($global_clock) $display("%d", in); + always @ ($global_clock) $display("gc in=%0d", 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(dly0, $global_clock) || dly0%2==0 || dly2 < 3); + assert property (@(posedge clk) $fell(dly1, $global_clock) || dly1%2==1 || dly2 < 3); + assert property (@(posedge clk) !$stable(dly2, $global_clock) || dly2 < 3); + assert property (@(posedge clk) $changed(dly2, $global_clock) || dly2 < 3); // - 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)); + assert property (@(posedge clk) $rose_gclk(dly0) || dly0%2==0 || dly2 < 3); + assert property (@(posedge clk) $fell_gclk(dly1) || dly1%2==1 || dly2 < 3); + assert property (@(posedge clk) $past_gclk(dly1) == dly2 || dly2 < 3); + assert property (@(posedge clk) !$stable_gclk(dly2) || dly2 < 3); + assert property (@(posedge clk) $changed_gclk(dly2) || dly2 < 3); // global_clocking_future_functions are not supported yet: // $changing_gclk global_clocking_future_function @@ -99,21 +100,21 @@ endmodule module Test2 (/*AUTOARG*/ - // Inputs - clk, in - ); + // Inputs + clk, in + ); input clk; input [31:0] in; - reg [31:0] dly0; - reg [31:0] dly1 = 1; - reg [31:0] dly2; + reg [31:0] dly0 = 0; + reg [31:0] dly1 = 0; + reg [31:0] dly2 = 0; always @(posedge clk) begin dly0 <= in; - dly1 <= in; - dly2 <= in; + dly1 <= dly0; + dly2 <= dly1; if ($rose(dly0[31:4])) $stop; if ($fell(dly1[31:4])) $stop; if (!$stable(dly2[31:4])) $stop; @@ -122,23 +123,21 @@ module Test2 (/*AUTOARG*/ default clocking @(posedge clk); endclocking - assert property ($rose(dly0[0]) || dly0%2==0); - assert property ($fell(dly1[0]) || dly1%2==1); - assert property ($stable(dly2[31:4])); - assert property (!$changed(dly2[31:4])); + assert property ($rose(dly0[0]) || dly0%2==0 || dly2 < 3); + assert property ($fell(dly1[0]) || dly1%2==1 || dly2 < 3); + assert property ($stable(dly2[31:4]) || dly2 < 3); + assert property (!$changed(dly2[31:4]) || dly2 < 3); endmodule module Test3 (/*AUTOARG*/ - // Inputs - clk, in - ); + // Inputs + clk, in + ); input clk; input [31:0] in; // Check the named form of global clocking global clocking gck @(posedge clk); endclocking - always @ (gck) $display("%d", in); - always @ ($global_clock) $display("%d", in); endmodule