Support `$setuphold` (#5884)

This commit is contained in:
Krzysztof Sychla 2025-04-02 12:08:51 +02:00 committed by GitHub
parent 0b9c2163d5
commit cd5997a2e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 239 additions and 14 deletions

View File

@ -135,6 +135,7 @@ Krzysztof Boronski
Krzysztof Boroński
Krzysztof Obłonczek
Krzysztof Starecki
Krzysztof Sychla
Kuba Ober
Larry Doolittle
Liam Braun

View File

@ -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:

View File

@ -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);

View File

@ -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}
<TABLE><<EOF>> { FL; yylval.fl->v3error("EOF in 'table'");
yyleng = 0; yy_pop_state(); FL_BRK; yyterminate(); }
<EDGEDESC>{
01|10|[01][zZxX]|[zZxX][01] { FL; return yaEDGEDESC; }
}
<EDGEDESC>","|"[" { FL; return yytext[0]; }
<EDGEDESC>{ws} { FL_FWD; FL_BRK; } /* otherwise ignore white-space */
<EDGEDESC>"]" { FL; yy_pop_state(); return yytext[0]; }
/************************************************************************/
/* Preprocessor */
/* Common for all SYSC header states */

View File

@ -443,6 +443,8 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
// IEEE: string_literal
%token<strp> yaSTRING "STRING"
%token<strp> yaSTRING__IGNORE "STRING-ignored" // Used when expr:string not allowed
// IEEE: edge_descriptor
%token<nump> yaEDGEDESC "EDGE DESCRIPTOR"
%token<fl> yaTIMINGSPEC "TIMING SPEC ELEMENT"
@ -917,6 +919,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
%token<fl> yD_ROSE_GCLK "$rose_gclk"
%token<fl> yD_RTOI "$rtoi"
%token<fl> yD_SAMPLED "$sampled"
%token<fl> yD_SETUPHOLD "$setuphold"
%token<fl> yD_SFORMAT "$sformat"
%token<fl> yD_SFORMATF "$sformatf"
%token<fl> yD_SHORTREALTOBITS "$shortrealtobits"
@ -3117,6 +3120,11 @@ minTypMax<nodeExprp>: // IEEE: mintypmax_expression and constant_minty
| delayExpr ':' delayExpr ':' delayExpr { $$ = $3; MINTYPMAXDLYUNSUP($3); DEL($1); DEL($5); }
;
minTypMaxE<nodeExprp>:
/*empty*/ { $$ = nullptr; }
| minTypMax { $$ = $1; }
;
netSigList<varp>: // IEEE: list_of_port_identifiers
netSig { $$ = $1; }
| netSigList ',' netSig { $$ = $1; $1->addNext($3); }
@ -5765,33 +5773,82 @@ tableEntry<udpTableLinep>: // IEEE: combinational_entry + sequential_entry
//************************************************
// Specify
specify_block<nodep>: // ==IEEE: specify_block
ySPECIFY specifyJunkList yENDSPECIFY { $$ = nullptr; }
specify_block<nodep>: // ==IEEE: specify_block
ySPECIFY specify_itemList yENDSPECIFY { $$ = $2; }
| ySPECIFY yENDSPECIFY { $$ = nullptr; }
;
specifyJunkList:
specifyJunk { } /* ignored */
| specifyJunkList specifyJunk { } /* ignored */
specify_itemList<nodep>: // IEEE: { specify_item }
specify_item { $$ = $1; }
| specify_itemList specify_item { $$ = addNextNull($1, $2); }
;
specifyJunk:
BISONPRE_NOT(ySPECIFY,yENDSPECIFY) { }
| ySPECIFY specifyJunk yENDSPECIFY { }
| error {}
specify_item<nodep>: // ==IEEE: specify_item
system_timing_check { $$ = $1; }
| junkToSemiList ';' { $$ = nullptr; }
;
specparam_declaration<nodep>: // ==IEEE: specparam_declaration
specparam_declaration<nodep>: // ==IEEE: specparam_declaration
ySPECPARAM junkToSemiList ';' { $$ = nullptr; }
;
system_timing_check<nodep>: // ==IEEE: system_timing_check
setuphold_timing_check { $$ = $1; }
;
setuphold_timing_check<nodep>: // ==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<nodeExprp>: // ==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<nodeExprp>:
expr { $$ = $1; }
| expr ':' expr ':' expr { $$ = $3; }
;
delayed_referenceE<nodeExprp>:
/*empty*/ { $$ = nullptr; }
| terminal_identifier { $$ = $1; }
;
terminal_identifier<nodeExprp>:
idArrayed { $$ = $1; }
;
idAnyE<strp>:
/*empty*/ { $$ = nullptr; }
| idAny { $$ = $1; }
;
junkToSemiList:
junkToSemi { } /* ignored */
| junkToSemiList junkToSemi { } /* ignored */
;
junkToSemi:
BISONPRE_NOT(';',yENDSPECIFY,yENDMODULE) { }
BISONPRE_NOT(';',yENDSPECIFY,yENDMODULE,yD_SETUPHOLD) { }
| error {}
;

View File

@ -77,7 +77,6 @@ module t (/*AUTOARG*/
$recrem();
$removal();
$setup();
$setuphold();
$skew();
$timeskew();
$width();

18
test_regress/t/t_setuphold.py Executable file
View File

@ -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()

View File

@ -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