Fix delays using wrong timeunits when modules inlined (#4806).

This commit is contained in:
Wilson Snyder 2024-01-05 18:07:24 -05:00
parent a36a07c297
commit 5294140753
8 changed files with 135 additions and 13 deletions

View File

@ -14,7 +14,7 @@ Verilator 5.021 devel
**Minor:**
* Remove deprecated 32-bit pointer mode (`gcc -m32`).
* Fix delays using wrong timeunits when modules inlined (#4806). [Paul Wright]
Verilator 5.020 2024-01-01

View File

@ -189,7 +189,9 @@ private:
m_clockingp->addNextHere(new AstAlwaysObserved{
flp, new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)}, forkp});
if (v3Global.opt.timing().isSetTrue()) {
assignp->timingControlp(new AstDelay{flp, skewp->unlinkFrBack(), false});
AstDelay* const delayp = new AstDelay{flp, skewp->unlinkFrBack(), false};
delayp->timeunit(m_modp->timeunit());
assignp->timingControlp(delayp);
} else if (v3Global.opt.timing().isSetFalse()) {
nodep->v3warn(E_NOTIMING,
"Clocking output skew greater than #0 requires --timing");

View File

@ -2728,6 +2728,7 @@ class AstDelay final : public AstNodeStmt {
// Delay statement
// @astgen op1 := lhsp : AstNodeExpr // Delay value
// @astgen op2 := stmtsp : List[AstNode] // Statements under delay
VTimescale m_timeunit; // Delay's time unit
const bool m_isCycle; // True if it is a cycle delay
public:
@ -2739,8 +2740,10 @@ public:
ASTGEN_MEMBERS_AstDelay;
void dump(std::ostream& str) const override;
bool isTimingControl() const override { return true; }
bool isCycleDelay() const { return m_isCycle; }
bool same(const AstNode* /*samep*/) const override { return true; }
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
VTimescale timeunit() const { return m_timeunit; }
bool isCycleDelay() const { return m_isCycle; }
};
class AstDisable final : public AstNodeStmt {
string m_name; // Name of block

View File

@ -536,6 +536,13 @@ class LinkParseVisitor final : public VNVisitor {
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void visit(AstDelay* nodep) override {
cleanFileline(nodep);
UASSERT_OBJ(m_modp, nodep, "Delay not under module");
nodep->timeunit(m_modp->timeunit());
iterateChildren(nodep);
}
void visit(AstNodeForeach* nodep) override {
// FOREACH(array, loopvars, body)
UINFO(9, "FOREACH " << nodep << endl);

View File

@ -472,7 +472,6 @@ class TimingControlVisitor final : public VNVisitor {
AstScope* m_scopep = nullptr; // Current scope
AstActive* m_activep = nullptr; // Current active
AstNode* m_procp = nullptr; // NodeProcedure/CFunc/Begin we're under
double m_timescaleFactor = 1.0; // Factor to scale delays by
int m_forkCnt = 0; // Number of forks inside a module
bool m_underJumpBlock = false; // True if we are inside of a jump-block
bool m_underProcedure = false; // True if we are under an always or initial
@ -548,8 +547,10 @@ class TimingControlVisitor final : public VNVisitor {
return stmtp == nodep ? nullptr : stmtp;
}
// Calculate the factor to scale delays by
double calculateTimescaleFactor(VTimescale timeunit) const {
int scalePowerOfTen = timeunit.powerOfTen() - m_netlistp->timeprecision().powerOfTen();
double calculateTimescaleFactor(AstNode* nodep, VTimescale timeunit) const {
UASSERT_OBJ(!timeunit.isNone(), nodep, "timenunit must be set");
const int scalePowerOfTen
= timeunit.powerOfTen() - m_netlistp->timeprecision().powerOfTen();
return std::pow(10.0, scalePowerOfTen);
}
// Creates the global delay scheduler variable
@ -782,8 +783,6 @@ class TimingControlVisitor final : public VNVisitor {
UASSERT(!m_classp, "Module or class under class");
VL_RESTORER(m_classp);
m_classp = VN_CAST(nodep, Class);
VL_RESTORER(m_timescaleFactor);
m_timescaleFactor = calculateTimescaleFactor(nodep->timeunit());
VL_RESTORER(m_forkCnt);
m_forkCnt = 0;
iterateChildren(nodep);
@ -895,20 +894,20 @@ class TimingControlVisitor final : public VNVisitor {
"Cycle delays should have been handled in V3AssertPre");
FileLine* const flp = nodep->fileline();
AstNodeExpr* valuep = V3Const::constifyEdit(nodep->lhsp()->unlinkFrBack());
auto* const constp = VN_CAST(valuep, Const);
AstConst* const constp = VN_CAST(valuep, Const);
if (!constp || !constp->isZero()) {
// Scale the delay
const double timescaleFactor = calculateTimescaleFactor(nodep, nodep->timeunit());
if (valuep->dtypep()->isDouble()) {
valuep = new AstRToIRoundS{
flp,
new AstMulD{flp, valuep,
new AstConst{flp, AstConst::RealDouble{}, m_timescaleFactor}}};
flp, new AstMulD{flp, valuep,
new AstConst{flp, AstConst::RealDouble{}, timescaleFactor}}};
valuep->dtypeSetBitSized(64, VSigning::UNSIGNED);
} else {
valuep->dtypeSetBitSized(64, VSigning::UNSIGNED);
valuep = new AstMul{flp, valuep,
new AstConst{flp, AstConst::Unsized64{},
static_cast<uint64_t>(m_timescaleFactor)}};
static_cast<uint64_t>(timescaleFactor)}};
}
}
// Replace self with a 'co_await dlySched.delay(<valuep>)'

View File

@ -0,0 +1,26 @@
[0] clkb is 1
[5e-07] clkb is 0
[1e-06] clkb is 1
[1.5e-06] clkb is 0
[1.75e-06] Initial finishing, clkb = 0
[2e-06] clkb is 1
[2.5e-06] clkb is 0
[3e-06] clkb is 1
[3.5e-06] clkb is 0
[4e-06] clkb is 1
[4.5e-06] clkb is 0
[5e-06] clkb is 1
[5.5e-06] clkb is 0
[6e-06] clkb is 1
[6.5e-06] clkb is 0
[7e-06] clkb is 1
[7.5e-06] clkb is 0
[8e-06] clkb is 1
[8.5e-06] clkb is 0
[9e-06] clkb is 1
[9.5e-06] clkb is 0
[1e-05] clkb is 1
[1e-05] Finishing (TOP.t.bot)
*-* All Finished *-*
[10500] final (TOP.t)
[1.05e-05] final (TOP.t.bot) count was 21

View File

@ -0,0 +1,24 @@
#!/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(simulator => 1);
compile(
verilator_flags2 => ["--exe --main --timing"],
make_main => 0,
);
execute(
check_finished => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,61 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Paul Wright.
// SPDX-License-Identifier: CC0-1.0
module t;
timeunit 1ns;
timeprecision 1ps;
logic clkb, clk;
initial begin
clkb = 0;
end
always @(clk) begin
clkb <= ~clk;
end
bot bot (.clkb(clkb), .clk(clk));
final begin
$display("[%g] final (%m)", $realtime());
end
endmodule
module bot (input logic clkb, output logic clk);
timeunit 1s;
timeprecision 1fs;
integer count;
real delay;
initial begin
count = 0;
delay = 500e-9;
clk = clkb;
#(3.5 * delay) $display("[%g] Initial finishing, clkb = %b", $realtime(), clkb);
end
always @(clkb) begin
$display("[%g] clkb is %b", $realtime(), clkb);
count++;
#(delay) clk = clkb;
end
always @(count) begin
if (count > 20) begin
$display("[%g] Finishing (%m)", $realtime());
if ($realtime() < (delay * 20)) begin
$display("[%g] %%Error: That was too quick!", $realtime());
end
$write("*-* All Finished *-*\n");
$finish;
end
end
final begin
$display("[%g] final (%m) count was %0d", $realtime(), count);
end
endmodule