12#include "mlir/Pass/Pass.h"
14#include "llvm/ADT/STLExtras.h"
15#include "llvm/ADT/SmallVector.h"
16#include "llvm/ADT/StringRef.h"
25#define GEN_PASS_DEF_AIECERTVERIFY
26#include "aie/Dialect/AIEX/Transforms/AIEXPasses.h.inc"
32#define DEBUG_TYPE "cert-verify"
38static int getUcGroupId(Operation *op) {
39 auto group = op->getParentOfType<AIEX::CertAttachToGroupOp>();
40 return group ? group.getGroupID() : 0;
46static LogicalResult checkLocalBarrierColocation(AIE::DeviceOp device) {
47 LogicalResult result = success();
49 std::map<std::pair<int, int>, Operation *> firstScope;
51 [&](AIEX::CertLocalBarrierOp barrier) {
52 int uc = getUcGroupId(barrier);
53 int id =
static_cast<int>(barrier.getLocalBarrierId());
54 Operation *scope = barrier->getParentOfType<AIEX::CertPageOp>();
56 scope = barrier->getParentOfType<AIEX::CertJobOp>();
57 auto key = std::make_pair(uc,
id);
58 auto it = firstScope.find(key);
59 if (it == firstScope.end()) {
60 firstScope[key] = scope;
63 if (it->second != scope) {
65 <<
"cert.local_barrier "
66 << stringifyCertLocalBarrierId(barrier.getLocalBarrierId())
68 <<
" must be on the same page as its other participants";
79static LogicalResult checkWaitTctsUniqueness(AIE::DeviceOp device) {
80 LogicalResult result = success();
82 std::map<std::tuple<Operation *, int, int>, Operation *> seen;
83 device.walk([&](AIEX::CertWaitTCTSOp wait) {
84 int tile = wait.getTileId();
85 int channel = wait.getChannelId();
86 Operation *job = wait->getParentOfType<AIEX::CertJobOp>();
87 Operation *scope = wait->getParentOfType<AIEX::CertPageOp>();
90 auto key = std::make_tuple(scope, tile, channel);
91 auto it = seen.find(key);
92 if (it == seen.end()) {
97 if (it->second == job)
99 wait.emitError() <<
"more than one job on this cert.page waits on tile "
100 << tile <<
" channel " << channel <<
" in uC "
101 << getUcGroupId(wait)
102 <<
"; at most one job per page may wait_tcts on a given "
109static std::set<int> computePresentUcs(AIE::DeviceOp device) {
110 std::set<int> present;
111 device.walk([&](AIEX::CertAttachToGroupOp group) {
112 present.insert(group.getGroupID());
114 if (!device.getBody()->getOps<AIEX::CertPageOp>().empty() ||
115 !device.getBody()->getOps<AIEX::CertJobOp>().empty() ||
116 !device.getBody()->getOps<AIEX::CertSectionOp>().empty())
123static LogicalResult checkPreemptConsistency(AIE::DeviceOp device) {
124 std::map<int, std::multiset<int>> preemptIds;
125 std::map<int, AIEX::CertAttachToGroupOp> repAttach;
126 std::set<int> present;
129 device.walk([&](AIEX::CertAttachToGroupOp group) {
130 int uc = group.getGroupID();
132 if (!repAttach.count(uc))
133 repAttach[uc] = group;
137 if (!device.getBody()->getOps<AIEX::CertPageOp>().empty() ||
138 !device.getBody()->getOps<AIEX::CertJobOp>().empty() ||
139 !device.getBody()->getOps<AIEX::CertSectionOp>().empty())
143 device.walk([&](AIEX::CertPreemptOp preempt) {
144 int uc = getUcGroupId(preempt);
146 preemptIds[uc].insert(preempt.getId());
149 if (present.size() < 2)
152 int ref = *present.begin();
153 const std::multiset<int> &refSet = preemptIds[ref];
154 LogicalResult result = success();
155 for (
int uc : present) {
158 if (preemptIds[uc] != refSet) {
159 Operation *loc = repAttach.count(uc) ? repAttach[uc].getOperation()
160 : device.getOperation();
162 <<
"cert.preempt ids on uC " << uc <<
" differ from uC " << ref
163 <<
"; all uCs must have the same preempt ids (G-preempt)";
174static LogicalResult checkGroupIds(AIE::DeviceOp device) {
177 LogicalResult result = success();
178 auto checkId = [&](Operation *op, int64_t id, llvm::StringRef what) {
179 if (id < 0 || id >= numUcs) {
180 op->emitError() << what <<
" " <<
id <<
" is not a valid uC id [0, "
185 device.walk([&](AIEX::CertAttachToGroupOp g) {
186 checkId(g, g.getGroupID(),
"cert.attach_to_group group id");
189 device.walk([&](AIEX::CertPageOp p) {
190 if (
auto pl = p.getPlacementAttr())
191 checkId(p, pl.getInt(),
"cert.page placement");
202static LogicalResult checkRemoteBarrierParties(AIE::DeviceOp device) {
203 std::set<int> present = computePresentUcs(device);
204 LogicalResult result = success();
206 std::map<AIEX::CertRemoteBarrierId, SmallVector<AIEX::CertRemoteBarrierOp, 4>>
208 device.walk([&](AIEX::CertRemoteBarrierOp b) {
209 byId[b.getRemoteBarrierId()].push_back(b);
212 for (
auto &entry : byId) {
213 StringRef
id = stringifyCertRemoteBarrierId(entry.first);
214 SmallVector<AIEX::CertRemoteBarrierOp, 4> &occs = entry.second;
217 uint32_t mask = occs.front().getPartyMask();
218 bool masksAgree = llvm::all_of(occs, [&](AIEX::CertRemoteBarrierOp b) {
219 return b.getPartyMask() == mask;
223 b.emitError() <<
"cert.remote_barrier " <<
id
224 <<
" occurrences disagree on party_mask";
230 std::map<int, int> occPerUc;
231 for (
auto b : occs) {
232 int own = getUcGroupId(b);
234 if (own >= 0 && own < 32 && !((mask >> own) & 1u)) {
235 b.emitError() <<
"cert.remote_barrier " <<
id <<
" on uC " << own
236 <<
" excludes its own uC from party_mask";
240 for (
auto &[uc, count] : occPerUc)
242 occs.front().emitError()
243 <<
"cert.remote_barrier " <<
id <<
" has " << count
244 <<
" occurrences on uC " << uc
245 <<
" (at most one participant per uC per rendezvous)";
250 for (
int bit = 0; bit < 32; ++bit) {
251 if (!((mask >> bit) & 1u))
253 if (!present.count(bit)) {
254 occs.front().emitError()
255 <<
"cert.remote_barrier " <<
id <<
" party_mask references uC "
256 << bit <<
" which is not present in the design";
258 }
else if (!occPerUc.count(bit)) {
259 occs.front().emitError()
260 <<
"cert.remote_barrier " <<
id <<
" party_mask includes uC " << bit
261 <<
" but that uC has no matching remote_barrier(" <<
id
262 <<
") to rendezvous with";
271static std::optional<uint32_t> registerWrittenBy(Operation &op) {
272 if (
auto o = dyn_cast<AIEX::CertUcDmaWriteDesOp>(op))
273 return o.getWaitHandle();
274 if (
auto o = dyn_cast<AIEX::CertRead32Op>(op))
276 if (
auto o = dyn_cast<AIEX::CertRead32DOp>(op))
284static LogicalResult checkUcDmaWaitHandles(AIE::DeviceOp device) {
285 LogicalResult result = success();
286 device.walk([&](AIEX::CertJobOp job) {
287 llvm::SmallDenseMap<uint32_t, Operation *> lastWrite;
288 for (Operation &o : job.getBody().front()) {
289 if (
auto wait = dyn_cast<AIEX::CertWaitUcDmaOp>(o)) {
290 uint32_t r = wait.getWaitHandle();
291 auto it = lastWrite.find(r);
293 wait.emitError() <<
"cert.wait_uc_dma handle r" << r
294 <<
" is a shared register; the handle must be a "
295 "local register r0..r7 written by a "
296 "cert.uc_dma_write_des in the same job";
298 }
else if (it == lastWrite.end()) {
299 wait.emitError() <<
"cert.wait_uc_dma waits on r" << r
300 <<
", which no earlier cert.uc_dma_write_des in "
303 }
else if (!isa<AIEX::CertUcDmaWriteDesOp>(it->second)) {
304 wait.emitError() <<
"cert.wait_uc_dma waits on r" << r
305 <<
", but its last write is a '"
306 << it->second->getName().getStringRef()
307 <<
"', which replaced the handle";
311 if (
auto r = registerWrittenBy(o))
318struct AIECertVerifyPass
319 : xilinx::AIEX::impl::AIECertVerifyBase<AIECertVerifyPass> {
320 void runOnOperation()
override {
321 AIE::DeviceOp device = getOperation();
322 if (failed(checkGroupIds(device)))
324 if (failed(checkLocalBarrierColocation(device)))
326 if (failed(checkWaitTctsUniqueness(device)))
328 if (failed(checkPreemptConsistency(device)))
330 if (failed(checkRemoteBarrierParties(device)))
332 if (failed(checkUcDmaWaitHandles(device)))
340 return std::make_unique<AIECertVerifyPass>();
virtual uint32_t getNumControllers() const
Total number of microcontrollers (uC) in the device.
std::unique_ptr< mlir::OperationPass< AIE::DeviceOp > > createAIECertVerifyPass()