CIRCT 24.0.0git
Loading...
Searching...
No Matches
VerifyAXI4Networks.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Verifies the properties of an AXI4 network that span more than one operation.
10//
11//===----------------------------------------------------------------------===//
12
15#include "mlir/IR/BuiltinOps.h"
16#include "llvm/ADT/TypeSwitch.h"
17
18namespace circt {
19namespace axi4 {
20#define GEN_PASS_DEF_VERIFYAXI4NETWORKS
21#include "circt/Dialect/AXI4/AXI4Passes.h.inc"
22} // namespace axi4
23} // namespace circt
24
25using namespace circt;
26using namespace axi4;
27using namespace mlir;
28
29namespace {
30/// The clock and reset an AXI4 op operates in.
31struct Domain {
32 Value clock, reset;
33};
34} // namespace
35
36/// The domain of an AXI4 op, or failure for one this pass does not know.
37static FailureOr<Domain> getDomain(Operation *op) {
38 return TypeSwitch<Operation *, FailureOr<Domain>>(op)
39 .Case<AbstractManagerOp, AbstractSubordinateOp, ChannelStructsToPortOp,
40 PortToChannelStructsOp, XbarOp>([](auto op) {
41 return Domain{op.getClock(), op.getReset()};
42 })
43 .Default([](Operation *op) -> FailureOr<Domain> {
44 op->emitOpError("unsupported AXI4 network op; cannot verify which "
45 "clock and reset domain it is in");
46 return failure();
47 });
48}
49
50/// Report a port value with more than one consumer, or with none at all.
51static LogicalResult verifyPortUses(Value port) {
52 if (!isa<PortType>(port.getType()))
53 return success();
54 if (port.use_empty()) {
55 mlir::emitWarning(port.getLoc())
56 << "AXI4 port has no uses, so takes no part in a network";
57 return success();
58 }
59 if (port.hasNUsesOrMore(2))
60 return mlir::emitError(port.getLoc())
61 << "AXI4 port must have at most one use; route through an "
62 "'axi4.xbar' to fan out to multiple endpoints";
63 return success();
64}
65
66/// Report two ops connected by a port but operating in different domains.
67static void emitDomainCrossing(Operation *op, Operation *other,
68 StringRef domain) {
69 auto diag = op->emitOpError()
70 << "is in a different " << domain << " domain to the '"
71 << other->getName().getStringRef() << "' connected to it";
72 diag.attachNote(other->getLoc()) << "connected operation here";
73}
74
75/// Report an endpoint that can handle fewer requests than the port reaching it
76/// can have concurrently outstanding - this is a warning since it will only
77/// impact throughput.
78static void warnBottleneck(Operation *op, TypedValue<PortType> port,
79 uint32_t writes, uint32_t reads) {
80 PortType reaching = port.getType();
81 if (writes < reaching.getOutstandingWrites())
82 op->emitWarning() << "endpoint can handle fewer writes than the port "
83 "reaching it can have concurrently outstanding ("
84 << writes << " < " << reaching.getOutstandingWrites()
85 << ")";
86 if (reads < reaching.getOutstandingReads())
87 op->emitWarning() << "endpoint can handle fewer reads than the port "
88 "reaching it can have concurrently outstanding ("
89 << reads << " < " << reaching.getOutstandingReads()
90 << ")";
91}
92
93namespace {
94struct VerifyAXI4NetworksPass
95 : public circt::axi4::impl::VerifyAXI4NetworksBase<VerifyAXI4NetworksPass> {
96 void runOnOperation() override;
97};
98} // namespace
99
100void VerifyAXI4NetworksPass::runOnOperation() {
101 ModuleOp module = getOperation();
102 Dialect *axi4Dialect = module->getContext()->getLoadedDialect<AXI4Dialect>();
103 bool anyFailed = false;
104
105 // Check uses of all axi4.port values
106 module.walk([&](Operation *op) {
107 for (Value result : op->getResults())
108 if (failed(verifyPortUses(result)))
109 anyFailed = true;
110 for (Region &region : op->getRegions())
111 for (Block &block : region)
112 for (BlockArgument arg : block.getArguments())
113 if (failed(verifyPortUses(arg)))
114 anyFailed = true;
115 });
116
117 // Ensure connected ops are in the same clock and reset domains
118 module.walk([&](Operation *op) {
119 if (op->getDialect() != axi4Dialect)
120 return;
121 FailureOr<Domain> domain = getDomain(op);
122 if (failed(domain)) {
123 anyFailed = true;
124 return;
125 }
126
127 for (Value operand : op->getOperands()) {
128 if (!isa<PortType>(operand.getType()))
129 continue;
130 // A port arriving from outside the module carries no comparable clock.
131 Operation *upstream = operand.getDefiningOp();
132 if (!upstream || upstream->getDialect() != axi4Dialect)
133 continue;
134
135 FailureOr<Domain> upstreamDomain = getDomain(upstream);
136 if (failed(upstreamDomain)) {
137 anyFailed = true;
138 continue;
139 }
140 if (domain->clock != upstreamDomain->clock) {
141 emitDomainCrossing(op, upstream, "clock");
142 anyFailed = true;
143 }
144 if (domain->reset != upstreamDomain->reset) {
145 emitDomainCrossing(op, upstream, "reset");
146 anyFailed = true;
147 }
148 }
149 });
150
151 // Warn on bottlenecks where an endpoint may not be able to keep up with the
152 // requests reaching it
153 module.walk([](Operation *op) {
154 TypeSwitch<Operation *>(op)
155 .Case<AbstractSubordinateOp>([](AbstractSubordinateOp subordinate) {
156 warnBottleneck(subordinate, subordinate.getUpstream(),
157 subordinate.getConcurrentWrites(),
158 subordinate.getConcurrentReads());
159 })
160 .Case<PortToChannelStructsOp>([](PortToChannelStructsOp bridge) {
161 warnBottleneck(bridge, bridge.getPort(), bridge.getConcurrentWrites(),
162 bridge.getConcurrentReads());
163 });
164 });
165
166 if (anyFailed)
167 signalPassFailure();
168}
static void emitDomainCrossing(Operation *op, Operation *other, StringRef domain)
Report two ops connected by a port but operating in different domains.
static FailureOr< Domain > getDomain(Operation *op)
The domain of an AXI4 op, or failure for one this pass does not know.
static LogicalResult verifyPortUses(Value port)
Report a port value with more than one consumer, or with none at all.
static void warnBottleneck(Operation *op, TypedValue< PortType > port, uint32_t writes, uint32_t reads)
Report an endpoint that can handle fewer requests than the port reaching it can have concurrently out...
Domain
The number of values each bit of a type can assume.
Definition MooreTypes.h:50
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.