19 #include <nlohmann/json.hpp>
22 using namespace ::
esi;
32 friend class ::esi::Manifest;
35 Impl(Context &
ctxt,
const std::string &jsonManifest);
40 std::optional<ModuleInfo>
getModInfo(
const nlohmann::json &)
const;
49 services::Service *
getService(AppIDPath idPath, AcceleratorConnection &,
50 const nlohmann::json &,
55 std::vector<services::Service *>
56 getServices(AppIDPath idPath, AcceleratorConnection &,
const nlohmann::json &,
61 std::vector<std::unique_ptr<BundlePort>>
64 const nlohmann::json &instJson)
const;
68 std::vector<std::unique_ptr<Instance>>
71 const nlohmann::json &instJson)
const;
75 std::unique_ptr<Instance>
78 const nlohmann::json &childJson)
const;
88 std::unique_ptr<Accelerator>
91 const Type *
parseType(
const nlohmann::json &typeJson);
101 std::optional<const Type *>
getType(Type::ID
id)
const {
102 return ctxt.getType(
id);
105 std::any
getAny(
const nlohmann::json &value)
const;
119 static std::optional<AppID>
parseID(
const nlohmann::json &jsonID) {
120 if (!jsonID.is_object())
122 std::optional<uint32_t> idx;
123 if (jsonID.contains(
"index"))
124 idx = jsonID.at(
"index").get<uint32_t>();
125 if (jsonID.contains(
"name") && jsonID.size() <= 2)
126 return AppID(jsonID.at(
"name").get<std::string>(), idx);
131 std::optional<AppID>
id =
parseID(jsonID);
133 throw std::runtime_error(
"Malformed manifest: invalid appID");
138 for (
auto &idJson : jsonIDPath)
144 return ServicePortDesc{jsonPort.at(
"serviceName").get<std::string>(),
145 jsonPort.at(
"port").get<std::string>()};
151 auto getObject = [
this](
const nlohmann::json &
json) -> std::any {
152 std::map<std::string, std::any> ret;
153 for (
auto &e :
json.items())
154 ret[e.key()] =
getAny(e.value());
157 if (ret.size() != 2 || !ret.contains(
"type") || !ret.contains(
"value"))
159 std::any value = ret.at(
"value");
160 std::any typeID = ret.at(
"type");
161 if (typeID.type() !=
typeid(std::string))
163 std::optional<const Type *> type =
164 getType(std::any_cast<std::string>(type));
169 return Constant{value, type};
172 auto getArray = [
this](
const nlohmann::json &
json) -> std::any {
173 std::vector<std::any> ret;
179 auto getValue = [&](
const nlohmann::json &innerValue) -> std::any {
180 if (innerValue.is_string())
181 return innerValue.get<std::string>();
182 else if (innerValue.is_number_unsigned())
183 return innerValue.get<uint64_t>();
184 else if (innerValue.is_number_integer())
185 return innerValue.get<int64_t>();
186 else if (innerValue.is_number_float())
187 return innerValue.get<
double>();
188 else if (innerValue.is_boolean())
189 return innerValue.get<
bool>();
190 else if (innerValue.is_null())
191 return innerValue.get<std::nullptr_t>();
192 else if (innerValue.is_object())
193 return getObject(innerValue);
194 else if (innerValue.is_array())
195 return getArray(innerValue);
197 throw std::runtime_error(
"Unknown type in manifest: " +
201 std::optional<AppID> appid =
parseID(value);
204 if (!value.is_object() || !value.contains(
"type") || !value.contains(
"value"))
205 return getValue(value);
206 return Constant{getValue(value.at(
"value")),
getType(value.at(
"type"))};
210 const nlohmann::json &mod)
const {
211 for (
auto &extra : mod.items())
212 if (extra.key() !=
"name" && extra.key() !=
"summary" &&
213 extra.key() !=
"version" && extra.key() !=
"repo" &&
214 extra.key() !=
"commitHash")
215 info.extra[extra.key()] = getAny(extra.value());
217 auto value = [&](
const std::string &key) -> std::optional<std::string> {
218 auto f = mod.find(key);
223 info.name = value(
"name");
224 info.summary = value(
"summary");
225 info.version = value(
"version");
226 info.repo = value(
"repo");
227 info.commitHash = value(
"commitHash");
231 const nlohmann::json &mod)
const {
232 for (
auto &item : mod.items()) {
233 std::any value = getAny(item.value());
234 auto *c = std::any_cast<Constant>(&value);
236 info.constants[item.key()] = *c;
239 info.constants[item.key()] = Constant{value, std::nullopt};
249 manifestJson = nlohmann::ordered_json::parse(manifestStr);
258 if (mod.contains(
"symInfo"))
260 if (mod.contains(
"symConsts"))
264 }
catch (
const std::exception &e) {
265 std::string msg =
"malformed manifest: " + std::string(e.what());
267 msg +=
" (schema version 0 is not considered stable)";
268 throw std::runtime_error(msg);
272 std::unique_ptr<Accelerator>
277 auto svcDecls = manifestJson.at(
"serviceDeclarations");
278 scanServiceDecls(acc, svcDecls, activeSvcs);
281 auto designJson = manifestJson.at(
"design");
282 std::vector<services::Service *> services =
283 getServices({}, acc, designJson, activeSvcs);
286 auto ports = getBundlePorts(acc, {}, activeSvcs, designJson);
288 return std::make_unique<Accelerator>(
289 getModInfo(designJson),
290 getChildInstances({}, acc, activeSvcs, designJson), services, ports);
293 std::optional<ModuleInfo>
295 auto instOfIter =
json.find(
"instOf");
296 if (instOfIter ==
json.end())
298 auto f = symbolInfoCache.find(instOfIter.value());
299 if (f != symbolInfoCache.end())
305 const nlohmann::json &svcDecls,
307 for (
auto &svcDecl : svcDecls) {
308 if (
auto f = svcDecl.find(
"serviceName"); f != svcDecl.end()) {
311 for (
auto &
detail : svcDecl.items())
315 services::Service::Type svcId =
316 services::ServiceRegistry::lookupServiceType(f.value());
317 auto svc = acc.getService(svcId, {},
"",
320 activeServices[svcDecl.at(
"symbol")] = svc;
325 std::vector<std::unique_ptr<Instance>>
328 const nlohmann::json &instJson)
const {
329 std::vector<std::unique_ptr<Instance>> ret;
330 auto childrenIter = instJson.find(
"children");
331 if (childrenIter == instJson.end())
333 for (
auto &child : childrenIter.value())
334 ret.emplace_back(getChildInstance(idPath, acc, activeServices, child));
338 std::unique_ptr<Instance>
341 const nlohmann::json &child)
const {
343 idPath.push_back(childID);
345 std::vector<services::Service *> services =
346 getServices(idPath, acc, child, activeServices);
348 auto children = getChildInstances(idPath, acc, activeServices, child);
349 auto ports = getBundlePorts(acc, idPath, activeServices, child);
350 return std::make_unique<Instance>(
parseIDChecked(child.at(
"appID")),
351 getModInfo(child), std::move(children),
357 const nlohmann::json &svcJson,
361 idPath.push_back(
id);
365 for (
auto &client : svcJson.at(
"clientDetails")) {
366 HWClientDetail clientDetail;
367 for (
auto &
detail : client.items()) {
368 if (
detail.key() ==
"relAppIDPath")
370 else if (
detail.key() ==
"servicePort")
372 else if (
detail.key() ==
"channelAssignments") {
373 for (
auto &chan :
detail.value().items()) {
374 ChannelAssignment chanAssign;
375 for (
auto &assign : chan.value().items())
376 if (assign.key() ==
"type")
377 chanAssign.type = assign.value();
379 chanAssign.implOptions[assign.key()] = getAny(assign.value());
380 clientDetail.channelAssignments[chan.key()] = chanAssign;
383 clientDetail.implOptions[
detail.key()] = getAny(
detail.value());
385 clientDetails.push_back(clientDetail);
390 std::string implName;
392 for (
auto &
detail : svcJson.items()) {
393 if (
detail.key() ==
"appID" ||
detail.key() ==
"clientDetails")
395 if (
detail.key() ==
"serviceImplName")
396 implName =
detail.value();
397 else if (
detail.key() ==
"service")
398 service =
detail.value().get<std::string>();
404 services::Service *svc =
nullptr;
405 auto activeServiceIter = activeServices.find(service);
406 if (activeServiceIter != activeServices.end()) {
407 services::Service::Type svcType =
408 services::ServiceRegistry::lookupServiceType(
409 activeServiceIter->second->getServiceSymbol());
410 svc = activeServiceIter->second->getChildService(
411 &acc, svcType, idPath, implName, svcDetails, clientDetails);
413 services::Service::Type svcType =
414 services::ServiceRegistry::lookupServiceType(service);
415 svc = acc.getService(svcType, idPath, implName, svcDetails, clientDetails);
420 activeServices[service] = svc;
424 std::vector<services::Service *>
426 const nlohmann::json &svcsJson,
428 std::vector<services::Service *> ret;
429 auto svcsIter = svcsJson.find(
"services");
430 if (svcsIter == svcsJson.end())
433 for (
auto &svc : svcsIter.value())
434 ret.emplace_back(getService(idPath, acc, svc, activeServices));
438 std::vector<std::unique_ptr<BundlePort>>
441 const nlohmann::json &instJson)
const {
442 std::vector<std::unique_ptr<BundlePort>> ret;
443 auto clientPortsIter = instJson.find(
"clientPorts");
444 if (clientPortsIter == instJson.end())
447 for (
auto &content : clientPortsIter.value()) {
449 std::string serviceName =
"";
450 if (
auto f = content.find(
"servicePort"); f != content.end())
452 auto svcIter = activeServices.find(serviceName);
453 if (svcIter == activeServices.end()) {
456 if (svcIter = activeServices.find(
""); svcIter == activeServices.end())
457 throw std::runtime_error(
458 "Malformed manifest: could not find active service '" +
461 services::Service *svc = svcIter->second;
463 std::string typeName = content.at(
"typeID");
464 auto type = getType(typeName);
466 throw std::runtime_error(
467 "Malformed manifest: could not find port type '" + typeName +
"'");
468 const BundleType *bundleType =
dynamic_cast<const BundleType *
>(*type);
470 throw std::runtime_error(
"Malformed manifest: type '" + typeName +
471 "' is not a bundle type");
474 std::map<std::string, ChannelPort &> portChannels =
475 acc.requestChannelsFor(idPath, bundleType, activeServices);
477 services::ServicePort *svcPort =
478 svc->getPort(idPath, bundleType, portChannels, acc);
480 ret.emplace_back(svcPort);
482 ret.emplace_back(
new BundlePort(idPath.back(), portChannels));
491 const Type *
parseType(
const nlohmann::json &typeJson, Context &
ctxt);
493 BundleType *parseBundleType(
const nlohmann::json &typeJson, Context &cache) {
494 assert(typeJson.at(
"mnemonic") ==
"bundle");
496 std::vector<std::tuple<std::string, BundleType::Direction, const Type *>>
498 for (
auto &chanJson : typeJson[
"channels"]) {
499 std::string dirStr = chanJson.at(
"direction");
502 dir = BundleType::Direction::To;
503 else if (dirStr ==
"from")
504 dir = BundleType::Direction::From;
506 throw std::runtime_error(
"Malformed manifest: unknown direction '" +
508 channels.emplace_back(chanJson.at(
"name"), dir,
511 return new BundleType(typeJson.at(
"id"), channels);
514 ChannelType *parseChannelType(
const nlohmann::json &typeJson, Context &cache) {
515 assert(typeJson.at(
"mnemonic") ==
"channel");
516 return new ChannelType(typeJson.at(
"id"),
520 Type *parseInt(
const nlohmann::json &typeJson, Context &cache) {
521 assert(typeJson.at(
"mnemonic") ==
"int");
522 std::string sign = typeJson.at(
"signedness");
523 uint64_t width = typeJson.at(
"hwBitwidth");
524 Type::ID
id = typeJson.at(
"id");
526 if (sign ==
"signed")
527 return new SIntType(
id, width);
528 else if (sign ==
"unsigned")
529 return new UIntType(
id, width);
530 else if (sign ==
"signless" && width == 0)
532 return new VoidType(
id);
533 else if (sign ==
"signless" && width > 0)
534 return new BitsType(
id, width);
536 throw std::runtime_error(
"Malformed manifest: unknown sign '" + sign +
"'");
539 StructType *parseStruct(
const nlohmann::json &typeJson, Context &cache) {
540 assert(typeJson.at(
"mnemonic") ==
"struct");
541 std::vector<std::pair<std::string, const Type *>> fields;
542 for (
auto &fieldJson : typeJson[
"fields"])
543 fields.emplace_back(fieldJson.at(
"name"),
545 return new StructType(typeJson.at(
"id"), fields);
548 ArrayType *parseArray(
const nlohmann::json &typeJson, Context &cache) {
549 assert(typeJson.at(
"mnemonic") ==
"array");
550 uint64_t size = typeJson.at(
"size");
551 return new ArrayType(typeJson.at(
"id"),
552 parseType(typeJson.at(
"element"), cache), size);
555 using TypeParser = std::function<Type *(
const nlohmann::json &, Context &)>;
556 const std::map<std::string_view, TypeParser> typeParsers = {
557 {
"bundle", parseBundleType},
558 {
"channel", parseChannelType},
559 {
"std::any", [](
const nlohmann::json &typeJson,
560 Context &cache) {
return new AnyType(typeJson.at(
"id")); }},
562 {
"struct", parseStruct},
563 {
"array", parseArray},
568 const Type *
parseType(
const nlohmann::json &typeJson, Context &cache) {
570 if (typeJson.is_string())
571 id = typeJson.get<std::string>();
573 id = typeJson.at(
"id");
574 if (std::optional<const Type *> t = cache.getType(
id))
576 if (typeJson.is_string())
577 throw std::runtime_error(
"malformed manifest: unknown type '" +
id +
"'");
580 std::string mnemonic = typeJson.at(
"mnemonic");
581 auto f = typeParsers.find(mnemonic);
582 if (f != typeParsers.end())
583 t = f->second(typeJson, cache);
589 cache.registerType(t);
599 for (
auto &typeJson : typesJson)
600 _typeTable.push_back(
parseType(typeJson));
607 Manifest::Manifest(Context &
ctxt,
const std::string &jsonManifest)
608 : impl(new
Impl(
ctxt, jsonManifest)) {}
610 Manifest::~Manifest() {
delete impl; }
612 uint32_t Manifest::getApiVersion()
const {
613 return impl->at(
"apiVersion").get<uint32_t>();
616 std::vector<ModuleInfo> Manifest::getModuleInfos()
const {
617 std::vector<ModuleInfo> ret;
618 for (
auto &[symbol, info] : impl->getSymbolInfo())
623 Accelerator *Manifest::buildAccelerator(AcceleratorConnection &acc)
const {
625 return acc.takeOwnership(impl->buildAccelerator(acc));
626 }
catch (
const std::exception &e) {
627 std::string msg =
"malformed manifest: " + std::string(e.what());
628 if (getApiVersion() == 0)
629 msg +=
" (schema version 0 is not considered stable)";
630 throw std::runtime_error(msg);
634 const std::vector<const Type *> &Manifest::getTypeTable()
const {
635 return impl->getTypeTable();
644 auto printAny = [&os](std::any a) {
645 if (
auto *c = std::any_cast<Constant>(&a))
646 a = std::any_cast<Constant>(a).value;
648 const std::type_info &t = a.type();
649 if (t ==
typeid(std::string))
650 os << std::any_cast<std::string>(a);
651 else if (t ==
typeid(int64_t))
652 os << std::any_cast<int64_t>(a);
653 else if (t ==
typeid(uint64_t))
654 os << std::any_cast<uint64_t>(a);
655 else if (t ==
typeid(
double))
656 os << std::any_cast<double>(a);
657 else if (t ==
typeid(
bool))
658 os << std::any_cast<bool>(a);
659 else if (t ==
typeid(std::nullptr_t))
666 os << *m.name <<
" ";
668 os << *m.version <<
" ";
669 if (m.repo || m.commitHash) {
674 os <<
"@" << *m.commitHash;
678 os <<
": " << *m.summary;
681 if (!m.constants.empty()) {
682 os <<
" Constants:\n";
683 for (
auto &e : m.constants) {
684 os <<
" " << e.first <<
": ";
690 if (!m.extra.empty()) {
691 os <<
" Extra metadata:\n";
692 for (
auto &e : m.extra) {
693 os <<
" " << e.first <<
": ";
704 ret.insert(ret.end(), b.begin(), b.end());
708 std::string AppIDPath::toStr()
const {
709 std::ostringstream os;
720 if (a.size() != b.size())
721 return a.size() < b.size();
722 for (
size_t i = 0, e = a.size(); i < e; ++i)
729 std::ostream &
operator<<(std::ostream &os,
const AppID &
id) {
732 os <<
"[" << *
id.idx <<
"]";
735 std::ostream &
operator<<(std::ostream &os,
const AppIDPath &path) {
736 for (
size_t i = 0, e = path.size(); i < e; ++i) {
assert(baseType &&"element must be base type")
static ParseResult parseType(Type &result, StringRef name, AsmParser &parser)
Parse a type defined by this dialect.
AcceleratorConnection::ServiceTable ServiceTable
static AppIDPath parseIDPath(const nlohmann::json &jsonIDPath)
std::ostream & operator<<(std::ostream &os, const ModuleInfo &m)
static AppID parseIDChecked(const nlohmann::json &jsonID)
static std::optional< AppID > parseID(const nlohmann::json &jsonID)
static ServicePortDesc parseServicePort(const nlohmann::json &jsonPort)
Impl(Context &ctxt, const std::string &jsonManifest)
const Type * parseType(const nlohmann::json &typeJson)
auto at(const std::string &key) const
void populateTypes(const nlohmann::json &typesJson)
Parse all the types and populate the types table.
std::optional< ModuleInfo > getModInfo(const nlohmann::json &) const
std::vector< std::unique_ptr< BundlePort > > getBundlePorts(AcceleratorConnection &acc, AppIDPath idPath, const ServiceTable &activeServices, const nlohmann::json &instJson) const
Get the bundle ports for the instance at 'idPath' and specified in 'instJson'.
std::vector< std::unique_ptr< Instance > > getChildInstances(AppIDPath idPath, AcceleratorConnection &acc, const ServiceTable &activeServices, const nlohmann::json &instJson) const
Build the set of child instances (recursively) for the module instance description.
nlohmann::json manifestJson
const std::vector< const Type * > & getTypeTable() const
Get the ordered list of types from the manifest.
std::vector< services::Service * > getServices(AppIDPath idPath, AcceleratorConnection &, const nlohmann::json &, ServiceTable &activeServices) const
Get all the services in the description of an instance.
std::map< std::string, const ModuleInfo > symbolInfoCache
std::any getAny(const nlohmann::json &value) const
Convert the json value to a 'std::any', which can be exposed outside of this file.
void parseModuleMetadata(ModuleInfo &info, const nlohmann::json &mod) const
std::unique_ptr< Accelerator > buildAccelerator(AcceleratorConnection &acc) const
Build a dynamic API for the Accelerator connection 'acc' based on the manifest stored herein.
void parseModuleConsts(ModuleInfo &info, const nlohmann::json &mod) const
services::Service * getService(AppIDPath idPath, AcceleratorConnection &, const nlohmann::json &, ServiceTable &activeServices) const
Get a Service for the service specified in 'json'.
void scanServiceDecls(AcceleratorConnection &, const nlohmann::json &, ServiceTable &) const
Go through the "service_decls" section of the manifest and populate the services table as appropriate...
std::optional< const Type * > getType(Type::ID id) const
const std::map< std::string, const ModuleInfo > & getSymbolInfo() const
std::vector< const Type * > _typeTable
std::unique_ptr< Instance > getChildInstance(AppIDPath idPath, AcceleratorConnection &acc, ServiceTable activeServices, const nlohmann::json &childJson) const
Get a single child instance.
Direction
The direction of a Component or Cell port.
bool operator<(const DictEntry &entry, const DictEntry &other)
FVInt operator+(uint64_t a, const FVInt &b)
std::map< std::string, std::any > ServiceImplDetails
std::vector< HWClientDetail > HWClientDetails
std::optional< uint32_t > idx