19 #include <nlohmann/json.hpp>
37 friend class ::esi::Manifest;
45 std::optional<ModuleInfo>
getModInfo(
const nlohmann::json &)
const;
55 const nlohmann::json &,
60 std::vector<services::Service *>
66 std::vector<std::unique_ptr<BundlePort>>
69 const nlohmann::json &instJson)
const;
73 std::vector<std::unique_ptr<Instance>>
76 const nlohmann::json &instJson)
const;
80 std::unique_ptr<Instance>
83 const nlohmann::json &childJson)
const;
93 std::unique_ptr<Accelerator>
117 std::optional<uint32_t> idx;
118 if (jsonID.contains(
"index"))
119 idx = jsonID.at(
"index").get<uint32_t>();
120 return AppID(jsonID.at(
"name").get<std::string>(), idx);
125 for (
auto &
id : jsonIDPath)
132 jsonPort.at(
"inner").get<std::string>()};
137 static std::any
getAny(
const nlohmann::json &value) {
138 auto getObject = [](
const nlohmann::json &
json) {
139 std::map<std::string, std::any> ret;
140 for (
auto &e :
json.items())
141 ret[e.key()] =
getAny(e.value());
145 auto getArray = [](
const nlohmann::json &
json) {
146 std::vector<std::any> ret;
152 if (value.is_string())
153 return value.get<std::string>();
154 else if (value.is_number_integer())
155 return value.get<int64_t>();
156 else if (value.is_number_unsigned())
157 return value.get<uint64_t>();
158 else if (value.is_number_float())
159 return value.get<
double>();
160 else if (value.is_boolean())
161 return value.get<
bool>();
162 else if (value.is_null())
163 return value.get<std::nullptr_t>();
164 else if (value.is_object())
165 return getObject(value);
166 else if (value.is_array())
167 return getArray(value);
169 throw std::runtime_error(
"Unknown type in manifest: " + value.dump(2));
174 std::map<std::string, std::any> extras;
175 for (
auto &extra : mod.items())
176 if (extra.key() !=
"name" && extra.key() !=
"summary" &&
177 extra.key() !=
"version" && extra.key() !=
"repo" &&
178 extra.key() !=
"commitHash" && extra.key() !=
"symbolRef")
179 extras[extra.key()] =
getAny(extra.value());
181 auto value = [&](
const std::string &key) -> std::optional<std::string> {
182 auto f = mod.find(key);
187 return ModuleInfo{value(
"name"), value(
"summary"), value(
"version"),
188 value(
"repo"), value(
"commitHash"), extras};
197 manifestJson = nlohmann::ordered_json::parse(manifestStr);
205 std::unique_ptr<Accelerator>
210 auto svcDecls = manifestJson.at(
"service_decls");
211 scanServiceDecls(acc, svcDecls, activeSvcs);
214 auto designJson = manifestJson.at(
"design");
215 std::vector<services::Service *> services =
216 getServices({}, acc, designJson, activeSvcs);
219 auto ports = getBundlePorts(acc, {}, activeSvcs, designJson);
221 return make_unique<Accelerator>(
222 getModInfo(designJson),
223 getChildInstances({}, acc, activeSvcs, designJson), services, ports);
226 std::optional<ModuleInfo>
228 auto instOfIter =
json.find(
"inst_of");
229 if (instOfIter ==
json.end())
231 auto f = symbolInfoCache.find(instOfIter.value());
232 if (f != symbolInfoCache.end())
238 const nlohmann::json &svcDecls,
240 for (
auto &svcDecl : svcDecls) {
241 if (
auto f = svcDecl.find(
"type_name"); f != svcDecl.end()) {
244 for (
auto &
detail : svcDecl.items())
249 services::ServiceRegistry::lookupServiceType(f.value());
253 activeServices[svcDecl.at(
"symbol")] = svc;
258 std::vector<std::unique_ptr<Instance>>
261 const nlohmann::json &instJson)
const {
262 std::vector<std::unique_ptr<Instance>> ret;
263 auto childrenIter = instJson.find(
"children");
264 if (childrenIter == instJson.end())
266 for (
auto &child : childrenIter.value())
267 ret.emplace_back(getChildInstance(idPath, acc, activeServices, child));
271 std::unique_ptr<Instance>
274 const nlohmann::json &child)
const {
276 idPath.push_back(childID);
278 std::vector<services::Service *> services =
279 getServices(idPath, acc, child, activeServices);
281 auto children = getChildInstances(idPath, acc, activeServices, child);
282 auto ports = getBundlePorts(acc, idPath, activeServices, child);
283 return make_unique<Instance>(
parseID(child.at(
"app_id")), getModInfo(child),
284 std::move(children), services, ports);
289 const nlohmann::json &svcJson,
293 idPath.push_back(
id);
297 for (
auto &client : svcJson.at(
"client_details")) {
299 for (
auto &
detail : client.items()) {
300 if (
detail.key() ==
"relAppIDPath")
302 else if (
detail.key() ==
"port")
307 clientDetails.push_back(clientDetail);
312 std::string implName;
314 for (
auto &
detail : svcJson.items()) {
315 if (
detail.key() ==
"appID" ||
detail.key() ==
"client_details")
317 if (
detail.key() ==
"serviceImplName")
318 implName =
detail.value();
319 else if (
detail.key() ==
"service")
320 service =
detail.value().get<std::string>().substr(1);
328 services::ServiceRegistry::lookupServiceType(service);
330 acc.
getService(svcType, idPath, implName, svcDetails, clientDetails);
333 activeServices[service] = svc;
337 std::vector<services::Service *>
339 const nlohmann::json &svcsJson,
341 std::vector<services::Service *> ret;
342 auto contentsIter = svcsJson.find(
"contents");
343 if (contentsIter == svcsJson.end())
346 for (
auto &content : contentsIter.value())
347 if (content.at(
"class") ==
"service")
348 ret.emplace_back(getService(idPath, acc, content, activeServices));
352 std::vector<std::unique_ptr<BundlePort>>
355 const nlohmann::json &instJson)
const {
356 std::vector<std::unique_ptr<BundlePort>> ret;
357 auto contentsIter = instJson.find(
"contents");
358 if (contentsIter == instJson.end())
361 for (
auto &content : contentsIter.value()) {
362 if (content.at(
"class") !=
"client_port")
366 std::string serviceName =
"";
367 if (
auto f = content.find(
"servicePort"); f != content.end())
369 auto svcIter = activeServices.find(serviceName);
370 if (svcIter == activeServices.end()) {
373 if (svcIter = activeServices.find(
""); svcIter == activeServices.end())
374 throw std::runtime_error(
375 "Malformed manifest: could not find active service '" +
380 std::string typeName = content.at(
"bundleType").at(
"circt_name");
381 auto type = getType(typeName);
383 throw std::runtime_error(
384 "Malformed manifest: could not find port type '" + typeName +
"'");
387 throw std::runtime_error(
"Malformed manifest: type '" + typeName +
388 "' is not a bundle type");
390 idPath.push_back(
parseID(content.at(
"appID")));
391 std::map<std::string, ChannelPort &> portChannels =
395 svc->
getPort(idPath, bundleType, portChannels, acc);
397 ret.emplace_back(svcPort);
399 ret.emplace_back(
new BundlePort(idPath.back(), portChannels));
411 assert(typeJson.at(
"mnemonic") ==
"bundle");
413 std::vector<std::tuple<std::string, BundleType::Direction, const Type *>>
415 for (
auto &chanJson : typeJson[
"channels"]) {
416 std::string dirStr = chanJson.at(
"direction");
419 dir = BundleType::Direction::To;
420 else if (dirStr ==
"from")
421 dir = BundleType::Direction::From;
423 throw std::runtime_error(
"Malformed manifest: unknown direction '" +
425 channels.emplace_back(chanJson.at(
"name"), dir,
428 return new BundleType(typeJson.at(
"circt_name"), channels);
432 assert(typeJson.at(
"mnemonic") ==
"channel");
437 Type *parseInt(
const nlohmann::json &typeJson,
Context &cache) {
438 assert(typeJson.at(
"mnemonic") ==
"int");
439 std::string sign = typeJson.at(
"signedness");
440 uint64_t
width = typeJson.at(
"hw_bitwidth");
441 Type::ID
id = typeJson.at(
"circt_name");
443 if (sign ==
"signed")
445 else if (sign ==
"unsigned")
447 else if (sign ==
"signless" &&
width == 0)
450 else if (sign ==
"signless" &&
width > 0)
453 throw std::runtime_error(
"Malformed manifest: unknown sign '" + sign +
"'");
457 assert(typeJson.at(
"mnemonic") ==
"struct");
458 std::vector<std::pair<std::string, const Type *>> fields;
459 for (
auto &fieldJson : typeJson[
"fields"])
460 fields.emplace_back(fieldJson.at(
"name"),
462 return new StructType(typeJson.at(
"circt_name"), fields);
466 assert(typeJson.at(
"mnemonic") ==
"array");
467 uint64_t size = typeJson.at(
"size");
468 return new ArrayType(typeJson.at(
"circt_name"),
469 parseType(typeJson.at(
"element"), cache), size);
472 using TypeParser = std::function<
Type *(
const nlohmann::json &,
Context &)>;
473 const std::map<std::string_view, TypeParser> typeParsers = {
474 {
"bundle", parseBundleType},
475 {
"channel", parseChannelType},
477 [](
const nlohmann::json &typeJson,
Context &cache) {
478 return new AnyType(typeJson.at(
"circt_name"));
481 {
"struct", parseStruct},
482 {
"array", parseArray},
489 std::string circt_name = typeJson.at(
"circt_name");
490 if (std::optional<const Type *> t = cache.
getType(circt_name))
493 std::string mnemonic = typeJson.at(
"mnemonic");
495 auto f = typeParsers.find(mnemonic);
496 if (f != typeParsers.end())
497 t = f->second(typeJson, cache);
500 t =
new Type(circt_name);
513 for (
auto &typeJson : typesJson)
514 _typeTable.push_back(
parseType(typeJson));
521 Manifest::Manifest(
Context &
ctxt,
const std::string &jsonManifest)
522 : impl(new
Impl(
ctxt, jsonManifest)) {}
527 return impl->
at(
"api_version").get<uint32_t>();
531 std::vector<ModuleInfo> ret;
532 for (
auto &mod :
impl->
at(
"symbols"))
537 std::unique_ptr<Accelerator>
552 auto printAny = [&os](std::any a) {
553 const std::type_info &t = a.type();
554 if (t ==
typeid(std::string))
555 os << std::any_cast<std::string>(a);
556 else if (t ==
typeid(int64_t))
557 os << std::any_cast<int64_t>(a);
558 else if (t ==
typeid(uint64_t))
559 os << std::any_cast<uint64_t>(a);
560 else if (t ==
typeid(
double))
561 os << std::any_cast<double>(a);
562 else if (t ==
typeid(
bool))
563 os << std::any_cast<bool>(a);
564 else if (t ==
typeid(std::nullptr_t))
571 os << *m.
name <<
" ";
586 if (!m.
extra.empty()) {
587 os <<
" Extra metadata:\n";
588 for (
auto &e : m.
extra) {
589 os <<
" " << e.first <<
": ";
600 ret.insert(ret.end(), b.begin(), b.end());
605 std::ostringstream os;
616 if (a.size() != b.size())
617 return a.size() < b.size();
618 for (
size_t i = 0, e = a.size(); i < e; ++i)
628 os <<
"[" << *
id.idx <<
"]";
632 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.
std::map< std::string, services::Service * > ServiceTable
static AppIDPath parseIDPath(const nlohmann::json &jsonIDPath)
static std::any getAny(const nlohmann::json &value)
Convert the json value to a 'std::any', which can be exposed outside of this file.
std::ostream & operator<<(std::ostream &os, const ModuleInfo &m)
static ModuleInfo parseModuleInfo(const nlohmann::json &mod)
static ServicePortDesc parseServicePort(const nlohmann::json &jsonPort)
static AppID parseID(const nlohmann::json &jsonID)
auto at(const std::string &key) const
nlohmann::json manifestJson
const Type * parseType(const nlohmann::json &typeJson)
std::vector< const Type * > _typeTable
services::Service * getService(AppIDPath idPath, AcceleratorConnection &, const nlohmann::json &, ServiceTable &activeServices) const
Get a Service for the service specified in 'json'.
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.
std::optional< ModuleInfo > getModInfo(const nlohmann::json &) const
const std::vector< const Type * > & getTypeTable() const
Get the ordered list of types from the manifest.
void scanServiceDecls(AcceleratorConnection &, const nlohmann::json &, ServiceTable &) const
Go through the "service_decls" section of the manifest and populate the services table as appropriate...
Impl(Context &ctxt, const std::string &jsonManifest)
std::optional< const Type * > getType(Type::ID id) const
std::unique_ptr< Accelerator > buildAccelerator(AcceleratorConnection &acc) const
Build a dynamic API for the Accelerator connection 'acc' based on the manifest stored herein.
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::map< std::string, ModuleInfo > symbolInfoCache
void populateTypes(const nlohmann::json &typesJson)
Parse all the types and populate the types table.
std::unique_ptr< Instance > getChildInstance(AppIDPath idPath, AcceleratorConnection &acc, ServiceTable activeServices, const nlohmann::json &childJson) const
Get a single child instance.
std::vector< services::Service * > getServices(AppIDPath idPath, AcceleratorConnection &, const nlohmann::json &, ServiceTable &activeServices) const
Get all the services in the description of an instance.
Abstract class representing a connection to an accelerator.
ServiceClass * getService(AppIDPath id={}, std::string implName={}, ServiceImplDetails details={}, HWClientDetails clients={})
Get a typed reference to a particular service type.
virtual std::map< std::string, ChannelPort & > requestChannelsFor(AppIDPath, const BundleType *)=0
Request the host side channel ports for a particular instance (identified by the AppID path).
The "any" type is a special type which can be used to represent any type, as identified by the type i...
std::string toStr() const
AppIDPath operator+(const AppIDPath &b)
Arrays have a compile time specified (static) size and an element type.
Bits are just an array of bits.
Services provide connections to 'bundles' – collections of named, unidirectional communication channe...
Bundles represent a collection of channels.
Channels are the basic communication primitives.
AcceleratorConnections, Accelerators, and Manifests must all share a context.
std::optional< const Type * > getType(Type::ID id) const
Resolve a type id to the type.
void registerType(Type *type)
Register a type with the context. Takes ownership of the pointer type.
std::unique_ptr< Accelerator > buildAccelerator(AcceleratorConnection &acc) const
std::vector< ModuleInfo > getModuleInfos() const
uint32_t getApiVersion() const
const std::vector< const Type * > & getTypeTable() const
The Type Table is an ordered list of types.
Structs are an ordered collection of fields, each with a name and a type.
Root class of the ESI type system.
The "void" type is a special type which can be used to represent no type.
Add a custom interface to a service client at a particular point in the design hierarchy.
Parent class of all APIs modeled as 'services'.
const std::type_info & Type
virtual ServicePort * getPort(AppIDPath id, const BundleType *type, const std::map< std::string, ChannelPort & > &, AcceleratorConnection &) const
Get specialized port for this service to attach to the given appid path.
std::map< std::string, std::any > ServiceImplDetails
bool operator<(const AppID &a, const AppID &b)
std::vector< HWClientDetail > HWClientDetails
std::optional< uint32_t > idx
A description of a hardware client.
std::map< std::string, std::any > implOptions
const std::optional< std::string > commitHash
const std::optional< std::string > repo
const std::optional< std::string > version
const std::optional< std::string > summary
const std::map< std::string, std::any > extra
const std::optional< std::string > name
A description of a service port.