Skip to content

Commit

Permalink
Exclude non-simple wide expressions from V3Gate
Browse files Browse the repository at this point in the history
Signed-off-by: Bartłomiej Chmiel <[email protected]>
  • Loading branch information
b-chmiel committed Nov 27, 2024
1 parent 99daa8d commit 9127c59
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 0 deletions.
49 changes: 49 additions & 0 deletions src/V3Gate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,54 @@ class GateClkDecomp final {
static void apply(GateGraph& graph) { GateClkDecomp{graph}; }
};

//######################################################################
// Iterate over vertices looking for wide expressions to exclude from inline

class GateWideExprExclude final {
size_t m_statExcluded = 0; // Statistic tracking

void exclude(GateVarVertex* const vVtxp) {
if (!vVtxp->varScp()->isWide()) return;
if (vVtxp->inEmpty() || !vVtxp->inEdges().frontp() || !vVtxp->inEdges().frontp()->fromp())
return; // No driver, no exclusion.

const GateLogicVertex* const lVtxp
= vVtxp->inEdges().frontp()->fromp()->as<GateLogicVertex>();

if (const AstActive* const primaryActivep = lVtxp->activep()) {
// Exclude from inlining variables READ multiple times that are initialized by wide
// assigns.
int reads = 0;
for (const V3GraphEdge* const edgep : vVtxp->outEdges().unlinkable()) {
if (!edgep || !edgep->top()) continue;
if (const GateLogicVertex* const lvp = edgep->top()->as<GateLogicVertex>()) {
// To decouple actives thus simplifying scheduling, exclude only those
// VarRefs that are referenced under the same active as they were assigned.
if (lvp->activep() == primaryActivep) reads += edgep->weight();
}
}
if (reads > 1 && VN_IS(lVtxp->nodep(), NodeAssign)) {
vVtxp->clearReducible("Multiple wide reads");
++m_statExcluded;
}
}
}

// CONSTRUCTORS
explicit GateWideExprExclude(GateGraph& graph) {
for (V3GraphVertex& vtx : graph.vertices())
if (GateVarVertex* const vVtxp = vtx.cast<GateVarVertex>()) exclude(vVtxp);
}

~GateWideExprExclude() {
V3Stats::addStat("Optimizations, Gate excluded wide expressions", m_statExcluded);
}

public:
// PUBLIC METHODS
static void apply(GateGraph& graph) { GateWideExprExclude{graph}; }
};

//######################################################################
// Is this a simple expression with a single input and single output?

Expand Down Expand Up @@ -860,6 +908,7 @@ class GateInline final {
: m_graph{graph} {
// Find gate interconnect and optimize
graph.userClearVertices(); // vertex->user(): bool. Indicates we've set it as consumed
GateWideExprExclude::apply(graph);
// Get rid of buffers first,
optimizeSignals(false);
// Then propagate more complicated equations
Expand Down
19 changes: 19 additions & 0 deletions test_regress/t/t_duplicate_wide_expr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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('vlt')

test.compile(verilator_flags2=['--stats'])

test.file_grep(test.stats, r'Optimizations, Gate excluded wide expressions\s+(\d+)', 1)
test.file_grep(test.stats, r'Optimizations, Substituted temps\s+(\d+)', 642)

test.passes()
43 changes: 43 additions & 0 deletions test_regress/t/t_duplicate_wide_expr.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Antmicro.
// SPDX-License-Identifier: CC0-1.0

localparam N = 100; // Should be wide

module t(i, o, o_single, o_outside);
input i;
wire [N-1:0] i;
output o;
wire [N-1:0][1:0] o;
output o_single;
wire [N-1:0] o_single;
output o_outside;
logic [N-1:0] o_outside;

// Shouldn't be inlined
wire [N-1:0] wide_non_simple = N << i;

// Should be inlined
wire [N-1:0] wide_const = 1 >> (N-1);
wire [N-1:0] wide_single = N << i;
wire [N-1:0] wide_outside = N ~^ i;

for (genvar n = 0; n < N ; ++n) begin
assign o[n][0] = i[N-1-n] | wide_non_simple[N-1-n];
assign o[n][1] = i[N-1-n] & wide_const[N-1-n];
end

assign o_single = i ~| wide_single;

sub sub(.i(i), .wide_outside(wide_outside), .o_outside(o_outside));
endmodule

module sub(input wire [N-1:0] i, input wire [N-1:0] wide_outside, output logic [N-1:0] o_outside);
initial begin
for (integer n = 0; n < N ; ++n) begin
o_outside[n] = i[N-1-n] | wide_outside[N-1-n];
end
end
endmodule

0 comments on commit 9127c59

Please sign in to comment.