forked from github/verilator
parent
37d68d39c8
commit
f6c0108c86
1
Changes
1
Changes
@ -36,6 +36,7 @@ Verilator 4.202 2021-04-24
|
||||
* Add PINNOTFOUND warning in place of error (#2868). [Udi Finkelstein]
|
||||
* Support overlaps in priority case statements (#2864). [Rupert Swarbrick]
|
||||
* Support for null ports (#2875). [Udi Finkelstein]
|
||||
* Optimize large lookup tables to static data (#2925). [Geza Lore]
|
||||
* Fix class unpacked-array compile error (#2774). [Iru Cai]
|
||||
* Fix scope types in FST and VCD traces (#2805). [Alex Torregrosa]
|
||||
* Fix exceeding command-line ar limit (#2834). [Yinan Xu]
|
||||
|
@ -1814,13 +1814,18 @@ class EmitCImp final : EmitCStmts {
|
||||
splitSizeInc(1);
|
||||
if (dtypep->isWide()) { // Handle unpacked; not basicp->isWide
|
||||
string out;
|
||||
if (zeroit) {
|
||||
out += "VL_ZERO_RESET_W(";
|
||||
if (varp->valuep()) {
|
||||
AstConst* const constp = VN_CAST(varp->valuep(), Const);
|
||||
if (!constp) varp->v3fatalSrc("non-const initializer for variable");
|
||||
for (int w = 0; w < varp->widthWords(); ++w) {
|
||||
out += varp->nameProtect() + suffix + "[" + cvtToStr(w) + "] = ";
|
||||
out += cvtToStr(constp->num().edataWord(w)) + "U;\n";
|
||||
}
|
||||
} else {
|
||||
out += "VL_RAND_RESET_W(";
|
||||
out += zeroit ? "VL_ZERO_RESET_W(" : "VL_RAND_RESET_W(";
|
||||
out += cvtToStr(dtypep->widthMin());
|
||||
out += ", " + varp->nameProtect() + suffix + ");\n";
|
||||
}
|
||||
out += cvtToStr(dtypep->widthMin());
|
||||
out += ", " + varp->nameProtect() + suffix + ");\n";
|
||||
return out;
|
||||
} else {
|
||||
string out = varp->nameProtect() + suffix;
|
||||
|
@ -1526,11 +1526,8 @@ bool V3Number::isCaseEq(const V3Number& rhs) const {
|
||||
if (isString()) return toString() == rhs.toString();
|
||||
if (isDouble()) return toDouble() == rhs.toDouble();
|
||||
if (this->width() != rhs.width()) return false;
|
||||
|
||||
for (int bit = 0; bit < std::max(this->width(), rhs.width()); bit++) {
|
||||
if (this->bitIs(bit) != rhs.bitIs(bit)) return false;
|
||||
}
|
||||
return true;
|
||||
if (m_value != rhs.m_value) return false;
|
||||
return m_valueX == rhs.m_valueX;
|
||||
}
|
||||
|
||||
V3Number& V3Number::opCaseEq(const V3Number& lhs, const V3Number& rhs) {
|
||||
|
@ -30,17 +30,21 @@
|
||||
#include "V3Global.h"
|
||||
#include "V3Premit.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3Hashed.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
constexpr int STATIC_CONST_MIN_WIDTH = 256; // Minimum size to extract to static constant
|
||||
|
||||
//######################################################################
|
||||
// Structure for global state
|
||||
|
||||
class PremitAssignVisitor final : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// AstVar::user4() // bool; occurs on LHS of current assignment
|
||||
AstUser4InUse m_inuser4;
|
||||
// AstVar::user3() // bool; occurs on LHS of current assignment
|
||||
AstUser3InUse m_inuser3;
|
||||
|
||||
// STATE
|
||||
bool m_noopt = false; // Disable optimization of variables in this block
|
||||
@ -50,7 +54,7 @@ private:
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeAssign* nodep) override {
|
||||
// AstNode::user4ClearTree(); // Implied by AstUser4InUse
|
||||
// AstNode::user3ClearTree(); // Implied by AstUser3InUse
|
||||
// LHS first as fewer varrefs
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
// Now find vars marked as lhs
|
||||
@ -59,9 +63,9 @@ private:
|
||||
virtual void visit(AstVarRef* nodep) override {
|
||||
// it's LHS var is used so need a deep temporary
|
||||
if (nodep->access().isWriteOrRW()) {
|
||||
nodep->varp()->user4(true);
|
||||
nodep->varp()->user3(true);
|
||||
} else {
|
||||
if (nodep->varp()->user4()) {
|
||||
if (nodep->varp()->user3()) {
|
||||
if (!m_noopt) UINFO(4, "Block has LHS+RHS var: " << nodep << endl);
|
||||
m_noopt = true;
|
||||
}
|
||||
@ -88,9 +92,11 @@ private:
|
||||
// AstNodeMath::user() -> bool. True if iterated already
|
||||
// AstShiftL::user2() -> bool. True if converted to conditional
|
||||
// AstShiftR::user2() -> bool. True if converted to conditional
|
||||
// AstConst::user2p() -> Replacement static variable pointer
|
||||
// *::user4() -> See PremitAssignVisitor
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
// AstUser4InUse part of V3Hashed
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
@ -100,6 +106,11 @@ private:
|
||||
AstTraceInc* m_inTracep = nullptr; // Inside while loop, special statement additions
|
||||
bool m_assignLhs = false; // Inside assignment lhs, don't breakup extracts
|
||||
|
||||
V3Hashed m_hashed; // Hash set for static constants that can be reused
|
||||
|
||||
VDouble0 m_staticConstantsExtracted; // Statistic tracking
|
||||
VDouble0 m_staticConstantsReused; // Statistic tracking
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
@ -139,14 +150,6 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
AstVar* getBlockTemp(AstNode* nodep) {
|
||||
string newvarname = (string("__Vtemp") + cvtToStr(m_modp->varNumGetInc()));
|
||||
AstVar* varp
|
||||
= new AstVar(nodep->fileline(), AstVarType::STMTTEMP, newvarname, nodep->dtypep());
|
||||
m_cfuncp->addInitsp(varp);
|
||||
return varp;
|
||||
}
|
||||
|
||||
void insertBeforeStmt(AstNode* newp) {
|
||||
// Insert newp before m_stmtp
|
||||
if (m_inWhilep) {
|
||||
@ -173,28 +176,66 @@ private:
|
||||
AstNRelinker linker;
|
||||
nodep->unlinkFrBack(&linker);
|
||||
|
||||
AstVar* varp = getBlockTemp(nodep);
|
||||
AstVar* varp = nullptr;
|
||||
|
||||
AstConst* const constp = VN_CAST(nodep, Const);
|
||||
|
||||
const bool useStatic = constp && (constp->width() >= STATIC_CONST_MIN_WIDTH)
|
||||
&& !constp->num().isFourState();
|
||||
if (useStatic) {
|
||||
// Extract as static constant
|
||||
m_hashed.hash(constp);
|
||||
const auto& it = m_hashed.findDuplicate(constp);
|
||||
if (it == m_hashed.end()) {
|
||||
const string newvarname = string("__Vconst") + cvtToStr(m_modp->varNumGetInc());
|
||||
varp = new AstVar(nodep->fileline(), AstVarType::MODULETEMP, newvarname,
|
||||
nodep->dtypep());
|
||||
varp->isConst(true);
|
||||
varp->isStatic(true);
|
||||
varp->valuep(constp);
|
||||
m_modp->addStmtp(varp);
|
||||
m_hashed.hashAndInsert(constp);
|
||||
nodep->user2p(varp);
|
||||
++m_staticConstantsExtracted;
|
||||
} else {
|
||||
varp = VN_CAST(it->second->user2p(), Var);
|
||||
++m_staticConstantsReused;
|
||||
}
|
||||
} else {
|
||||
// Keep as local temporary
|
||||
const string newvarname = string("__Vtemp") + cvtToStr(m_modp->varNumGetInc());
|
||||
varp
|
||||
= new AstVar(nodep->fileline(), AstVarType::STMTTEMP, newvarname, nodep->dtypep());
|
||||
m_cfuncp->addInitsp(varp);
|
||||
}
|
||||
|
||||
if (noSubst) varp->noSubst(true); // Do not remove varrefs to this in V3Const
|
||||
|
||||
// Replace node tree with reference to var
|
||||
AstVarRef* newp = new AstVarRef(nodep->fileline(), varp, VAccess::READ);
|
||||
linker.relink(newp);
|
||||
// Put assignment before the referencing statement
|
||||
AstAssign* assp = new AstAssign(
|
||||
nodep->fileline(), new AstVarRef(nodep->fileline(), varp, VAccess::WRITE), nodep);
|
||||
insertBeforeStmt(assp);
|
||||
if (debug() > 8) assp->dumpTree(cout, "deepou:");
|
||||
|
||||
if (!useStatic) {
|
||||
// Put assignment before the referencing statement
|
||||
AstAssign* assp = new AstAssign(
|
||||
nodep->fileline(), new AstVarRef(nodep->fileline(), varp, VAccess::WRITE), nodep);
|
||||
insertBeforeStmt(assp);
|
||||
if (debug() > 8) assp->dumpTree(cout, "deepou:");
|
||||
}
|
||||
|
||||
nodep->user1(true); // Don't add another assignment
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
UINFO(4, " MOD " << nodep << endl);
|
||||
VL_RESTORER(m_modp);
|
||||
{
|
||||
m_modp = nodep;
|
||||
m_cfuncp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
UASSERT_OBJ(m_modp == nullptr, nodep, "Nested modules ?");
|
||||
UASSERT_OBJ(m_hashed.mmap().empty(), nodep, "Statements outside module ?");
|
||||
m_modp = nodep;
|
||||
m_cfuncp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
m_modp = nullptr;
|
||||
m_hashed.clear();
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
VL_RESTORER(m_cfuncp);
|
||||
@ -401,7 +442,11 @@ private:
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit PremitVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~PremitVisitor() override = default;
|
||||
virtual ~PremitVisitor() {
|
||||
V3Stats::addStat("Optimizations, Prelim static constants extracted",
|
||||
m_staticConstantsExtracted);
|
||||
V3Stats::addStat("Optimizations, Prelim static constants reused", m_staticConstantsReused);
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
9
test_regress/t/t_extract_static_const.out
Normal file
9
test_regress/t/t_extract_static_const.out
Normal file
@ -0,0 +1,9 @@
|
||||
0x88888888
|
||||
0x77777777
|
||||
0x66666666
|
||||
0x55555555
|
||||
0x44444444
|
||||
0x33333333
|
||||
0x22222222
|
||||
0x11111111
|
||||
*-* All Finished *-*
|
31
test_regress/t/t_extract_static_const.pl
Executable file
31
test_regress/t/t_extract_static_const.pl
Executable file
@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 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(vlt_all => 1);
|
||||
|
||||
compile(
|
||||
verilator_flags2 => ["--stats"],
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
||||
if ($Self->{vlt}) {
|
||||
# Note, with vltmt this might be split differently, so only checking vlt
|
||||
file_grep($Self->{stats}, qr/Optimizations, Prelim static constants extracted\s+(\d+)/i,
|
||||
1);
|
||||
file_grep($Self->{stats}, qr/Optimizations, Prelim static constants reused\s+(\d+)/i,
|
||||
7);
|
||||
}
|
||||
|
||||
ok(1);
|
||||
1;
|
32
test_regress/t/t_extract_static_const.v
Executable file
32
test_regress/t/t_extract_static_const.v
Executable file
@ -0,0 +1,32 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2020 by Geza Lore.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t (/*AUTOARG*/);
|
||||
|
||||
wire [255:0] C = {32'h1111_1111,
|
||||
32'h2222_2222,
|
||||
32'h3333_3333,
|
||||
32'h4444_4444,
|
||||
32'h5555_5555,
|
||||
32'h6666_6666,
|
||||
32'h7777_7777,
|
||||
32'h8888_8888};
|
||||
|
||||
initial begin
|
||||
// Note: Base index via $c to prevent optimizatoin by Verilator
|
||||
$display("0x%32x", C[$c(0*32)+:32]);
|
||||
$display("0x%32x", C[$c(1*32)+:32]);
|
||||
$display("0x%32x", C[$c(2*32)+:32]);
|
||||
$display("0x%32x", C[$c(3*32)+:32]);
|
||||
$display("0x%32x", C[$c(4*32)+:32]);
|
||||
$display("0x%32x", C[$c(5*32)+:32]);
|
||||
$display("0x%32x", C[$c(6*32)+:32]);
|
||||
$display("0x%32x", C[$c(7*32)+:32]);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
11
test_regress/t/t_extract_static_const_multimodule.out
Normal file
11
test_regress/t/t_extract_static_const_multimodule.out
Normal file
@ -0,0 +1,11 @@
|
||||
0x88888888
|
||||
0x66666666
|
||||
0x44444444
|
||||
0x22222222
|
||||
0x1111111122222222333333334444444455555555666666667777777788888888
|
||||
0x77777777
|
||||
0x55555555
|
||||
0x33333333
|
||||
0x11111111
|
||||
0x1111111122222222333333334444444455555555666666667777777788888888
|
||||
*-* All Finished *-*
|
31
test_regress/t/t_extract_static_const_multimodule.pl
Executable file
31
test_regress/t/t_extract_static_const_multimodule.pl
Executable file
@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 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(vlt_all => 1);
|
||||
|
||||
compile(
|
||||
verilator_flags2 => ["--stats"],
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
||||
if ($Self->{vlt}) {
|
||||
# Note, with vltmt this might be split differently, so only checking vlt
|
||||
file_grep($Self->{stats}, qr/Optimizations, Prelim static constants extracted\s+(\d+)/i,
|
||||
2);
|
||||
file_grep($Self->{stats}, qr/Optimizations, Prelim static constants reused\s+(\d+)/i,
|
||||
6);
|
||||
}
|
||||
|
||||
ok(1);
|
||||
1;
|
102
test_regress/t/t_extract_static_const_multimodule.v
Executable file
102
test_regress/t/t_extract_static_const_multimodule.v
Executable file
@ -0,0 +1,102 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2020 by Geza Lore.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
//
|
||||
// Constants should not be shared by different non-inlined modules
|
||||
//
|
||||
|
||||
module a(
|
||||
input wire clk,
|
||||
input wire trig_i,
|
||||
output reg trig_o
|
||||
);
|
||||
/* verilator no_inline_module */
|
||||
|
||||
// Same constant as in module b
|
||||
wire [255:0] C = {32'h1111_1111,
|
||||
32'h2222_2222,
|
||||
32'h3333_3333,
|
||||
32'h4444_4444,
|
||||
32'h5555_5555,
|
||||
32'h6666_6666,
|
||||
32'h7777_7777,
|
||||
32'h8888_8888};
|
||||
|
||||
always @(posedge clk) begin
|
||||
trig_o <= 1'd0;
|
||||
if (trig_i) begin
|
||||
// Note: Base index via $c to prevent optimizatoin by Verilator
|
||||
$display("0x%32x", C[$c(0*32)+:32]);
|
||||
$display("0x%32x", C[$c(2*32)+:32]);
|
||||
$display("0x%32x", C[$c(4*32)+:32]);
|
||||
$display("0x%32x", C[$c(6*32)+:32]);
|
||||
$display("0x%256x", C);
|
||||
trig_o <= 1'd1;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module b(
|
||||
input wire clk,
|
||||
input wire trig_i,
|
||||
output reg trig_o
|
||||
);
|
||||
/* verilator no_inline_module */
|
||||
|
||||
// Same constant as in module a
|
||||
wire [255:0] C = {32'h1111_1111,
|
||||
32'h2222_2222,
|
||||
32'h3333_3333,
|
||||
32'h4444_4444,
|
||||
32'h5555_5555,
|
||||
32'h6666_6666,
|
||||
32'h7777_7777,
|
||||
32'h8888_8888};
|
||||
|
||||
always @(posedge clk) begin
|
||||
trig_o <= 1'd0;
|
||||
if (trig_i) begin
|
||||
// Note: Base index via $c to prevent optimizatoin by Verilator
|
||||
$display("0x%32x", C[$c(1*32)+:32]);
|
||||
$display("0x%32x", C[$c(3*32)+:32]);
|
||||
$display("0x%32x", C[$c(5*32)+:32]);
|
||||
$display("0x%32x", C[$c(7*32)+:32]);
|
||||
$display("0x%256x", C);
|
||||
trig_o <= 1'd1;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
|
||||
input clk;
|
||||
|
||||
integer cyc = 0;
|
||||
|
||||
reg trig_i;
|
||||
wire trig_ab;
|
||||
wire trig_o;
|
||||
|
||||
a a_inst(.clk(clk), .trig_i(trig_i), .trig_o(trig_ab));
|
||||
b b_inst(.clk(clk), .trig_i(trig_ab), .trig_o(trig_o));
|
||||
|
||||
always @(posedge clk) begin
|
||||
trig_i <= cyc == 1;
|
||||
|
||||
if (trig_o) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
cyc++;
|
||||
end
|
||||
|
||||
endmodule
|
Loading…
Reference in New Issue
Block a user