Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ESI][Runtime] Parse and expose manifest constants #7492

Merged
merged 3 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions integration_test/Dialect/ESI/runtime/loopback.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// RUN: circt-opt %s --esi-connect-services --esi-appid-hier=top=top --esi-build-manifest=top=top --esi-clean-metadata > %t4.mlir
// RUN: circt-opt %t4.mlir --lower-esi-to-physical --lower-esi-bundles --lower-esi-ports --lower-esi-to-hw=platform=cosim --lower-seq-to-sv --lower-hwarith-to-hw --canonicalize --export-split-verilog -o %t3.mlir
// RUN: cd ..
// RUN: esiquery trace w:%t6/esi_system_manifest.json info | FileCheck %s --check-prefix=QUERY-INFO
// RUN: esiquery trace w:%t6/esi_system_manifest.json hier | FileCheck %s --check-prefix=QUERY-HIER
// RUN: %python %s.py trace w:%t6/esi_system_manifest.json
// RUN: esi-cosim.py --source %t6 --top top -- %python %s.py cosim env
Expand Down Expand Up @@ -95,6 +96,7 @@ hw.module @CallableFunc1() {
}

esi.manifest.sym @Loopback name "LoopbackIP" version "v0.0" summary "IP which simply echos bytes" {foo=1}
esi.manifest.constants @Loopback {depth=5:ui32}

