diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index f84c3d6a9..7ee2c4306 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -135,6 +135,7 @@ Krzysztof Boronski Krzysztof Boroński Krzysztof Obłonczek Krzysztof Starecki +Krzysztof Sychla Kuba Ober Larry Doolittle Liam Braun diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 26c895c29..e25f1502b 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -3339,6 +3339,24 @@ public: int instrCount() const override { return INSTR_COUNT_PLI; } bool sameNode(const AstNode* /*samep*/) const override { return true; } }; +class AstSetuphold final : public AstNodeStmt { + // Verilog $setuphold + // @astgen op1 := refevp : AstNodeExpr + // @astgen op2 := dataevp : AstNodeExpr + // @astgen op3 := delrefp : Optional[AstNodeExpr] + // @astgen op4 := deldatap : Optional[AstNodeExpr] +public: + AstSetuphold(FileLine* fl, AstNodeExpr* refevp, AstNodeExpr* dataevp, + AstNodeExpr* delrefp = nullptr, AstNodeExpr* deldatap = nullptr) + : ASTGEN_SUPER_Setuphold(fl) { + this->refevp(refevp); + this->dataevp(dataevp); + this->delrefp(delrefp); + this->deldatap(deldatap); + } + ASTGEN_MEMBERS_AstSetuphold; + bool sameNode(const AstNode* /*samep*/) const override { return true; } +}; class AstStackTraceT final : public AstNodeStmt { // $stacktrace used as task public: diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 108f2de02..8da3687be 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1377,6 +1377,46 @@ class WidthVisitor final : public VNVisitor { } } + void visit(AstSetuphold* nodep) override { + FileLine* const flp = nodep->fileline(); + AstAssignW* newp = nullptr; + if (nodep->delrefp()) { + newp = convertSetupholdToAssign(flp, nodep->refevp(), nodep->delrefp()); + } + if (nodep->deldatap()) { + if (!newp) { + newp = convertSetupholdToAssign(flp, nodep->dataevp(), nodep->deldatap()); + } else { + newp->addNextHere( + convertSetupholdToAssign(flp, nodep->dataevp(), nodep->deldatap())); + } + } + if (!newp) { + pushDeletep(nodep->unlinkFrBack()); + return; + } + nodep->replaceWith(newp); + } + + AstAssignW* convertSetupholdToAssign(FileLine* const flp, AstNodeExpr* const evp, + AstNodeExpr* const delp) { + AstNodeExpr* const lhsp = delp->cloneTreePure(false); + AstNodeExpr* const rhsp = evp->cloneTreePure(false); + UASSERT_OBJ(VN_IS(lhsp, NodeVarRef) || VN_IS(lhsp, NodePreSel), lhsp, + "Incorrect reference in a timing check"); + if (AstNodeVarRef* varRefp = VN_CAST(lhsp, NodeVarRef)) { + if (varRefp->varp()->direction() == VDirection::INPUT) { return nullptr; } + varRefp->access(VAccess::WRITE); + } + if (AstNodePreSel* selp = VN_CAST(lhsp, NodePreSel)) { + if (AstNodeVarRef* varRefp = VN_CAST(selp->fromp(), NodeVarRef)) { + if (varRefp->varp()->direction() == VDirection::INPUT) { return nullptr; } + varRefp->access(VAccess::WRITE); + } + } + return new AstAssignW{flp, lhsp, rhsp}; + } + void visit(AstStable* nodep) override { if (m_vup->prelim()) { iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); diff --git a/src/verilog.l b/src/verilog.l index eb13dbaf8..9aa326993 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -79,7 +79,7 @@ static double lexParseDouble(FileLine* fl, const char* textp, size_t length) { %o 25000 %s V95 V01NC V01C V05 S05 S09 S12 S17 S23 -%s ATTRMODE QQQ STRING TABLE +%s ATTRMODE QQQ STRING TABLE EDGEDESC %s VA5 SAX VLT %s SYSCHDR SYSCHDRP SYSCINT SYSCIMP SYSCIMPH SYSCCTOR SYSCDTOR @@ -271,7 +271,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "$rtoi" { FL; return yD_RTOI; } "$sampled" { FL; return yD_SAMPLED; } "$setup" { FL; return yaTIMINGSPEC; } - "$setuphold" { FL; return yaTIMINGSPEC; } + "$setuphold" { FL; return yD_SETUPHOLD; } "$sformat" { FL; return yD_SFORMAT; } "$sformatf" { FL; return yD_SFORMATF; } "$shortrealtobits" { FL; return yD_SHORTREALTOBITS; } @@ -328,6 +328,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "default" { FL; return yDEFAULT; } "defparam" { FL; return yDEFPARAM; } "disable" { FL; return yDISABLE; } + "edge"/{ws}*"[" { FL; yy_push_state(EDGEDESC); return yEDGE; } "edge" { FL; return yEDGE; } "else" { FL; return yELSE; } "end" { FL; return yEND; } @@ -1023,6 +1024,13 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} <> { FL; yylval.fl->v3error("EOF in 'table'"); yyleng = 0; yy_pop_state(); FL_BRK; yyterminate(); } +{ + 01|10|[01][zZxX]|[zZxX][01] { FL; return yaEDGEDESC; } +} +","|"[" { FL; return yytext[0]; } +{ws} { FL_FWD; FL_BRK; } /* otherwise ignore white-space */ +"]" { FL; yy_pop_state(); return yytext[0]; } + /************************************************************************/ /* Preprocessor */ /* Common for all SYSC header states */ diff --git a/src/verilog.y b/src/verilog.y index c75848d4e..bc7c2586b 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -443,6 +443,8 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) // IEEE: string_literal %token yaSTRING "STRING" %token yaSTRING__IGNORE "STRING-ignored" // Used when expr:string not allowed +// IEEE: edge_descriptor +%token yaEDGEDESC "EDGE DESCRIPTOR" %token yaTIMINGSPEC "TIMING SPEC ELEMENT" @@ -917,6 +919,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yD_ROSE_GCLK "$rose_gclk" %token yD_RTOI "$rtoi" %token yD_SAMPLED "$sampled" +%token yD_SETUPHOLD "$setuphold" %token yD_SFORMAT "$sformat" %token yD_SFORMATF "$sformatf" %token yD_SHORTREALTOBITS "$shortrealtobits" @@ -3117,6 +3120,11 @@ minTypMax: // IEEE: mintypmax_expression and constant_minty | delayExpr ':' delayExpr ':' delayExpr { $$ = $3; MINTYPMAXDLYUNSUP($3); DEL($1); DEL($5); } ; +minTypMaxE: + /*empty*/ { $$ = nullptr; } + | minTypMax { $$ = $1; } + ; + netSigList: // IEEE: list_of_port_identifiers netSig { $$ = $1; } | netSigList ',' netSig { $$ = $1; $1->addNext($3); } @@ -5765,33 +5773,82 @@ tableEntry: // IEEE: combinational_entry + sequential_entry //************************************************ // Specify -specify_block: // ==IEEE: specify_block - ySPECIFY specifyJunkList yENDSPECIFY { $$ = nullptr; } +specify_block: // ==IEEE: specify_block + ySPECIFY specify_itemList yENDSPECIFY { $$ = $2; } | ySPECIFY yENDSPECIFY { $$ = nullptr; } ; -specifyJunkList: - specifyJunk { } /* ignored */ - | specifyJunkList specifyJunk { } /* ignored */ +specify_itemList: // IEEE: { specify_item } + specify_item { $$ = $1; } + | specify_itemList specify_item { $$ = addNextNull($1, $2); } ; -specifyJunk: - BISONPRE_NOT(ySPECIFY,yENDSPECIFY) { } - | ySPECIFY specifyJunk yENDSPECIFY { } - | error {} +specify_item: // ==IEEE: specify_item + system_timing_check { $$ = $1; } + | junkToSemiList ';' { $$ = nullptr; } ; -specparam_declaration: // ==IEEE: specparam_declaration +specparam_declaration: // ==IEEE: specparam_declaration ySPECPARAM junkToSemiList ';' { $$ = nullptr; } ; +system_timing_check: // ==IEEE: system_timing_check + setuphold_timing_check { $$ = $1; } + ; + +setuphold_timing_check: // ==IEEE: $setuphold_timing_check + yD_SETUPHOLD '(' timing_check_event ',' timing_check_event ',' timing_check_limit ',' timing_check_limit ')' ';' { $$ = nullptr; } + | yD_SETUPHOLD '(' timing_check_event ',' timing_check_event ',' timing_check_limit ',' timing_check_limit ',' idAnyE ')' ';' { $$ = nullptr; } + | yD_SETUPHOLD '(' timing_check_event ',' timing_check_event ',' timing_check_limit ',' timing_check_limit ',' idAnyE ',' minTypMaxE ')' ';' { $$ = nullptr; } + | yD_SETUPHOLD '(' timing_check_event ',' timing_check_event ',' timing_check_limit ',' timing_check_limit ',' idAnyE ',' minTypMaxE ',' minTypMaxE ')' ';' { $$ = nullptr; } + | yD_SETUPHOLD '(' timing_check_event ',' timing_check_event ',' timing_check_limit ',' timing_check_limit ',' idAnyE ',' minTypMaxE ',' minTypMaxE ',' delayed_referenceE ')' ';' { $$ = new AstSetuphold{$1, $3, $5, $17}; } + | yD_SETUPHOLD '(' timing_check_event ',' timing_check_event ',' timing_check_limit ',' timing_check_limit ',' idAnyE ',' minTypMaxE ',' minTypMaxE ',' delayed_referenceE ',' delayed_referenceE ')' ';' { $$ = new AstSetuphold{$1, $3, $5, $17, $19}; } + ; + +timing_check_event: // ==IEEE: $timing_check_event + terminal_identifier { $$ = $1; } + | yPOSEDGE terminal_identifier { $$ = $2; } + | yNEGEDGE terminal_identifier { $$ = $2; } + | yEDGE terminal_identifier { $$ = $2; } + | yEDGE '[' edge_descriptor_list ']' terminal_identifier { $$ = $5; } + | terminal_identifier yP_ANDANDAND expr { $$ = $1; } + | yPOSEDGE terminal_identifier yP_ANDANDAND expr { $$ = $2; } + | yNEGEDGE terminal_identifier yP_ANDANDAND expr { $$ = $2; } + | yEDGE terminal_identifier yP_ANDANDAND expr { $$ = $2; } + | yEDGE '[' edge_descriptor_list ']' terminal_identifier yP_ANDANDAND expr { $$ = $5; } + ; + +edge_descriptor_list: + yaEDGEDESC { } + | edge_descriptor_list ',' yaEDGEDESC { } + ; + +timing_check_limit: + expr { $$ = $1; } + | expr ':' expr ':' expr { $$ = $3; } + ; + +delayed_referenceE: + /*empty*/ { $$ = nullptr; } + | terminal_identifier { $$ = $1; } + ; + +terminal_identifier: + idArrayed { $$ = $1; } + ; + +idAnyE: + /*empty*/ { $$ = nullptr; } + | idAny { $$ = $1; } + ; + junkToSemiList: junkToSemi { } /* ignored */ | junkToSemiList junkToSemi { } /* ignored */ ; junkToSemi: - BISONPRE_NOT(';',yENDSPECIFY,yENDMODULE) { } + BISONPRE_NOT(';',yENDSPECIFY,yENDMODULE,yD_SETUPHOLD) { } | error {} ; diff --git a/test_regress/t/t_gate_basic.v b/test_regress/t/t_gate_basic.v index 8ac9c0dab..d7c9b2690 100644 --- a/test_regress/t/t_gate_basic.v +++ b/test_regress/t/t_gate_basic.v @@ -77,7 +77,6 @@ module t (/*AUTOARG*/ $recrem(); $removal(); $setup(); - $setuphold(); $skew(); $timeskew(); $width(); diff --git a/test_regress/t/t_setuphold.py b/test_regress/t/t_setuphold.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_setuphold.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_setuphold.v b/test_regress/t/t_setuphold.v new file mode 100644 index 000000000..3085c7d70 --- /dev/null +++ b/test_regress/t/t_setuphold.v @@ -0,0 +1,84 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// 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 + +module t (/*AUTOARG*/ + // Inputs + clk, + d, + t_in + ); + + input clk; + input d; + input t_in; + wire delayed_CLK; + wire delayed_D; + reg notifier; + wire [1:0] BL_X = 2'b11; + wire [5:0] BL_X2; + wire BL_0; + wire [3:0] BL_1 = 4'b1100; + wire fake_CLK; + wire fake_D; + + logic[3:0] sh1 = 1; + logic[3:0] sh2 = 2; + logic[3:0] sh3 = 3; + logic[3:0] sh4 = 4; + logic[3:0] sh5 = 5; + logic[3:0] sh6 = 6; + + int cyc = 0; + + specify + $setuphold (posedge clk, negedge d, 0, 0, notifier, (0:0:0), 0, delayed_CLK, delayed_D); + $setuphold (posedge sh1, negedge sh3, 0, 0, notifier,,, sh2, sh4); + $setuphold (posedge sh5, negedge d, 0, 0, notifier,,, sh6); + $setuphold (posedge clk, negedge d, 0, 0, notifier, (1:2:3), (0:0:0)); + $setuphold (posedge clk, negedge d, 0, 0, notifier, (1:2:3)); + $setuphold (posedge clk, negedge d, 0, 0, notifier); + $setuphold (posedge clk, negedge d, 0, 0); + $setuphold (posedge clk, negedge d, 0, 0); + $setuphold (posedge clk, negedge d, (0:0:0), (0:0:0)); + $setuphold (posedge clk, negedge d, 0:0:0, 0:0:0); + $setuphold (posedge clk, negedge d, 0, 0,,,,,); + + $setuphold (posedge clk &&& sh1, BL_X[0], 0, 0, ,,,delayed_CLK, BL_0); + $setuphold (posedge clk &&& sh1, BL_1, 0, 0, ,,,delayed_CLK, BL_X2[4:1]); + + $setuphold (fake_CLK, fake_D &&& sh1, 0, 0); + $setuphold (posedge fake_CLK, posedge fake_D &&& sh1, 0, 0); + $setuphold (negedge fake_CLK, negedge fake_D &&& sh1, 0, 0); + $setuphold (edge fake_CLK, edge fake_D &&& sh1, 0, 0); + $setuphold (edge [0Z, z1, 10] fake_CLK, edge [01, x0, 0X] fake_CLK &&& sh1, 0, 0); + + $setuphold (posedge clk, negedge d, 0, 0, notifier, (0:0:0), 0, t_in); + endspecify + + initial begin + if (sh1 != sh2 || sh3 != sh4) begin + $stop; + end + if (sh5 != sh6) begin + $stop; + end + if (BL_0 != BL_X[0] || BL_1 != BL_X2[4:1]) begin + $stop; + end + end + + always @(posedge clk) begin + cyc <= cyc + 1; + $display("%d %d", clk, delayed_CLK); + if (delayed_CLK != clk || delayed_D != d) begin + $stop; + end + if (cyc == 10) begin + $display("*-* All Finished *-*"); + $finish; + end + end +endmodule