Support `$setuphold` (#5884)
This commit is contained in:
parent
0b9c2163d5
commit
cd5997a2e6
|
@ -135,6 +135,7 @@ Krzysztof Boronski
|
|||
Krzysztof Boroński
|
||||
Krzysztof Obłonczek
|
||||
Krzysztof Starecki
|
||||
Krzysztof Sychla
|
||||
Kuba Ober
|
||||
Larry Doolittle
|
||||
Liam Braun
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 {}
|
||||
;
|
||||
|
||||
|
|
|
@ -77,7 +77,6 @@ module t (/*AUTOARG*/
|
|||
$recrem();
|
||||
$removal();
|
||||
$setup();
|
||||
$setuphold();
|
||||
$skew();
|
||||
$timeskew();
|
||||
$width();
|
||||
|
|
|
@ -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()
|
|
@ -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
|
Loading…
Reference in New Issue