CIRCT 24.0.0git
Loading...
Searching...
No Matches
AXI4Ops.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// This file implements the AXI4 ops.
10//
11//===----------------------------------------------------------------------===//
12
14#include "llvm/ADT/STLExtras.h"
15#include "llvm/ADT/StringExtras.h"
16#include "llvm/Support/MathExtras.h"
17
18using namespace circt;
19using namespace axi4;
20using namespace mlir;
21
22//===----------------------------------------------------------------------===//
23// XbarOp
24//===----------------------------------------------------------------------===//
25
26namespace {
27/// A width field of a port type, named for diagnostics.
28struct WidthField {
29 llvm::StringLiteral name;
30 uint32_t (PortType::*get)() const;
31};
32} // namespace
33
34// The port width fields, the ones an xbar carries through unchanged first,
35// followed by the ID widths it widens.
36static constexpr WidthField kWidths[] = {
37 {"addr_width", &PortType::getAddrWidth},
38 {"data_width", &PortType::getDataWidth},
39 {"user_width", &PortType::getUserWidth},
40 {"write_id_width", &PortType::getWriteIdWidth},
41 {"read_id_width", &PortType::getReadIdWidth}};
42static constexpr size_t kNumSharedWidths = 3;
43
44/// Verify that `port` agrees with `reference` on each of the widths in
45/// `fields`.
46static LogicalResult verifyWidthsMatch(XbarOp op, ArrayRef<WidthField> fields,
47 PortType port, const Twine &portDesc,
48 PortType reference,
49 const Twine &referenceDesc) {
50 for (const WidthField &field : fields) {
51 uint32_t width = (port.*field.get)();
52 uint32_t expected = (reference.*field.get)();
53 if (width != expected)
54 return op.emitOpError()
55 << portDesc << "'s '" << field.name << "' (" << width
56 << ") must match " << referenceDesc << "'s (" << expected << ")";
57 }
58 return success();
59}
60
61/// The downstream port and window covering `address`, or a null window if no
62/// downstream port covers it.
63static std::pair<size_t, WindowAttr> findDownstreamWindow(ValueRange downstream,
64 uint64_t address) {
65 for (auto [i, value] : llvm::enumerate(downstream))
66 for (WindowAttr window :
67 cast<PortType>(value.getType()).getWindows().getWindows())
68 if (window.getBase() <= address && address <= window.getLast())
69 return {i, window};
70 return {0, {}};
71}
72
73//===----------------------------------------------------------------------===//
74// Outstanding request helpers
75//===----------------------------------------------------------------------===//
76
77/// The outstanding writes and reads a routing op sends down `downstream`, from
78/// the `upstream` ports whose windows reach it.
79static std::pair<uint64_t, uint64_t>
80routingOutstandingBelow(ValueRange upstream, PortType downstream) {
81 uint64_t writes = 0, reads = 0;
82 for (Value value : upstream) {
83 auto manager = cast<PortType>(value.getType());
84 if (!manager.getWindows().overlaps(downstream.getWindows()))
85 continue;
86 writes += manager.getOutstandingWrites();
87 reads += manager.getOutstandingReads();
88 }
89 return {writes, reads};
90}
91
92/// Verify that `port` holds exactly the outstanding requests reaching it.
93static LogicalResult verifyOutstanding(Operation *op, const Twine &portDesc,
94 PortType port,
95 std::pair<uint64_t, uint64_t> reaching,
96 const Twine &sourceDesc) {
97 auto [writes, reads] = reaching;
98 if (port.getOutstandingWrites() != writes)
99 return op->emitOpError() << portDesc << "'s 'outstanding_writes' ("
100 << port.getOutstandingWrites() << ") must be the "
101 << writes << " writes " << sourceDesc;
102 if (port.getOutstandingReads() != reads)
103 return op->emitOpError() << portDesc << "'s 'outstanding_reads' ("
104 << port.getOutstandingReads() << ") must be the "
105 << reads << " reads " << sourceDesc;
106 return success();
107}
108
109LogicalResult XbarOp::verify() {
110 ValueRange upstream = getUpstream();
111 ValueRange downstream = getDownstream();
112 if (upstream.empty())
113 return emitOpError("must have at least one upstream port");
114 if (downstream.empty())
115 return emitOpError("must have at least one downstream port");
116
117 // Make sure all upstream ports agree on widths
118 auto upstreamTy = cast<PortType>(upstream.front().getType());
119 for (auto [i, value] : llvm::enumerate(upstream.drop_front()))
120 if (failed(verifyWidthsMatch(
121 *this, kWidths, cast<PortType>(value.getType()),
122 "upstream port #" + Twine(i + 1), upstreamTy, "upstream port #0")))
123 return failure();
124
125 // Each manager's transactions are tagged with its index downstream.
126 uint32_t idBits = llvm::Log2_64_Ceil(upstream.size());
127
128 // Make sure all downstream ports agree on address, data, and user widths
129 for (auto [i, value] : llvm::enumerate(downstream)) {
130 auto downstreamTy = cast<PortType>(value.getType());
131 if (failed(verifyWidthsMatch(
132 *this, ArrayRef(kWidths).take_front(kNumSharedWidths), downstreamTy,
133 "downstream port #" + Twine(i), upstreamTy, "upstream port #0")))
134 return failure();
135
136 // Make sure downstream ports are wide enough to uniquely tag transactions
137 // from upstream ports.
138 for (const WidthField &field :
139 ArrayRef(kWidths).drop_front(kNumSharedWidths)) {
140 uint32_t least = (upstreamTy.*field.get)() + idBits;
141 if ((downstreamTy.*field.get)() < least)
142 return emitOpError()
143 << "downstream port #" << i << "'s '" << field.name
144 << "' must be at least " << least << " to tag transactions from "
145 << upstream.size() << " managers, got "
146 << (downstreamTy.*field.get)();
147 }
148 }
149
150 // Downstream windows must not overlap (so routing is unambiguous)
151 for (auto [i, value] : llvm::enumerate(downstream)) {
152 auto windows = cast<PortType>(value.getType()).getWindows();
153 for (auto [j, other] : llvm::enumerate(downstream.take_front(i)))
154 if (windows.overlaps(cast<PortType>(other.getType()).getWindows()))
155 return emitOpError() << "downstream ports #" << j << " and #" << i
156 << " have overlapping windows";
157 }
158
159 // Every window a manager can access must be routed downstream, to a port
160 // supporting at least the bursts the manager issues there.
161 // We've already verified no overlap, so we can just check the existence of a
162 // supporting window.
163 for (auto [i, value] : llvm::enumerate(upstream)) {
164 auto managerTy = cast<PortType>(value.getType());
165 for (WindowAttr window : managerTy.getWindows().getWindows()) {
166 // Walk through addresses, skipping the ones we know are covered
167 // Begin at the window's start
168 for (uint64_t address = window.getBase();;) {
169 // Make sure it's supported
170 auto [j, covering] = findDownstreamWindow(downstream, address);
171 if (!covering)
172 return emitOpError()
173 << "address 0x" << llvm::utohexstr(address, /*LowerCase=*/true)
174 << ", in upstream port #" << i
175 << "'s windows, is not covered by any downstream port";
176 if (!covering.getBurstSpecs().covers(window.getBurstSpecs()))
177 return emitOpError()
178 << "downstream port #" << j
179 << " does not support all the bursts upstream port #" << i
180 << " issues at address 0x"
181 << llvm::utohexstr(address, /*LowerCase=*/true)
182 << "; upstream requires " << window.getBurstSpecs()
183 << ", downstream supports " << covering.getBurstSpecs();
184 // If we know that every remaining address in the upstream window is
185 // covered by this downstream window, we're done
186 if (covering.getLast() >= window.getLast())
187 break;
188 // Otherwise, skip ahead to the next address that we don't already know
189 // is covered (the address directly after the end of the covering
190 // downstream window)
191 address = covering.getLast() + 1;
192 }
193 }
194 }
195
196 // Which managers reach a port follows from the windows, so this needs them
197 // checked first.
198 for (auto [i, value] : llvm::enumerate(downstream)) {
199 auto downstreamTy = cast<PortType>(value.getType());
200 if (failed(verifyOutstanding(
201 *this, "downstream port #" + Twine(i), downstreamTy,
202 routingOutstandingBelow(upstream, downstreamTy),
203 "the managers reaching it can issue")))
204 return failure();
205 }
206
207 return success();
208}
209
210//===----------------------------------------------------------------------===//
211// TableGen generated logic.
212//===----------------------------------------------------------------------===//
213
214// Provide the autogenerated implementation guts for the Op classes.
215#define GET_OP_CLASSES
216#include "circt/Dialect/AXI4/AXI4.cpp.inc"
static LogicalResult verifyOutstanding(Operation *op, const Twine &portDesc, PortType port, std::pair< uint64_t, uint64_t > reaching, const Twine &sourceDesc)
Verify that port holds exactly the outstanding requests reaching it.
Definition AXI4Ops.cpp:93
static std::pair< size_t, WindowAttr > findDownstreamWindow(ValueRange downstream, uint64_t address)
The downstream port and window covering address, or a null window if no downstream port covers it.
Definition AXI4Ops.cpp:63
static std::pair< uint64_t, uint64_t > routingOutstandingBelow(ValueRange upstream, PortType downstream)
The outstanding writes and reads a routing op sends down downstream, from the upstream ports whose wi...
Definition AXI4Ops.cpp:80
static LogicalResult verifyWidthsMatch(XbarOp op, ArrayRef< WidthField > fields, PortType port, const Twine &portDesc, PortType reference, const Twine &referenceDesc)
Verify that port agrees with reference on each of the widths in fields.
Definition AXI4Ops.cpp:46
static constexpr WidthField kWidths[]
Definition AXI4Ops.cpp:36
static constexpr size_t kNumSharedWidths
Definition AXI4Ops.cpp:42
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:56
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.