MLIR-AIE
AIERegisterDatabase.cpp
Go to the documentation of this file.
1//===- AIERegisterDatabase.cpp ----------------------------------*- C++ -*-===//
2//
3// This file is licensed 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// Copyright (C) 2025, Advanced Micro Devices, Inc.
8//
9//===----------------------------------------------------------------------===//
10// Register and event database implementation
11//===----------------------------------------------------------------------===//
12
14#include "llvm/Support/ErrorOr.h"
15#include "llvm/Support/FileSystem.h"
16#include "llvm/Support/MemoryBuffer.h"
17#include "llvm/Support/Path.h"
18#include "llvm/Support/Program.h"
19#include "llvm/Support/raw_ostream.h"
20#include <algorithm>
21#include <cstdlib>
22#include <limits>
23
24using namespace xilinx::AIE;
25using namespace llvm;
26
27const BitFieldInfo *RegisterInfo::getField(StringRef fieldName) const {
28 for (const auto &field : bit_fields) {
29 if (field.name == fieldName) {
30 return &field;
31 }
32 }
33 return nullptr;
34}
35
36namespace {
37std::optional<std::string> findRegDBFile(llvm::StringRef fileName) {
38 auto checkDirectory = [&](llvm::StringRef dir) -> std::optional<std::string> {
39 if (dir.empty())
40 return std::nullopt;
41 llvm::SmallString<256> candidate(dir);
42 llvm::sys::path::append(candidate, fileName);
43 if (llvm::sys::fs::exists(candidate))
44 return std::string(candidate.str());
45 return std::nullopt;
46 };
47
48 if (const char *installDir = std::getenv("MLIR_AIE_INSTALL_DIR")) {
49 llvm::SmallString<256> dir(installDir);
50 llvm::sys::path::append(dir, "lib", "regdb");
51 if (auto path = checkDirectory(dir))
52 return path;
53 }
54
55 std::string mainExecutable = llvm::sys::fs::getMainExecutable(
56 nullptr, reinterpret_cast<void *>(&findRegDBFile));
57 if (!mainExecutable.empty()) {
58 llvm::SmallString<256> dir(mainExecutable);
59 llvm::sys::path::remove_filename(dir);
60 llvm::sys::path::append(dir, "..", "lib", "regdb");
61 llvm::sys::path::remove_dots(dir, true);
62 if (auto path = checkDirectory(dir))
63 return path;
64 }
65
66 return std::nullopt;
67}
68} // namespace
69
70std::unique_ptr<RegisterDatabase> RegisterDatabase::loadAIE2() {
71 auto registerPath = findRegDBFile("aie_registers_aie2.json");
72 auto eventPath = findRegDBFile("events_database.json");
73
74 if (!registerPath || !eventPath) {
75 llvm::errs() << "Failed to locate AIE register database resources. "
76 << "Set MLIR_AIE_INSTALL_DIR to the "
77 << "MLIR-AIE installation path.\n";
78 return nullptr;
79 }
80
81 auto db = std::unique_ptr<RegisterDatabase>(new RegisterDatabase());
82 if (!db->loadFromJSON(*registerPath, *eventPath))
83 return nullptr;
84
85 return db;
86}
87
88bool RegisterDatabase::loadFromJSON(StringRef registerPath,
89 StringRef eventPath) {
90 // Load register database
91 auto registerBuf = MemoryBuffer::getFile(registerPath);
92 if (!registerBuf) {
93 llvm::errs() << "Failed to load register database: " << registerPath
94 << "\n";
95 return false;
96 }
97
98 auto registerJSON = json::parse(registerBuf.get()->getBuffer());
99 if (!registerJSON) {
100 llvm::errs() << "Failed to parse register JSON\n";
101 return false;
102 }
103
104 // Parse register database
105 auto *root = registerJSON->getAsObject();
106 if (!root)
107 return false;
108
109 auto *modules = root->getObject("modules");
110 if (!modules)
111 return false;
112
113 // Iterate through modules (CORE_MODULE, MEMORY_MODULE, etc.)
114 for (const auto &modulePair : *modules) {
115 StringRef moduleName = modulePair.first;
116 auto *moduleObj = modulePair.second.getAsObject();
117 if (!moduleObj)
118 continue;
119
120 auto *registers = moduleObj->getArray("registers");
121 if (!registers)
122 continue;
123
124 // Parse each register
125 for (const auto &regVal : *registers) {
126 auto *regObj = regVal.getAsObject();
127 if (!regObj)
128 continue;
129
130 RegisterInfo regInfo;
131 regInfo.module = moduleName.str();
132 regInfo.offset = 0; // Initialize to 0
133
134 if (auto name = regObj->getString("name"))
135 regInfo.name = name->str();
136 if (auto offset = regObj->getString("offset")) {
137 // Parse hex offset (string like "0x00000340D0")
138 StringRef offsetStr = *offset;
139 // Remove "0x" prefix if present
140 if (offsetStr.starts_with("0x") || offsetStr.starts_with("0X"))
141 offsetStr = offsetStr.drop_front(2);
142
143 uint64_t offsetVal;
144 if (offsetStr.getAsInteger(16, offsetVal)) {
145 llvm::errs() << "Warning: Failed to parse offset '" << *offset
146 << "' for register " << regInfo.name << "\n";
147 } else {
148 regInfo.offset = static_cast<uint32_t>(offsetVal);
149 }
150 }
151 if (auto width = regObj->getInteger("width"))
152 regInfo.width = static_cast<uint32_t>(*width);
153 if (auto type = regObj->getString("type"))
154 regInfo.type = type->str();
155 if (auto reset = regObj->getString("reset"))
156 regInfo.reset = reset->str();
157 if (auto desc = regObj->getString("description"))
158 regInfo.description = desc->str();
159
160 // Parse bit fields
161 if (auto *fields = regObj->getArray("bit_fields")) {
162 for (const auto &fieldVal : *fields) {
163 auto *fieldObj = fieldVal.getAsObject();
164 if (!fieldObj)
165 continue;
166
167 BitFieldInfo fieldInfo;
168 if (auto name = fieldObj->getString("name"))
169 fieldInfo.name = name->str();
170 if (auto *bitRange = fieldObj->getArray("bit_range")) {
171 if (bitRange->size() == 2) {
172 if (auto start = (*bitRange)[0].getAsInteger())
173 fieldInfo.bit_start = static_cast<uint32_t>(*start);
174 if (auto end = (*bitRange)[1].getAsInteger())
175 fieldInfo.bit_end = static_cast<uint32_t>(*end);
176 }
177 }
178 if (auto type = fieldObj->getString("type"))
179 fieldInfo.type = type->str();
180 if (auto reset = fieldObj->getString("reset"))
181 fieldInfo.reset = reset->str();
182 if (auto desc = fieldObj->getString("description"))
183 fieldInfo.description = desc->str();
184
185 regInfo.bit_fields.push_back(fieldInfo);
186 }
187 }
188
189 // Store with module::name as key for uniqueness (lowercase for
190 // case-insensitive lookup)
191 std::string key = moduleName.lower() + "::" + regInfo.name;
192 std::transform(key.begin(), key.end(), key.begin(), ::tolower);
193 registers_[key] = regInfo;
194 }
195 }
196
197 // Load event database
198 auto eventBuf = MemoryBuffer::getFile(eventPath);
199 if (!eventBuf) {
200 llvm::errs() << "Failed to load event database: " << eventPath << "\n";
201 return false;
202 }
203
204 auto eventJSON = json::parse(eventBuf.get()->getBuffer());
205 if (!eventJSON) {
206 llvm::errs() << "Failed to parse event JSON\n";
207 return false;
208 }
209
210 // Parse event database for aie2
211 auto *eventRoot = eventJSON->getAsObject();
212 if (!eventRoot)
213 return false;
214
215 auto *aie2 = eventRoot->getObject("aie2");
216 if (!aie2) {
217 llvm::errs() << "Failed to find 'aie2' architecture in event database\n";
218 return false;
219 }
220
221 auto *eventModules = aie2->getObject("modules");
222 if (!eventModules)
223 return false;
224
225 // Iterate through modules (core, memory, pl, mem_tile)
226 for (const auto &modulePair : *eventModules) {
227 StringRef moduleName = modulePair.first;
228 auto *events = modulePair.second.getAsArray();
229 if (!events)
230 continue;
231
232 // Parse each event
233 for (const auto &eventVal : *events) {
234 auto *eventObj = eventVal.getAsObject();
235 if (!eventObj)
236 continue;
237
238 EventInfo eventInfo;
239 eventInfo.module = moduleName.str();
240
241 if (auto name = eventObj->getString("name"))
242 eventInfo.name = name->str();
243 if (auto number = eventObj->getInteger("number"))
244 eventInfo.number = static_cast<uint32_t>(*number);
245
246 // Store with module::name as key (lowercase for case-insensitive lookup)
247 std::string key = moduleName.str() + "::" + eventInfo.name;
248 std::transform(key.begin(), key.end(), key.begin(), ::tolower);
249 events_[key] = eventInfo;
250 }
251 }
252
253 return true;
254}
255
257 StringRef module) const {
258 std::string key = module.str() + "::" + name.str();
259 std::transform(key.begin(), key.end(), key.begin(), ::tolower);
260 auto it = registers_.find(key);
261 return it != registers_.end() ? &it->second : nullptr;
262}
263
264std::optional<uint32_t> RegisterDatabase::lookupEvent(StringRef name,
265 StringRef module) const {
266 std::string key = module.str() + "::" + name.str();
267 std::transform(key.begin(), key.end(), key.begin(), ::tolower);
268 auto it = events_.find(key);
269 if (it != events_.end()) {
270 return it->second.number;
271 }
272 return std::nullopt;
273}
274
276 uint32_t value) const {
277 // Validate value fits in field width
278 uint32_t width = field.getWidth();
279 if (width == 0)
280 return 0;
281
282 uint64_t maxValue = width >= 32 ? std::numeric_limits<uint32_t>::max()
283 : ((1ULL << width) - 1ULL);
284
285 if (value > maxValue) {
286 llvm::errs() << "Warning: value " << value << " exceeds field width "
287 << width << "\n";
288 value = static_cast<uint32_t>(value & maxValue); // Truncate
289 } else if (width < 32) {
290 value &= static_cast<uint32_t>(maxValue);
291 }
292
293 // Shift to correct bit position
294 uint64_t encoded = (static_cast<uint64_t>(value) << field.bit_start);
295 return static_cast<uint32_t>(encoded);
296}
Register and event database for a specific architecture.
uint32_t encodeFieldValue(const BitFieldInfo &field, uint32_t value) const
Encode a value for a specific bitfield.
static std::unique_ptr< RegisterDatabase > loadAIE2()
Load database for AIE2 architecture.
const RegisterInfo * lookupRegister(llvm::StringRef name, llvm::StringRef module) const
Lookup register by name and module.
std::optional< uint32_t > lookupEvent(llvm::StringRef name, llvm::StringRef module) const
Lookup event by name and module.
Include the generated interface declarations.
Bit field information for a register.
const BitFieldInfo * getField(llvm::StringRef fieldName) const
std::vector< BitFieldInfo > bit_fields