hw.module @top(in %clk: !seq.clock, in %rst: i1) {
esi.service.instance #esi.appid<"cosim"> svc @HostComms impl as "cosim" (%clk, %rst) : (!seq.clock, i1) -> ()
Expand All @@ -107,6 +109,16 @@ hw.module @top(in %clk: !seq.clock, in %rst: i1) {
hw.instance "loopback_array" @LoopbackArray() -> ()
}

// QUERY-INFO: API version: 0
// QUERY-INFO: ********************************
// QUERY-INFO: * Module information
// QUERY-INFO: ********************************
// QUERY-INFO: - LoopbackIP v0.0 : IP which simply echos bytes
// QUERY-INFO: Constants:
// QUERY-INFO: depth: 5
// QUERY-INFO: Extra metadata:
// QUERY-INFO: foo: 1

// QUERY-HIER: ********************************
// QUERY-HIER: * Design hierarchy
// QUERY-HIER: ********************************
Expand Down
7 changes: 7 additions & 0 deletions integration_test/Dialect/ESI/runtime/loopback.mlir.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@
for esiType in m.type_table:
print(f"{esiType}")

for info in m.module_infos:
print(f"{info.name}")
for const_name, const in info.constants.items():
print(f" {const_name}: {const.value} {const.type}")
if info.name == "LoopbackIP" and const_name == "depth":
assert const.value == 5

d = acc.build_accelerator()

loopback = d.children[esiaccel.AppID("loopback_inst", 0)]
Expand Down
7 changes: 7 additions & 0 deletions lib/Dialect/ESI/runtime/cpp/include/esi/Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <vector>

namespace esi {
class Type;

//===----------------------------------------------------------------------===//
// Common accelerator description types.
Expand Down Expand Up @@ -53,12 +54,18 @@ class AppIDPath : public std::vector<AppID> {
};
bool operator<(const AppIDPath &a, const AppIDPath &b);

struct Constant {
std::any value;
std::optional<const Type *> type;
};

struct ModuleInfo {
std::optional<std::string> name;
std::optional<std::string> summary;
std::optional<std::string> version;
std::optional<std::string> repo;
std::optional<std::string> commitHash;
std::map<std::string, Constant> constants;
std::map<std::string, std::any> extra;
};

Expand Down
85 changes: 62 additions & 23 deletions lib/Dialect/ESI/runtime/cpp/lib/Manifest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ class Manifest::Impl {
return ctxt.getType(id);
}

std::any getAny(const nlohmann::json &value) const;
void parseModuleMetadata(ModuleInfo &info, const nlohmann::json &mod) const;
void parseModuleConsts(ModuleInfo &info, const nlohmann::json &mod) const;

// The parsed json.
nlohmann::json manifestJson;
// Cache the module info for each symbol.
Expand Down Expand Up @@ -138,42 +142,50 @@ static ServicePortDesc parseServicePort(const nlohmann::json &jsonPort) {

/// Convert the json value to a 'std::any', which can be exposed outside of this
/// file.
static std::any getAny(const nlohmann::json &value) {
auto getObject = [](const nlohmann::json &json) {
std::any Manifest::Impl::getAny(const nlohmann::json &value) const {
auto getObject = [this](const nlohmann::json &json) -> std::any {
std::map<std::string, std::any> ret;
for (auto &e : json.items())
ret[e.key()] = getAny(e.value());
return ret;
};

auto getArray = [](const nlohmann::json &json) {
auto getArray = [this](const nlohmann::json &json) -> std::any {
std::vector<std::any> ret;
for (auto &e : json)
ret.push_back(getAny(e));
return ret;
};

if (value.is_string())
return value.get<std::string>();
else if (value.is_number_integer())
return value.get<int64_t>();
else if (value.is_number_unsigned())
return value.get<uint64_t>();
else if (value.is_number_float())
return value.get<double>();
else if (value.is_boolean())
return value.get<bool>();
else if (value.is_null())
return value.get<std::nullptr_t>();
else if (value.is_object())
return getObject(value);
else if (value.is_array())
return getArray(value);
else
throw std::runtime_error("Unknown type in manifest: " + value.dump(2));
auto getValue = [&](const nlohmann::json &innerValue) -> std::any {
if (innerValue.is_string())
return innerValue.get<std::string>();
else if (innerValue.is_number_integer())
return innerValue.get<int64_t>();
else if (innerValue.is_number_unsigned())
return innerValue.get<uint64_t>();
else if (innerValue.is_number_float())
return innerValue.get<double>();
else if (innerValue.is_boolean())
return innerValue.get<bool>();
else if (innerValue.is_null())
return innerValue.get<std::nullptr_t>();
else if (innerValue.is_object())
return getObject(innerValue);
else if (innerValue.is_array())
return getArray(innerValue);
else
throw std::runtime_error("Unknown type in manifest: " +
innerValue.dump(2));
};

if (!value.is_object() || !value.contains("type") || !value.contains("value"))
return getValue(value);
return Constant{getValue(value.at("value")), getType(value.at("type"))};
}

static void parseModuleInfo(ModuleInfo &info, const nlohmann::json &mod) {
void Manifest::Impl::parseModuleMetadata(ModuleInfo &info,
const nlohmann::json &mod) const {
for (auto &extra : mod.items())
if (extra.key() != "name" && extra.key() != "summary" &&
extra.key() != "version" && extra.key() != "repo" &&
Expand All @@ -193,6 +205,19 @@ static void parseModuleInfo(ModuleInfo &info, const nlohmann::json &mod) {
info.commitHash = value("commitHash");
}

void Manifest::Impl::parseModuleConsts(ModuleInfo &info,
const nlohmann::json &mod) const {
for (auto &item : mod.items()) {
std::any value = getAny(item.value());
auto *c = std::any_cast<Constant>(&value);
if (c)
info.constants[item.key()] = *c;
else
// If the value isn't a "proper" constant, present it as one with no type.
info.constants[item.key()] = Constant{value, std::nullopt};
}
}

//===----------------------------------------------------------------------===//
// Manifest::Impl class implementation.
//===----------------------------------------------------------------------===//
Expand All @@ -209,7 +234,9 @@ Manifest::Impl::Impl(Context &ctxt, const std::string &manifestStr)
for (auto &mod : manifestJson.at("symbols")) {
ModuleInfo info;
if (mod.contains("sym_info"))
parseModuleInfo(info, mod);
parseModuleMetadata(info, mod.at("sym_info"));
if (mod.contains("sym_consts"))
parseModuleConsts(info, mod.at("sym_consts"));
symbolInfoCache.insert(make_pair(mod.at("symbol"), info));
}
} catch (const std::exception &e) {
Expand Down Expand Up @@ -577,6 +604,9 @@ const std::vector<const Type *> &Manifest::getTypeTable() const {
// Print a module info, including the extra metadata.
std::ostream &operator<<(std::ostream &os, const ModuleInfo &m) {
auto printAny = [&os](std::any a) {
if (auto *c = std::any_cast<Constant>(&a))
a = std::any_cast<Constant>(a).value;

const std::type_info &t = a.type();
if (t == typeid(std::string))
os << std::any_cast<std::string>(a);
Expand Down Expand Up @@ -610,6 +640,15 @@ std::ostream &operator<<(std::ostream &os, const ModuleInfo &m) {
os << ": " << *m.summary;
os << "\n";

if (!m.constants.empty()) {
os << " Constants:\n";
for (auto &e : m.constants) {
os << " " << e.first << ": ";
printAny(e.second);
os << "\n";
}
}

if (!m.extra.empty()) {
os << " Extra metadata:\n";
for (auto &e : m.extra) {
Expand Down
45 changes: 45 additions & 0 deletions lib/Dialect/ESI/runtime/python/esiaccel/esiCppAccel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,45 @@ struct polymorphic_type_hook<ChannelPort> {
return port;
}
};

namespace detail {
/// Pybind11 doesn't have a built-in type caster for std::any
/// (https://github.com/pybind/pybind11/issues/1590). We must provide one which
/// knows about all of the potential types which the any might be.
template <>
struct type_caster<std::any> {
public:
PYBIND11_TYPE_CASTER(std::any, const_name("object"));

static handle cast(std::any src, return_value_policy /* policy */,
handle /* parent */) {
const std::type_info &t = src.type();
if (t == typeid(std::string))
return py::str(std::any_cast<std::string>(src));
else if (t == typeid(int64_t))
return py::int_(std::any_cast<int64_t>(src));
else if (t == typeid(uint64_t))
return py::int_(std::any_cast<uint64_t>(src));
else if (t == typeid(double))
return py::float_(std::any_cast<double>(src));
else if (t == typeid(bool))
return py::bool_(std::any_cast<bool>(src));
else if (t == typeid(std::nullptr_t))
return py::none();
return py::none();
}
};
} // namespace detail
} // namespace pybind11

/// Resolve a Type to the Python wrapper object.
py::object getPyType(std::optional<const Type *> t) {
py::object typesModule = py::module_::import("esiaccel.types");
if (!t)
return py::none();
return typesModule.attr("_get_esi_type")(*t);
}

// NOLINTNEXTLINE(readability-identifier-naming)
PYBIND11_MODULE(esiCppAccel, m) {
py::class_<Type>(m, "Type")
Expand Down Expand Up @@ -75,6 +112,12 @@ PYBIND11_MODULE(esiCppAccel, m) {
py::return_value_policy::reference)
.def_property_readonly("size", &ArrayType::getSize);

py::class_<Constant>(m, "Constant")
.def_property_readonly("value", [](Constant &c) { return c.value; })
.def_property_readonly(
"type", [](Constant &c) { return getPyType(*c.type); },
py::return_value_policy::reference);

py::class_<ModuleInfo>(m, "ModuleInfo")
.def_property_readonly("name", [](ModuleInfo &info) { return info.name; })
.def_property_readonly("summary",
Expand All @@ -84,6 +127,8 @@ PYBIND11_MODULE(esiCppAccel, m) {
.def_property_readonly("repo", [](ModuleInfo &info) { return info.repo; })
.def_property_readonly("commit_hash",
[](ModuleInfo &info) { return info.commitHash; })
.def_property_readonly("constants",
[](ModuleInfo &info) { return info.constants; })
// TODO: "extra" field.
.def("__repr__", [](ModuleInfo &info) {
std::string ret;
Expand Down
Loading