mirror of
https://github.com/verilator/verilator.git
synced 2025-01-01 04:07:34 +00:00
Fix delays using wrong timeunits when modules inlined (#4806).
This commit is contained in:
parent
a36a07c297
commit
5294140753
2
Changes
2
Changes
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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>)'
|
||||
|
26
test_regress/t/t_timing_timescale.out
Normal file
26
test_regress/t/t_timing_timescale.out
Normal 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
|
24
test_regress/t/t_timing_timescale.pl
Executable file
24
test_regress/t/t_timing_timescale.pl
Executable 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;
|
61
test_regress/t/t_timing_timescale.v
Normal file
61
test_regress/t/t_timing_timescale.v
Normal 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
|
Loading…
Reference in New Issue
Block a user