From f20997a2f0ce162edb3de0d9fda0434c248b8728 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 28 Jan 2023 12:22:23 -0500 Subject: [PATCH] Support global clocking and $global_clock. --- Changes | 1 + src/V3Assert.cpp | 3 +-- src/V3AstNodeOther.h | 8 +++++-- src/V3AstNodes.cpp | 5 ++++ src/V3LinkDot.cpp | 22 ++++++++++++++---- src/verilog.l | 1 + src/verilog.y | 15 ++++++------ test_regress/t/t_clocking_bad5.out | 22 ++++++++++++++++++ test_regress/t/t_clocking_bad5.pl | 20 ++++++++++++++++ test_regress/t/t_clocking_bad5.v | 34 ++++++++++++++++++++++++++++ test_regress/t/t_clocking_unsup1.out | 6 ----- test_regress/t/t_clocking_unsup1.v | 2 -- test_regress/t/t_past_funcs.v | 24 ++++++++++++++++++++ 13 files changed, 139 insertions(+), 24 deletions(-) create mode 100644 test_regress/t/t_clocking_bad5.out create mode 100755 test_regress/t/t_clocking_bad5.pl create mode 100644 test_regress/t/t_clocking_bad5.v diff --git a/Changes b/Changes index a16337032..47232dc75 100644 --- a/Changes +++ b/Changes @@ -14,6 +14,7 @@ Verilator 5.007 devel **Minor:** * Support unpacked unions. +* Support global clocking and $global_clock. Verilator 5.006 2022-01-22 diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 7b33bc841..ea5dd9713 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -350,8 +350,7 @@ private: AstNodeExpr* const exprp = nodep->exprp()->unlinkFrBack(); AstNodeExpr* inp = newSampledExpr(exprp); AstVar* invarp = nullptr; - AstSenTree* const sentreep = nodep->sentreep(); - sentreep->unlinkFrBack(); + AstSenTree* const sentreep = nodep->sentreep()->unlinkFrBack(); AstAlways* const alwaysp = new AstAlways{nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, nullptr}; m_modp->addStmtsp(alwaysp); diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index c71ccb688..59216ff2a 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -822,19 +822,23 @@ class AstClocking final : public AstNode { // @astgen op4 := eventp : Optional[AstVar] std::string m_name; // Clocking block name const bool m_isDefault = false; // True if default clocking + const bool m_isGlobal = false; // True if global clocking public: AstClocking(FileLine* fl, const std::string& name, AstSenItem* sensesp, - AstClockingItem* itemsp, bool isDefault) + AstClockingItem* itemsp, bool isDefault, bool isGlobal) : ASTGEN_SUPER_Clocking(fl) - , m_isDefault{isDefault} { + , m_isDefault{isDefault} + , m_isGlobal{isGlobal} { m_name = name; this->sensesp(sensesp); addItemsp(itemsp); } ASTGEN_MEMBERS_AstClocking; + void dump(std::ostream& str) const override; std::string name() const override { return m_name; } bool isDefault() const { return m_isDefault; } + bool isGlobal() const { return m_isGlobal; } }; class AstClockingItem final : public AstNode { // Parents: CLOCKING diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index d3ed29858..8cfc76cb7 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1509,6 +1509,11 @@ void AstNodeCoverOrAssert::dump(std::ostream& str) const { this->AstNodeStmt::dump(str); if (immediate()) str << " [IMMEDIATE]"; } +void AstClocking::dump(std::ostream& str) const { + this->AstNode::dump(str); + if (isDefault()) str << " [DEFAULT]"; + if (isGlobal()) str << " [GLOBAL]"; +} void AstDisplay::dump(std::ostream& str) const { this->AstNodeStmt::dump(str); // str << " " << displayType().ascii(); diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index d5019866b..eb0522b63 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -1137,11 +1137,23 @@ class LinkDotFindVisitor final : public VNVisitor { iterate(nodep->sensesp()); iterateAndNextNull(nodep->itemsp()); // If the block has no name, one cannot reference the clockvars - if (nodep->name().empty()) return; - VL_RESTORER(m_curSymp); - m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_classOrPackagep); - m_curSymp->fallbackp(nullptr); - iterateAndNextNull(nodep->itemsp()); + VSymEnt* itSymp = nullptr; + if (nodep->isGlobal() // + && m_statep->forPrimary()) { // else flattening may see two globals + m_statep->checkDuplicate(m_curSymp, nodep, "__024global_clock"); + itSymp + = m_statep->insertBlock(m_curSymp, "__024global_clock", nodep, m_classOrPackagep); + itSymp->fallbackp(nullptr); + } + if (!nodep->name().empty()) { + itSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_classOrPackagep); + itSymp->fallbackp(nullptr); + } + if (itSymp) { + VL_RESTORER(m_curSymp); + m_curSymp = itSymp; + iterateAndNextNull(nodep->itemsp()); + } } void visit(AstClockingItem* nodep) override { if (nodep->varp()) { diff --git a/src/verilog.l b/src/verilog.l index bff9bd3b1..a1ccdce0b 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -236,6 +236,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "$fwriteb" { FL; return yD_FWRITEB; } "$fwriteh" { FL; return yD_FWRITEH; } "$fwriteo" { FL; return yD_FWRITEO; } + "$global_clock" { FL; return yD_GLOBAL_CLOCK; } "$hold" { FL; return yaTIMINGSPEC; } "$hypot" { FL; return yD_HYPOT; } "$itor" { FL; return yD_ITOR; } diff --git a/src/verilog.y b/src/verilog.y index 7dcfb0258..11fc699b7 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -844,6 +844,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yD_FWRITEB "$fwriteb" %token yD_FWRITEH "$fwriteh" %token yD_FWRITEO "$fwriteo" +%token yD_GLOBAL_CLOCK "$global_clock" %token yD_HIGH "$high" %token yD_HYPOT "$hypot" %token yD_INCREMENT "$increment" @@ -4101,6 +4102,8 @@ 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_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}; } @@ -5441,17 +5444,15 @@ endLabelE: clocking_declaration: // IEEE: clocking_declaration yCLOCKING idAny clocking_event ';' clocking_itemListE yENDCLOCKING endLabelE - { $$ = new AstClocking{$2, *$2, $3, $5, false}; } + { $$ = new AstClocking{$2, *$2, $3, $5, false, false}; } | yDEFAULT yCLOCKING clocking_event ';' clocking_itemListE yENDCLOCKING endLabelE - { $$ = new AstClocking{$2, "", $3, $5, true}; } + { $$ = new AstClocking{$2, "", $3, $5, true, false}; } | yDEFAULT yCLOCKING idAny clocking_event ';' clocking_itemListE yENDCLOCKING endLabelE - { $$ = new AstClocking{$3, *$3, $4, $6, true}; } + { $$ = new AstClocking{$3, *$3, $4, $6, true, false}; } | yGLOBAL__CLOCKING yCLOCKING clocking_event ';' clocking_itemListE yENDCLOCKING endLabelE - { $$ = nullptr; - BBUNSUP($2, "Unsupported: global clocking"); } + { $$ = new AstClocking{$2, "", $3, $5, false, true}; } | yGLOBAL__CLOCKING yCLOCKING idAny clocking_event ';' clocking_itemListE yENDCLOCKING endLabelE - { $$ = nullptr; - BBUNSUP($3, "Unsupported: global clocking"); } + { $$ = new AstClocking{$3, *$3, $4, $6, false, true}; } ; clocking_event: // IEEE: clocking_event diff --git a/test_regress/t/t_clocking_bad5.out b/test_regress/t/t_clocking_bad5.out new file mode 100644 index 000000000..487c4450e --- /dev/null +++ b/test_regress/t/t_clocking_bad5.out @@ -0,0 +1,22 @@ +%Error: t/t_clocking_bad5.v:29:20: Duplicate declaration of CLOCKING 'ck': '$global_clock' + 29 | global clocking ogck @(posedge clk); endclocking + | ^~~~ + t/t_clocking_bad5.v:26:20: ... Location of original declaration + 26 | global clocking ck @(posedge clk); endclocking + | ^~ +%Error: t/t_clocking_bad5.v:32:20: Duplicate declaration of CLOCKING 'ogck': '$global_clock' + 32 | global clocking ck @(posedge clk); endclocking + | ^~ + t/t_clocking_bad5.v:29:20: ... Location of original declaration + 29 | global clocking ogck @(posedge clk); endclocking + | ^~~~ +%Error: t/t_clocking_bad5.v:32:20: Duplicate declaration of CLOCKING 'ck': 'ck' + 32 | global clocking ck @(posedge clk); endclocking + | ^~ + t/t_clocking_bad5.v:26:20: ... Location of original declaration + 26 | global clocking ck @(posedge clk); endclocking + | ^~ +%Error: t/t_clocking_bad5.v:16:14: Can't find definition of variable: '$global_clock' + 16 | always @ ($global_clock) $display; + | ^~~~~~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_clocking_bad5.pl b/test_regress/t/t_clocking_bad5.pl new file mode 100755 index 000000000..7fdfc93e0 --- /dev/null +++ b/test_regress/t/t_clocking_bad5.pl @@ -0,0 +1,20 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2023 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(linter => 1); + +lint( + verilator_flags2 => ["--timing"], + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_clocking_bad5.v b/test_regress/t/t_clocking_bad5.v new file mode 100644 index 000000000..31eb33744 --- /dev/null +++ b/test_regress/t/t_clocking_bad5.v @@ -0,0 +1,34 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t(/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + sub sub(.*); + + // Bad - no global clock + always @ ($global_clock) $display; + +endmodule + +module sub(/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + global clocking ck @(posedge clk); endclocking + + // Bad - global duplicate + global clocking ogck @(posedge clk); endclocking + + // Bad - name duplicate + global clocking ck @(posedge clk); endclocking + +endmodule diff --git a/test_regress/t/t_clocking_unsup1.out b/test_regress/t/t_clocking_unsup1.out index 1dce09ae4..164271f81 100644 --- a/test_regress/t/t_clocking_unsup1.out +++ b/test_regress/t/t_clocking_unsup1.out @@ -14,10 +14,4 @@ %Error-UNSUPPORTED: t/t_clocking_unsup1.v:18:15: Unsupported: clocking event edge override 18 | output edge #1 b; | ^~~~ -%Error-UNSUPPORTED: t/t_clocking_unsup1.v:13:20: Unsupported: global clocking - 13 | global clocking cb @(posedge clk); - | ^~ -%Error-UNSUPPORTED: t/t_clocking_unsup1.v:21:11: Unsupported: global clocking - 21 | global clocking @(posedge clk); - | ^~~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_clocking_unsup1.v b/test_regress/t/t_clocking_unsup1.v index 0eac13dc7..7315e2e63 100644 --- a/test_regress/t/t_clocking_unsup1.v +++ b/test_regress/t/t_clocking_unsup1.v @@ -18,6 +18,4 @@ module t(/*AUTOARG*/ output edge #1 b; endclocking - global clocking @(posedge clk); - endclocking endmodule diff --git a/test_regress/t/t_past_funcs.v b/test_regress/t/t_past_funcs.v index 2dce45368..07a4b3dd2 100644 --- a/test_regress/t/t_past_funcs.v +++ b/test_regress/t/t_past_funcs.v @@ -23,6 +23,11 @@ module t (/*AUTOARG*/ .clk (clk), .in (in[31:0])); + Test3 test3 (/*AUTOINST*/ + // Inputs + .clk (clk), + .in (in[31:0])); + always @ (posedge clk) begin if (cyc!=0) begin cyc <= cyc + 1; @@ -69,6 +74,10 @@ module Test (/*AUTOARG*/ assert property (@(posedge clk) $fell(dly1) || dly1%2==1); assert property (@(posedge clk) !$stable(dly2)); assert property (@(posedge clk) $changed(dly2)); + + global clocking @(posedge clk); endclocking + always @ ($global_clock) $display("%d", in); + endmodule @@ -101,3 +110,18 @@ module Test2 (/*AUTOARG*/ assert property ($stable(dly2[31:4])); assert property (!$changed(dly2[31:4])); endmodule + +module Test3 (/*AUTOARG*/ + // 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