mirror of https://github.com/llvm/circt.git
[PyCDE] Add ESI metadata to modules (#6597)
Users can either explicitly specify metadata or it can be automatically generated.
This commit is contained in:
parent
01022f7f7b
commit
d0879a7663
|
@ -8,6 +8,7 @@ import pycde
|
|||
from pycde import (AppID, Clock, Input, Module, generator)
|
||||
from pycde.esi import DeclareRandomAccessMemory, ServiceDecl
|
||||
from pycde.bsp import cosim, xrt
|
||||
from pycde.module import Metadata
|
||||
from pycde.types import Bits
|
||||
|
||||
import sys
|
||||
|
@ -22,9 +23,19 @@ class MemComms:
|
|||
read = ServiceDecl.From(RamI64x8.read.type)
|
||||
|
||||
|
||||
class Dummy(Module):
|
||||
"""To test completely automated metadata collection."""
|
||||
|
||||
@generator
|
||||
def construct(ports):
|
||||
pass
|
||||
|
||||
|
||||
class MemWriter(Module):
|
||||
"""Write to address 3 the contents of address 2."""
|
||||
|
||||
metadata = Metadata(version="0.1", misc={"numWriters": 1, "style": "stupid"})
|
||||
|
||||
clk = Clock()
|
||||
rst = Input(Bits(1))
|
||||
|
||||
|
@ -54,7 +65,8 @@ def Top(xrt: bool):
|
|||
|
||||
@generator
|
||||
def construct(ports):
|
||||
MemWriter(clk=ports.clk, rst=ports.rst)
|
||||
Dummy(appid=AppID("dummy"))
|
||||
MemWriter(clk=ports.clk, rst=ports.rst, appid=AppID("mem_writer"))
|
||||
|
||||
# We don't have support for host--device channel communication on XRT yet.
|
||||
if not xrt:
|
||||
|
@ -82,5 +94,6 @@ if __name__ == "__main__":
|
|||
s = pycde.System(bsp(Top(is_xrt)),
|
||||
name="ESIMem",
|
||||
output_directory=sys.argv[1])
|
||||
s.generate()
|
||||
s.compile()
|
||||
s.package()
|
||||
|
|
|
@ -15,15 +15,18 @@ mem_read_addr.connect()
|
|||
mem_read_data = d.ports[esi.AppID("read")].read_port("data")
|
||||
mem_read_data.connect()
|
||||
|
||||
# Baseline
|
||||
m = acc.manifest()
|
||||
if (platform == "cosim"):
|
||||
# Baseline
|
||||
m = acc.manifest()
|
||||
|
||||
# MMIO method
|
||||
acc.cpp_accel.set_manifest_method(esi.esiCppAccel.ManifestMMIO)
|
||||
m_alt = acc.manifest()
|
||||
assert len(m.type_table) == len(m_alt.type_table)
|
||||
|
||||
info = m.module_infos
|
||||
assert len(info) == 3
|
||||
assert info[1].name == "Dummy"
|
||||
|
||||
|
||||
def read(addr: int) -> bytearray:
|
||||
mem_read_addr.write([addr])
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import List, Optional, Set, Tuple, Dict
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, List, Optional, Set, Tuple, Dict
|
||||
|
||||
from .common import (AppID, Clock, Input, Output, PortError, _PyProxy, Reset)
|
||||
from .support import (get_user_loc, _obj_to_attribute, create_type_string,
|
||||
|
@ -18,6 +19,7 @@ from .circt.support import BackedgeBuilder, attribute_to_var
|
|||
import builtins
|
||||
from contextvars import ContextVar
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
|
||||
# A memoization table for module parameterization function calls.
|
||||
|
@ -396,9 +398,69 @@ class ModuleBuilder(ModuleLikeBuilderBase):
|
|||
return sys._create_circt_mod(self)
|
||||
return ret
|
||||
|
||||
def add_metadata(self, sys, symbol: str, meta: Optional[Metadata]):
|
||||
"""Add the metadata to the IR so it potentially gets included in the
|
||||
manifest. (It'll only be included if one of the instances has an appid.) If
|
||||
user did not specify the metadata (or components thereof), attempt to fill
|
||||
them in automatically:
|
||||
- Name defaults to the module name.
|
||||
- Summary defaults to the module docstring.
|
||||
- If GitPython is installed, the commit hash and repo are automatically
|
||||
generated if neither are specified.
|
||||
"""
|
||||
|
||||
from .dialects.esi import esi
|
||||
|
||||
if meta is None:
|
||||
meta = Metadata()
|
||||
elif not isinstance(meta, Metadata):
|
||||
raise TypeError("Module metadata must be of type Metadata")
|
||||
|
||||
if meta.name is None:
|
||||
meta.name = self.modcls.__name__
|
||||
|
||||
try:
|
||||
# Attempt to automatically generate repo and commit hash using GitPython.
|
||||
if meta.repo is None and meta.commit_hash is None:
|
||||
import git
|
||||
import inspect
|
||||
modclsmodule = inspect.getmodule(self.modcls)
|
||||
if modclsmodule is not None:
|
||||
r = git.Repo(os.path.dirname(modclsmodule.__file__),
|
||||
search_parent_directories=True)
|
||||
if r is not None:
|
||||
meta.repo = r.remotes.origin.url
|
||||
meta.commit_hash = r.head.object.hexsha
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if meta.summary is None and self.modcls.__doc__ is not None:
|
||||
meta.summary = self.modcls.__doc__
|
||||
|
||||
with ir.InsertionPoint(sys.mod.body):
|
||||
meta_op = esi.SymbolMetadataOp(
|
||||
symbolRef=ir.FlatSymbolRefAttr.get(symbol),
|
||||
name=ir.StringAttr.get(meta.name),
|
||||
repo=ir.StringAttr.get(meta.repo) if meta.repo is not None else None,
|
||||
commitHash=ir.StringAttr.get(meta.commit_hash)
|
||||
if meta.commit_hash is not None else None,
|
||||
version=ir.StringAttr.get(meta.version)
|
||||
if meta.version is not None else None,
|
||||
summary=ir.StringAttr.get(meta.summary)
|
||||
if meta.summary is not None else None)
|
||||
if meta.misc is not None:
|
||||
for k, v in meta.misc.items():
|
||||
meta_op.attributes[k] = _obj_to_attribute(v)
|
||||
|
||||
def create_op(self, sys, symbol):
|
||||
"""Callback for creating a module op."""
|
||||
|
||||
if hasattr(self.modcls, "metadata"):
|
||||
meta = self.modcls.metadata
|
||||
self.add_metadata(sys, symbol, meta)
|
||||
else:
|
||||
self.add_metadata(sys, symbol, None)
|
||||
|
||||
if len(self.generators) > 0:
|
||||
if hasattr(self, "parameters") and self.parameters is not None:
|
||||
self.attributes["pycde.parameters"] = self.parameters
|
||||
|
@ -617,6 +679,20 @@ class modparams:
|
|||
return cls
|
||||
|
||||
|
||||
@dataclass
|
||||
class Metadata:
|
||||
"""Metadata for a module. This is used to provide information about a module
|
||||
in the ESI manifest. Set the classvar 'metadata' to an instance of this class
|
||||
to provide metadata for a module."""
|
||||
|
||||
name: Optional[str] = None
|
||||
repo: Optional[str] = None
|
||||
commit_hash: Optional[str] = None
|
||||
version: Optional[str] = None
|
||||
summary: Optional[str] = None
|
||||
misc: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
||||
class ImportedModSpec(ModuleBuilder):
|
||||
"""Specialization to support imported CIRCT modules."""
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ from pycde import esi
|
|||
from pycde.common import AppID, Output, RecvBundle, SendBundle
|
||||
from pycde.constructs import Wire
|
||||
from pycde.esi import MMIO
|
||||
from pycde.module import Metadata
|
||||
from pycde.types import (Bits, Bundle, BundledChannel, Channel,
|
||||
ChannelDirection, ChannelSignaling, UInt, ClockType)
|
||||
from pycde.testing import unittestmodule
|
||||
|
@ -27,6 +28,9 @@ class HostComms:
|
|||
from_host = TestFromBundle
|
||||
|
||||
|
||||
# CHECK: esi.manifest.sym @LoopbackInOutTop name "LoopbackInOut" repo "{{.+}}" commit "{{.+}}" version "0.1" {bar = "baz", foo = 1 : i64}
|
||||
|
||||
|
||||
# CHECK-LABEL: hw.module @LoopbackInOutTop(in %clk : !seq.clock, in %rst : i1)
|
||||
# CHECK: esi.service.instance #esi.appid<"cosim"[0]> svc @HostComms impl as "cosim"(%clk, %rst) : (!seq.clock, i1) -> ()
|
||||
# CHECK: %bundle, %req = esi.bundle.pack %chanOutput : !esi.bundle<[!esi.channel<i16> to "resp", !esi.channel<i24> from "req"]>
|
||||
|
@ -39,6 +43,15 @@ class LoopbackInOutTop(Module):
|
|||
clk = Clock()
|
||||
rst = Input(types.i1)
|
||||
|
||||
metadata = Metadata(
|
||||
name="LoopbackInOut",
|
||||
version="0.1",
|
||||
misc={
|
||||
"foo": 1,
|
||||
"bar": "baz"
|
||||
},
|
||||
)
|
||||
|
||||
@generator
|
||||
def construct(self):
|
||||
# Use Cosim to implement the 'HostComms' service.
|
||||
|
|
|
@ -178,7 +178,7 @@ static ModuleInfo parseModuleInfo(const nlohmann::json &mod) {
|
|||
for (auto &extra : mod.items())
|
||||
if (extra.key() != "name" && extra.key() != "summary" &&
|
||||
extra.key() != "version" && extra.key() != "repo" &&
|
||||
extra.key() != "commit_hash" && extra.key() != "symbolRef")
|
||||
extra.key() != "commitHash" && extra.key() != "symbolRef")
|
||||
extras[extra.key()] = getAny(extra.value());
|
||||
|
||||
auto value = [&](const string &key) -> optional<string> {
|
||||
|
@ -188,7 +188,7 @@ static ModuleInfo parseModuleInfo(const nlohmann::json &mod) {
|
|||
return f.value();
|
||||
};
|
||||
return ModuleInfo{value("name"), value("summary"), value("version"),
|
||||
value("repo"), value("commit_hash"), extras};
|
||||
value("repo"), value("commitHash"), extras};
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -575,7 +575,8 @@ ostream &operator<<(ostream &os, const ModuleInfo &m) {
|
|||
os << ")";
|
||||
}
|
||||
if (m.summary)
|
||||
os << ": " << *m.summary << "\n";
|
||||
os << ": " << *m.summary;
|
||||
os << "\n";
|
||||
|
||||
if (!m.extra.empty()) {
|
||||
os << " Extra metadata:\n";
|
||||
|
|
|
@ -73,12 +73,13 @@ void printInfo(ostream &os, AcceleratorConnection &acc) {
|
|||
Manifest m(jsonManifest);
|
||||
os << "API version: " << m.getApiVersion() << endl << endl;
|
||||
os << "********************************" << endl;
|
||||
os << "* Design information" << endl;
|
||||
os << "* Module information" << endl;
|
||||
os << "********************************" << endl;
|
||||
os << endl;
|
||||
for (ModuleInfo mod : m.getModuleInfos())
|
||||
os << mod << endl;
|
||||
os << "- " << mod;
|
||||
|
||||
os << endl;
|
||||
os << "********************************" << endl;
|
||||
os << "* Type table" << endl;
|
||||
os << "********************************" << endl;
|
||||
|
|
|
@ -213,5 +213,6 @@ PYBIND11_MODULE(esiCppAccel, m) {
|
|||
.def_property_readonly("api_version", &Manifest::getApiVersion)
|
||||
.def("build_accelerator", &Manifest::buildAccelerator,
|
||||
py::return_value_policy::take_ownership)
|
||||
.def_property_readonly("type_table", &Manifest::getTypeTable);
|
||||
.def_property_readonly("type_table", &Manifest::getTypeTable)
|
||||
.def_property_readonly("module_infos", &Manifest::getModuleInfos);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
# None yet. Though we're assuming that we will have some at some point.
|
||||
|
||||
from __future__ import annotations
|
||||
from ast import Mod
|
||||
import typing
|
||||
|
||||
__all__ = [
|
||||
|
@ -284,6 +285,10 @@ class Manifest:
|
|||
def type_table(self) -> list[Type]:
|
||||
...
|
||||
|
||||
@property
|
||||
def module_infos(self) -> list[ModuleInfo]:
|
||||
...
|
||||
|
||||
|
||||
class ModuleInfo:
|
||||
|
||||
|
|
Loading…
Reference in New Issue