4"""Code generation from ESI manifests to source code.
6Uses a two-pass approach for C++: first collect and name all reachable types,
7then emit structs/aliases in a dependency-ordered sequence so headers are
8standalone and deterministic.
14from typing
import Dict, List, Set, TextIO, Tuple, Type, Optional
15from .accelerator
import AcceleratorConnection, Context
16from .esiCppAccel
import ModuleInfo
18from .types
import (BundlePort
as _BundlePort, FunctionPort
as _FunctionPort,
19 CallbackPort
as _CallbackPort, ToHostPort
as _ToHostPort,
20 FromHostPort
as _FromHostPort, MMIORegion
as
21 _MMIORegionPort, MetricPort
as _MetricPort)
27from dataclasses
import dataclass, field
as _dc_field
28from pathlib
import Path
30_thisdir = Path(__file__).absolute().
resolve().parent
35 """All strings needed to emit one port slot (member + ctor + find) in Connected."""
36 struct_decls: List[str] = _dc_field(default_factory=list)
38 ctor_params: List[str] = _dc_field(default_factory=list)
41 make_unique_args: List[str] = _dc_field(default_factory=list)
42 post_connect: str =
""
43 using_aliases: List[Tuple[str, str]] = _dc_field(default_factory=list)
47 """Base class for all generators."""
49 language: Optional[str] =
None
51 def __init__(self, conn: AcceleratorConnection):
54 def generate(self, output_dir: Path, system_name: str):
55 raise NotImplementedError(
"Generator.generate() must be overridden")
59 """Generate C++ headers from an ESI manifest."""
63 def __init__(self, conn: AcceleratorConnection):
70 """Get the C++ code for a constant in a module."""
71 const_strs: List[str] = [
72 f
"static constexpr {self.type_emitter.type_identifier(const.type)} "
73 f
"{name} = 0x{const.value:x};"
74 for name, const
in module_info.constants.items()
76 return "\n".join(const_strs)
84 """Return a C++-safe identifier from an AppID name."""
87 result.append(ch
if (ch.isalnum()
or ch ==
"_")
else "_")
92 return "".join(result)
95 """Walk the live hierarchy and return {module_name: first Instance}."""
96 accel = self.
_conn.build_accelerator()
97 result: Dict[str, object] = {}
98 queue = list(accel.children.values())
101 info = inst.cpp_hwmodule.info
104 if name
is not None and name
not in result:
106 queue.extend(inst.children.values())
110 """Return the C++ member type string for a port (no member name).
112 For typed ports (function/callback/to-host/from-host channels) `alias_prefix`
113 is required; the returned template parameters are written using the alias
114 names (`<prefix>Args`, `<prefix>Result`, `<prefix>Data`) that should be
115 emitted at module-class scope via `_port_using_aliases`. For non-typed
116 ports (MMIO regions, telemetry metrics, plain bundles) `alias_prefix` is
117 ignored and the runtime reference/pointer type is returned directly.
119 if isinstance(port, _FunctionPort):
120 assert alias_prefix
is not None, (
121 "alias_prefix is required for FunctionPort to avoid emitting "
122 "long mangled type names inline (which would also collide across "
123 "modules' `using` declarations)")
124 return (f
"esi::TypedFunction<{alias_prefix}Args, "
125 f
"{alias_prefix}Result>")
126 if isinstance(port, _CallbackPort):
127 assert alias_prefix
is not None, (
128 "alias_prefix is required for CallbackPort")
129 return (f
"esi::TypedCallback<{alias_prefix}Args, "
130 f
"{alias_prefix}Result>")
131 if isinstance(port, _ToHostPort):
132 assert alias_prefix
is not None, (
133 "alias_prefix is required for ToHostPort")
134 return f
"esi::TypedReadPort<{alias_prefix}Data>"
135 if isinstance(port, _FromHostPort):
136 assert alias_prefix
is not None, (
137 "alias_prefix is required for FromHostPort")
138 return f
"esi::TypedWritePort<{alias_prefix}Data>"
139 if isinstance(port, _MMIORegionPort):
140 return "esi::services::MMIO::MMIORegion &"
141 if isinstance(port, _MetricPort):
142 return "esi::services::TelemetryService::Metric &"
143 return "esi::BundlePort &"
146 port) -> List[Tuple[str, str]]:
147 """Return (alias_name, type_id) pairs to emit as `using` declarations at
148 module-class scope for the typed-port template parameters."""
149 if isinstance(port, (_FunctionPort, _CallbackPort)):
150 arg = self.
type_emitter.type_identifier(port.arg_window_type
or
152 res = self.
type_emitter.type_identifier(port.result_window_type
or
154 return [(f
"{alias_prefix}Args", arg), (f
"{alias_prefix}Result", res)]
155 if isinstance(port, (_ToHostPort, _FromHostPort)):
156 data = self.
type_emitter.type_identifier(port.data_window_type
or
158 return [(f
"{alias_prefix}Data", data)]
163 alias_prefix: Optional[str] =
None) -> str:
164 """Return the storage type used inside an `IndexedPorts<T>` for `port`.
166 Typed ports use the same `TypedFunction<...>` / `TypedReadPort<...>` etc.
167 that `_cpp_member_type` produces. MMIO regions, telemetry metrics, and
168 plain bundle ports are stored as raw pointers because `std::map<int, T&>`
171 if isinstance(port, _MMIORegionPort):
172 return "esi::services::MMIO::MMIORegion *"
173 if isinstance(port, _MetricPort):
174 return "esi::services::TelemetryService::Metric *"
176 (_FunctionPort, _CallbackPort, _ToHostPort, _FromHostPort)):
178 return "esi::BundlePort *"
181 """Return the C++ constructor parameter type for a port."""
182 if isinstance(port, _FunctionPort):
183 return "esi::services::FuncService::Function *"
184 if isinstance(port, _CallbackPort):
185 return "esi::services::CallService::Callback *"
186 if isinstance(port, _ToHostPort):
187 return "esi::ReadChannelPort &"
188 if isinstance(port, _FromHostPort):
189 return "esi::WriteChannelPort &"
190 if isinstance(port, _MMIORegionPort):
191 return "esi::services::MMIO::MMIORegion &"
192 if isinstance(port, _MetricPort):
193 return "esi::services::TelemetryService::Metric &"
194 return "esi::BundlePort &"
198 """Return the parameter name suffix ('_chan', '_svc', or '_port')."""
199 if isinstance(port, (_ToHostPort, _FromHostPort)):
201 if isinstance(port, (_MMIORegionPort, _MetricPort)):
207 """Return `esi::AppID(...)` expression for an AppID."""
211 return f
'esi::AppID("{name}")'
212 return f
'esi::AppID("{name}", {idx})'
215 """Return the code snippet that resolves a scalar port in connect()."""
217 if isinstance(port, _FunctionPort):
218 v = f
"{member_name}_port"
221 f
" esi::findPortAsOrThrow<esi::services::FuncService::Function>(\n"
222 f
" rawModule, {ae});")
223 if isinstance(port, _CallbackPort):
224 v = f
"{member_name}_port"
227 f
" esi::findPortAsOrThrow<esi::services::CallService::Callback>(\n"
228 f
" rawModule, {ae});")
229 if isinstance(port, _ToHostPort):
230 v = f
"{member_name}_chan"
233 f
" esi::findPortAsOrThrow<esi::services::ChannelService::ToHost>(\n"
234 f
' rawModule, {ae})->getRawRead("data");')
235 if isinstance(port, _FromHostPort):
236 v = f
"{member_name}_chan"
239 f
" esi::findPortAsOrThrow<esi::services::ChannelService::FromHost>(\n"
240 f
' rawModule, {ae})->getRawWrite("data");')
241 if isinstance(port, _MMIORegionPort):
242 v = f
"{member_name}_svc"
243 return (f
"auto &{v} =\n"
244 f
" *esi::findPortAsOrThrow<esi::services::MMIO::MMIORegion>(\n"
245 f
" rawModule, {ae});")
246 if isinstance(port, _MetricPort):
247 v = f
"{member_name}_svc"
250 f
" *esi::findPortAsOrThrow<esi::services::TelemetryService::Metric>(\n"
251 f
" rawModule, {ae});")
253 v = f
"{member_name}_port"
254 return f
"auto &{v} = esi::findPortOrThrow(rawModule, {ae});"
258 """Return the argument expression for make_unique<Connected>(...)."""
259 if isinstance(port, (_ToHostPort, _FromHostPort)):
260 return f
"{member_name}_chan"
261 if isinstance(port, (_MMIORegionPort, _MetricPort)):
262 return f
"{member_name}_svc"
263 return f
"{member_name}_port"
267 """True if the generated connect() should call .connect() on this port.
269 CallbackPort.connect() requires a user-supplied callback — skip.
270 MMIORegion, BundlePort — no .connect() method."""
271 return isinstance(port,
272 (_FunctionPort, _ToHostPort, _FromHostPort, _MetricPort))
275 """Build a _PortGroup for a single scalar (non-indexed) port."""
277 alias_prefix = member_name
if aliases
else None
279 is_ref = member_type.endswith(
" &")
282 param_name = f
"{member_name}{param_suffix}"
285 member_decl = f
"{member_type}{member_name};"
287 member_decl = f
"{member_type} {member_name};"
291 post = f
"connected->{member_name}.connect();"
294 member_decl=member_decl,
295 ctor_params=[f
"{param_type} {param_name}"],
296 init_entry=f
"{member_name}({param_name})",
300 using_aliases=aliases,
304 port_list) -> _PortGroup:
305 """Build a _PortGroup for a same-name, same-type indexed port array."""
307 first_port = port_list[0][1]
309 alias_prefix = member_name
if aliases
else None
311 alias_prefix=alias_prefix)
312 indexed_type = f
"esi::IndexedPorts<{elem_type}>"
313 map_var = f
"{member_name}_backing"
314 map_type = f
"std::map<int, {elem_type}>"
315 indexed_var = f
"{member_name}_map"
323 f
"{map_type} {map_var};",
324 f
"for (uint32_t idx : esi::findPortIndices(rawModule, "
325 f
"\"{appid_name}\")) {{",
327 appid_expr = f
'esi::AppID("{appid_name}", idx)'
328 if isinstance(first_port, _FunctionPort):
330 f
" {map_var}.try_emplace(\n"
331 f
" static_cast<int>(idx),\n"
332 f
" esi::findPortAsOrThrow<esi::services::FuncService::Function>"
334 f
" rawModule, {appid_expr}));")
335 elif isinstance(first_port, _CallbackPort):
337 f
" {map_var}.try_emplace(\n"
338 f
" static_cast<int>(idx),\n"
339 f
" esi::findPortAsOrThrow<esi::services::CallService::Callback>"
341 f
" rawModule, {appid_expr}));")
342 elif isinstance(first_port, _ToHostPort):
347 f
" esi::findPortAsOrThrow<esi::services::ChannelService::ToHost>"
349 f
" rawModule, {appid_expr});\n"
350 f
" {map_var}.try_emplace(\n"
351 f
" static_cast<int>(idx),\n"
352 f
" svc->getRawRead(\"data\"));")
353 elif isinstance(first_port, _FromHostPort):
356 f
" esi::findPortAsOrThrow<esi::services::ChannelService::"
358 f
" rawModule, {appid_expr});\n"
359 f
" {map_var}.try_emplace(\n"
360 f
" static_cast<int>(idx),\n"
361 f
" svc->getRawWrite(\"data\"));")
362 elif isinstance(first_port, _MMIORegionPort):
364 f
" {map_var}.try_emplace(\n"
365 f
" static_cast<int>(idx),\n"
366 f
" esi::findPortAsOrThrow<esi::services::MMIO::MMIORegion>(\n"
367 f
" rawModule, {appid_expr}));")
368 elif isinstance(first_port, _MetricPort):
370 f
" {map_var}.try_emplace(\n"
371 f
" static_cast<int>(idx),\n"
372 f
" esi::findPortAsOrThrow<esi::services::TelemetryService::"
374 f
" rawModule, {appid_expr}));")
379 f
" {map_var}.try_emplace(\n"
380 f
" static_cast<int>(idx),\n"
381 f
" &esi::findPortOrThrow(rawModule, {appid_expr}));")
382 find_parts.append(
"}")
383 find_parts.append(f
"{indexed_type} {indexed_var}(std::move({map_var}));")
388 post = (f
"for (auto &[idx, port] : connected->{member_name})\n"
392 member_decl=f
"{indexed_type} {member_name};",
393 ctor_params=[f
"{indexed_type} {indexed_var}"],
394 init_entry=f
"{member_name}(std::move({indexed_var}))",
395 find_code=
"\n".join(find_parts),
396 make_unique_args=[f
"std::move({indexed_var})"],
398 using_aliases=aliases,
402 port_list) -> _PortGroup:
403 """Build a _PortGroup for a same-name, mixed-type indexed port group."""
404 struct_name = f
"{member_name}_ports"
405 sub_member_decls: List[str] = []
406 ctor_params: List[str] = []
407 init_args: List[str] = []
408 find_parts: List[str] = []
409 make_args: List[str] = []
410 post_parts: List[str] = []
411 using_aliases: List[Tuple[str, str]] = []
413 for appid, port
in port_list:
414 idx = appid.idx
if appid.idx
is not None else 0
416 sub_alias_prefix = f
"{member_name}_{idx}"
418 using_aliases.extend(sub_aliases)
419 alias_prefix = sub_alias_prefix
if sub_aliases
else None
421 is_ref = member_type.endswith(
" &")
423 sub_member_decls.append(f
"{member_type}{sub_name};")
425 sub_member_decls.append(f
"{member_type} {sub_name};")
429 param_name = f
"{member_name}_{idx}{param_suffix}"
430 ctor_params.append(f
"{param_type} {param_name}")
431 init_args.append(param_name)
435 post_parts.append(f
"connected->{member_name}.{sub_name}.connect();")
437 struct_decl = (f
"struct {struct_name} {{\n" +
438 "".join(f
" {d}\n" for d
in sub_member_decls) +
" };")
439 init_entry = f
"{member_name}{{{', '.join(init_args)}}}"
442 struct_decls=[struct_decl],
443 member_decl=f
"{struct_name} {member_name};",
444 ctor_params=ctor_params,
445 init_entry=init_entry,
446 find_code=
"\n".join(find_parts),
447 make_unique_args=make_args,
448 post_connect=
"\n".join(post_parts),
449 using_aliases=using_aliases,
453 """Group `ports` (AppID → BundlePort) into _PortGroup list."""
455 groups_by_name: Dict[str, list] = {}
456 for appid, port
in ports.items():
458 if n
not in groups_by_name:
459 groups_by_name[n] = []
460 groups_by_name[n].
append((appid, port))
462 result: List[_PortGroup] = []
463 for appid_name, port_list
in groups_by_name.items():
466 port_list.sort(key=
lambda x: x[0].idx
if x[0].idx
is not None else -1)
468 if len(port_list) == 1:
469 appid, port = port_list[0]
470 if appid.idx
is None:
478 all_indexed = all(a.idx
is not None for a, _
in port_list)
486 all_same_type = len({type(p)
for _, p
in port_list}) == 1
497 module_info: ModuleInfo, port_groups: List[_PortGroup],
498 out: TextIO) ->
None:
499 """Emit the full module class to `out`."""
500 out.write(f
"/// Generated header for {system_name} module {name}.\n"
502 '#include "types.h"\n'
503 '#include "esi/TypedPorts.h"\n'
507 "#include <optional>\n"
508 "#include <string>\n"
510 f
"namespace {system_name} {{\n"
514 metadata_lines: List[str] = []
515 summary = getattr(module_info,
"summary",
None)
517 for line
in summary.splitlines():
518 metadata_lines.append(line)
519 for label, attr
in ((
"Version",
"version"), (
"Repository",
"repo"),
520 (
"Commit",
"commit_hash")):
521 val = getattr(module_info, attr,
None)
523 metadata_lines.append(f
"{label}: {val}")
526 for line
in metadata_lines:
527 out.write(f
"/// {line}\n" if line
else "///\n")
530 out.write(f
"class {name} {{\n"
535 out.write(
" // Module constants.\n")
536 out.write(f
" {consts}\n\n")
540 aliases = [a
for grp
in port_groups
for a
in grp.using_aliases]
542 for alias_name, alias_type
in aliases:
543 out.write(f
" using {alias_name} = {alias_type};\n")
548 " /// Holds the resolved, typed ports for this module instance.\n"
549 " /// Returned by `connect()`.\n"
550 " class Connected {\n public:\n")
553 for grp
in port_groups:
554 for decl
in grp.struct_decls:
555 out.write(f
" {decl}\n")
556 if any(grp.struct_decls
for grp
in port_groups):
560 for grp
in port_groups:
561 out.write(f
" {grp.member_decl}\n")
565 all_params = [p
for grp
in port_groups
for p
in grp.ctor_params]
566 out.write(
" Connected(\n")
567 for i, param
in enumerate(all_params):
568 comma =
"," if i < len(all_params) - 1
else ""
569 out.write(f
" {param}{comma}\n")
571 inits = [grp.init_entry
for grp
in port_groups]
572 out.write(
",\n ".join(inits))
573 out.write(
" {}\n };\n\n")
577 f
" {name}(esi::HWModule *rawModule) : rawModule(rawModule) {{}}\n\n")
583 " /// The connected module's name as reported by the manifest, or\n"
584 " /// std::nullopt if the module has no metadata.\n"
585 " std::optional<std::string> name() const {\n"
586 " auto info = rawModule->getInfo();\n"
587 " return info ? info->name : std::nullopt;\n"
589 " /// The connected module's summary string, if any.\n"
590 " std::optional<std::string> summary() const {\n"
591 " auto info = rawModule->getInfo();\n"
592 " return info ? info->summary : std::nullopt;\n"
594 " /// The connected module's version string, if any.\n"
595 " std::optional<std::string> version() const {\n"
596 " auto info = rawModule->getInfo();\n"
597 " return info ? info->version : std::nullopt;\n"
599 " /// The connected module's source repository, if any.\n"
600 " std::optional<std::string> repo() const {\n"
601 " auto info = rawModule->getInfo();\n"
602 " return info ? info->repo : std::nullopt;\n"
604 " /// The connected module's source commit hash, if any.\n"
605 " std::optional<std::string> commitHash() const {\n"
606 " auto info = rawModule->getInfo();\n"
607 " return info ? info->commitHash : std::nullopt;\n"
609 " /// Designer-specified constants for the connected module.\n"
610 " /// Returns an empty map if the module has no metadata.\n"
611 " std::map<std::string, esi::Constant> constants() const {\n"
612 " auto info = rawModule->getInfo();\n"
613 " return info ? info->constants\n"
614 " : std::map<std::string, esi::Constant>{};\n"
616 " /// Free-form designer-supplied metadata for the connected module.\n"
617 " /// Returns an empty map if the module has no metadata.\n"
618 " std::map<std::string, std::any> extra() const {\n"
619 " auto info = rawModule->getInfo();\n"
620 " return info ? info->extra : std::map<std::string, std::any>{};\n"
624 out.write(
" std::unique_ptr<Connected> connect() {\n")
627 for grp
in port_groups:
629 for line
in grp.find_code.splitlines():
630 out.write(f
" {line}\n")
634 all_args = [a
for grp
in port_groups
for a
in grp.make_unique_args]
635 out.write(
" auto connected = std::make_unique<Connected>(\n")
636 for i, arg
in enumerate(all_args):
637 comma =
"," if i < len(all_args) - 1
else ""
638 out.write(f
" {arg}{comma}\n")
642 for grp
in port_groups:
644 for line
in grp.post_connect.splitlines():
645 out.write(f
" {line}\n")
647 out.write(
" return connected;\n }\n\n")
649 out.write(
"private:\n esi::HWModule *rawModule;\n};\n\n")
650 out.write(f
"}} // namespace {system_name}\n")
653 """Write the C++ header. One for each module in the manifest."""
656 for module_info
in self.
manifest.module_infos:
657 if module_info.name
is None:
659 name = module_info.name
660 instance = module_instances.get(name)
662 if instance
is not None:
666 except (NotImplementedError, ValueError)
as e:
667 sys.stderr.write(f
"Warning: skipping module '{name}': {e}\n")
668 hdr_file = output_dir / f
"{name}.h"
669 with open(hdr_file,
"w")
as hdr:
670 hdr.write(f
"// Skipped: {e}\n")
673 hdr_file = output_dir / f
"{name}.h"
674 with open(hdr_file,
"w")
as hdr:
678 def generate(self, output_dir: Path, system_name: str):
684 """Plan C++ type naming and ordering from an ESI manifest."""
687 """Initialize the generator with the manifest and target namespace."""
691 self.used_names: Dict[str, bool] = {}
693 self.alias_base_names: Set[str] = set()
699 """Name the types and prepare for emission by registering all reachable
700 types and assigning."""
701 visited: Set[str] = set()
716 """Create a C++-safe identifier from the manifest-provided name."""
717 name = name.replace(
"::",
"_")
718 if name.startswith(
"@"):
722 if ch.isalnum()
or ch ==
"_":
725 sanitized.append(
"_")
729 sanitized.insert(0,
"_")
730 return "".join(sanitized)
733 """Reserve a globally unique identifier using the sanitized base name."""
735 if is_alias
and base
in self.alias_base_names:
737 f
"Warning: duplicate alias name '{base}' detected; disambiguating.\n")
739 self.alias_base_names.add(base)
742 while name
in self.used_names:
743 name = f
"{base}_{idx}"
745 self.used_names[name] = is_alias
749 """Derive a deterministic name for anonymous structs from their fields."""
751 for field_name, field_type
in struct_type.fields:
752 parts.append(field_name)
757 """Derive a deterministic name for anonymous unions from their fields."""
759 for field_name, field_type
in union_type.fields:
760 parts.append(field_name)
765 """Derive a deterministic name for generated window helpers.
767 Two distinct windows can wrap the same `into` struct (e.g. serial and
768 parallel encodings of the same payload), so the helper name must be
769 derived from BOTH the inner type's name and the window's own name/id.
772 into_name = self.type_id_map.get(into_type)
773 window_part = (window_type.name
776 base = f
"{into_name}_{window_part}"
777 elif window_type.name:
780 base = f
"_window_{window_part}"
785 wrapped = wrapped.inner_type
798 for field_name, field_type
in into_type.fields:
800 list_fields.append(field_name)
801 if len(list_fields) != 1:
804 list_field_name = list_fields[0]
809 for frame
in current_type.frames:
810 for field
in frame.fields:
811 if field.name != list_field_name:
813 if field.bulk_count_width > 0:
814 if header_field
is not None:
817 elif field.num_items > 0:
818 if data_field
is not None:
821 return (header_field
is not None and data_field
is not None and
822 data_field.num_items == 1)
825 """Return child types in a stable order for traversal."""
827 return [t.inner_type]
if t.inner_type
is not None else []
829 return [channel.type
for channel
in t.channels]
833 return [field_type
for _, field_type
in t.fields]
835 return [field_type
for _, field_type
in t.fields]
837 return [t.element_type]
841 return [t.element_type]
844 def _visit_types(self, t: types.ESIType, visited: Set[str], visit_fn) ->
None:
845 """Traverse types with alphabetical child ordering in post-order."""
847 raise TypeError(f
"Expected ESIType, got {type(t)}")
853 for child
in children:
858 """Scan for aliases and reserve their names (recursive)."""
861 def visit(alias_type: types.ESIType) ->
None:
864 if alias_type
not in self.type_id_map:
865 alias_name = self.
_reserve_name(alias_type.name, is_alias=
True)
866 self.type_id_map[alias_type] = alias_name
871 """Scan for structs/unions needing auto-names and reserve them."""
874 def visit(current_type: types.ESIType) ->
None:
875 if current_type
in self.type_id_map:
885 """Scan for supported window types and reserve helper names."""
887 def visit(current_type: types.ESIType) ->
None:
891 if current_type
in self.type_id_map:
899 """Collect types that require top-level declarations for a given type."""
904 def visit(current: types.ESIType) ->
None:
906 inner = current.inner_type
907 if inner
is not None and (isinstance(
923 """Collect only the declarations referenced by a generated window helper."""
929 for _, field_type
in into_type.fields:
938 """Collect and order types for deterministic emission."""
940 for esi_type
in self.type_id_map.keys():
946 for esi_type
in self.type_id_map.keys():
947 if isinstance(esi_type,
951 inner = esi_type.inner_type
953 inner)
in window_into_types:
955 if (isinstance(esi_type,
958 emit_types.append(esi_type)
961 name_to_type = {self.type_id_map[t]: t
for t
in emit_types}
962 sorted_names = sorted(name_to_type.keys(),
964 (0
if self.used_names.get(name,
False)
else 1, name))
972 def visit(current: types.ESIType) ->
None:
974 if current
in visited:
976 if current
in visiting:
979 visiting.add(current)
983 inner = current.inner_type
984 if inner
is not None:
987 for _, field_type
in current.fields:
992 for dep
in sorted(deps, key=
lambda dep: self.type_id_map[dep]):
995 visiting.remove(current)
997 ordered.append(current)
999 for name
in sorted_names:
1000 visit(name_to_type[name])
1002 return ordered, has_cycle
1006 """Emit C++ headers from precomputed type ordering."""
1014 """Get the C++ type string for an ESI type."""
1018 """Escape a Python string for use as a C++ string literal."""
1019 escaped = value.replace(
"\\",
"\\\\").replace(
'"',
'\\"')
1020 return f
'"{escaped}"'
1023 """Get the textual code for the storage class of an integer type."""
1030 """Get the textual code for a byte-addressable integer storage type."""
1034 elif bit_width <= 8:
1036 elif bit_width <= 16:
1038 elif bit_width <= 32:
1040 elif bit_width <= 64:
1043 raise ValueError(f
"Unsupported integer width: {bit_width}")
1046 return f
"uint{storage_width}_t"
1047 return f
"int{storage_width}_t"
1050 """Return the size of a fixed-width type in bytes."""
1051 if wrapped.bit_width < 0:
1052 raise ValueError(f
"Unsupported unbounded type width for '{wrapped}'")
1053 return (wrapped.bit_width + 7) // 8
1056 self, array_type: types.ArrayType) -> Tuple[str, List[int]]:
1057 """Return the base C++ type and outer-to-inner dimensions of a nested array."""
1058 dims: List[int] = []
1061 dims.append(inner.size)
1062 inner = inner.element_type
1064 return base_cpp, dims
1067 """Return the equivalent nested `std::array<...>` type for an array.
1069 `std::array<T, N>` is layout-compatible in practice with `T[N]` on every
1070 major implementation (and identical under `#pragma pack(1)`), so the
1071 generator uses it everywhere a fixed-size array would appear. This keeps
1072 field/value/ctor types storable in `std::vector` and assignable with `=`.
1076 for size
in reversed(dims):
1077 result = f
"std::array<{result}, {size}>"
1081 """Resolve an ESI type to its C++ identifier."""
1084 if isinstance(wrapped,
1092 raise ValueError(
"List types require a generated window wrapper")
1103 raise NotImplementedError(
1104 f
"Type '{wrapped}' not supported for C++ generation")
1107 """Strip alias wrappers to reach the underlying type."""
1109 wrapped = wrapped.inner_type
1114 """Emit a packed field declaration for generated window helpers.
1116 Arrays use `std::array` (handled by `_cpp_type`), so no bracket syntax is
1117 needed and a single uniform declaration form covers every type.
1122 wrapped.bit_width % 8 != 0:
1123 return f
"{field_cpp} {field_name} : {wrapped.bit_width};"
1124 return f
"{field_cpp} {field_name};"
1128 """Emit a constructor parameter for generated window helpers.
1130 Small scalar header fields are cheaper to pass by value than by reference.
1131 Larger aggregates stay as const references.
1136 return f
"{field_cpp} {field_name}"
1137 return f
"const {field_cpp} &{field_name}"
1141 """Copy a generated window field."""
1142 hdr.write(f
" {dest_expr} = {src_expr};\n")
1145 """Compute the byte width of a field type, rounding up to full bytes."""
1146 return (field_type.bit_width + 7) // 8
1149 """Return the bounded byte width of `esi_type`, or `None` if it has no
1150 well-defined static size (e.g. unbounded `!esi.any` or recursive types).
1153 bit_width = esi_type.bit_width
1156 if bit_width
is None or bit_width < 0:
1158 return (bit_width + 7) // 8
1163 expected_bytes: Optional[int],
1164 indent: str =
"") ->
None:
1165 """Emit a `static_assert` that pins the C++ `sizeof` of a packed type to
1166 the byte width derived from the manifest.
1168 `std::array` and bit-field layout are technically implementation-defined,
1169 so this assertion is the safety net that catches a toolchain that lays
1170 them out differently from the wire format. Skipped silently for types
1171 without a bounded static size.
1173 if expected_bytes
is None:
1176 f
"{indent}static_assert(sizeof({type_name}) == {expected_bytes},\n"
1177 f
"{indent} \"{type_name}: packed layout does not match "
1178 f
"manifest size\");\n")
1181 """Extract the metadata needed to emit a bulk list window wrapper."""
1184 raise ValueError(
"window codegen currently requires a struct into-type")
1186 field_map = {name: field_type
for name, field_type
in into_type.fields}
1189 for name, field_type
in into_type.fields
1192 if len(list_fields) != 1:
1193 raise ValueError(
"window codegen currently supports exactly one list")
1195 list_field_name, list_type = list_fields[0]
1202 for frame
in window_type.frames:
1203 for field
in frame.fields:
1204 if field.name != list_field_name:
1206 if field.bulk_count_width > 0:
1207 header_frame = frame
1208 header_field = field
1209 elif field.num_items > 0:
1213 if header_frame
is None or header_field
is None:
1214 raise ValueError(
"window codegen requires a bulk-count header frame")
1215 if data_frame
is None or data_field
is None:
1216 raise ValueError(
"window codegen requires a data frame for the list")
1217 if data_field.num_items != 1:
1218 raise ValueError(
"window codegen currently supports numItems == 1")
1220 ctor_params = [(name, field_type)
1221 for name, field_type
in into_type.fields
1222 if name != list_field_name]
1226 count_field_name = f
"{list_field_name}_count"
1227 count_width = header_field.bulk_count_width
1229 count_bytes = (count_width + 7) // 8
1230 for field
in reversed(header_frame.fields):
1231 if field.name == list_field_name:
1232 header_fields.append((count_field_name,
None))
1233 header_bytes += count_bytes
1235 field_type = field_map[field.name]
1236 header_fields.append((field.name, field_type))
1241 for field
in reversed(data_frame.fields):
1242 if field.name == list_field_name:
1243 data_fields.append((list_field_name, list_type.element_type))
1246 field_type = field_map[field.name]
1247 data_fields.append((field.name, field_type))
1250 frame_bytes = max(header_bytes, data_bytes)
1253 "ctor_params": ctor_params,
1254 "count_cpp": count_cpp,
1255 "count_field_name": count_field_name,
1256 "count_width": count_width,
1257 "data_fields": data_fields,
1258 "data_pad_bytes": frame_bytes - data_bytes,
1259 "element_cpp": self.
_cpp_type(list_type.element_type),
1260 "frame_bytes": frame_bytes,
1261 "header_fields": header_fields,
1262 "header_pad_bytes": frame_bytes - header_bytes,
1263 "list_field_name": list_field_name,
1268 """Emit a packed struct declaration plus its type id string."""
1269 fields = list(struct_type.fields)
1270 if struct_type.cpp_type.reverse:
1271 fields = list(reversed(fields))
1272 field_decls: List[str] = []
1273 for field_name, field_type
in fields:
1275 if field_cpp ==
"void":
1278 field_decls.append(f
"// void {field_name};")
1284 bitfield_width = wrapped.bit_width
1285 if bitfield_width >= 0:
1286 field_decls.append(f
"{field_cpp} {field_name} : {bitfield_width};")
1288 field_decls.append(f
"{field_cpp} {field_name};")
1290 field_decls.append(f
"{field_cpp} {field_name};")
1292 hdr.write(
"#pragma pack(push, 1)\n")
1293 hdr.write(f
"struct {struct_name} {{\n")
1294 for decl
in field_decls:
1295 hdr.write(f
" {decl}\n")
1300 logical_fields = [(name, ftype)
1301 for name, ftype
in struct_type.fields
1304 hdr.write(f
" {struct_name}() = default;\n")
1305 ctor_params =
", ".join(
1307 for name, ftype
in logical_fields)
1308 inits =
", ".join(f
"{name}({name})" for name, _
in logical_fields)
1309 hdr.write(f
" {struct_name}({ctor_params}) : {inits} {{}}\n\n")
1311 f
" static constexpr std::string_view _ESI_ID = {self._cpp_string_literal(struct_type.id)};\n"
1315 hdr.write(
"#pragma pack(pop)\n\n")
1318 """Emit a packed union declaration plus its type id string.
1320 Fields narrower than the union width get wrapper structs with a `_pad`
1321 byte array so the data sits at the MSB end, matching SV packed union
1322 layout where padding occupies the LSBs / lower addresses.
1326 fields = list(union_type.fields)
1328 hdr.write(
"#pragma pack(push, 1)\n")
1330 wrapper_names: Dict[str, str] = {}
1331 for field_name, field_type
in fields:
1332 if self.
_cpp_type(field_type) ==
"void":
1335 pad_bytes = union_bytes - field_bytes
1337 wrapper = f
"{union_name}_{field_name}"
1338 wrapper_names[field_name] = wrapper
1339 hdr.write(f
"struct {wrapper} {{\n")
1340 hdr.write(f
" uint8_t _pad[{pad_bytes}];\n")
1342 hdr.write(f
" {field_cpp} {field_name};\n")
1347 union_field_decls: List[str] = []
1348 for field_name, field_type
in fields:
1350 if field_cpp ==
"void":
1352 union_field_decls.append(f
"// void {field_name};")
1353 elif field_name
in wrapper_names:
1354 union_field_decls.append(f
"{wrapper_names[field_name]} {field_name};")
1356 union_field_decls.append(f
"{field_cpp} {field_name};")
1357 hdr.write(f
"union {union_name} {{\n")
1358 for decl
in union_field_decls:
1359 hdr.write(f
" {decl}\n")
1362 f
" static constexpr std::string_view _ESI_ID = {self._cpp_string_literal(union_type.id)};\n"
1366 hdr.write(
"#pragma pack(pop)\n\n")
1369 """Emit a SegmentedMessageData helper for a serial list window."""
1373 for name, field_type
in info[
"ctor_params"]
1375 value_ctor_params = list(ctor_params)
1376 value_ctor_params.append(
1377 f
"const std::vector<value_type> &{info['list_field_name']}")
1378 value_ctor_signature =
", ".join(value_ctor_params)
1379 frame_ctor_params = list(ctor_params)
1380 frame_ctor_params.append(
"std::vector<data_frame> frames")
1381 frame_ctor_signature =
", ".join(frame_ctor_params)
1382 helper_args =
", ".join(name
for name, _
in info[
"ctor_params"])
1383 helper_call = f
"{helper_args}, std::move(frames)" if helper_args
else "std::move(frames)"
1386 f
"struct {info['window_name']} : public esi::SegmentedMessageData {{\n")
1387 hdr.write(
"public:\n")
1388 hdr.write(f
" using value_type = {info['element_cpp']};\n")
1389 hdr.write(f
" using count_type = {info['count_cpp']};\n\n")
1390 hdr.write(
"#pragma pack(push, 1)\n")
1391 hdr.write(
" struct data_frame {\n")
1392 if info[
"data_pad_bytes"] > 0:
1393 hdr.write(f
" uint8_t _pad[{info['data_pad_bytes']}];\n")
1394 for field_name, field_type
in info[
"data_fields"]:
1396 hdr.write(f
" {decl}\n")
1399 hdr.write(
"#pragma pack(pop)\n\n")
1400 hdr.write(
"private:\n")
1401 hdr.write(
"#pragma pack(push, 1)\n")
1402 hdr.write(
" struct header_frame {\n")
1403 if info[
"header_pad_bytes"] > 0:
1404 hdr.write(f
" uint8_t _pad[{info['header_pad_bytes']}];\n")
1405 for field_name, field_type
in info[
"header_fields"]:
1406 if field_type
is None:
1407 if info[
"count_width"] % 8 == 0:
1408 decl = f
"count_type {field_name};"
1410 decl = f
"count_type {field_name} : {info['count_width']};"
1413 hdr.write(f
" {decl}\n")
1417 info[
"frame_bytes"],
1419 hdr.write(
"#pragma pack(pop)\n\n")
1420 hdr.write(
" header_frame header{};\n")
1421 hdr.write(
" std::vector<data_frame> data_frames;\n")
1422 hdr.write(
" header_frame footer{};\n\n")
1423 hdr.write(f
" void construct({frame_ctor_signature}) {{\n")
1424 hdr.write(
" if (frames.empty())\n")
1426 f
" throw std::invalid_argument(\"{info['window_name']}: bulk windowed lists cannot be empty\");\n"
1429 " if (frames.size() > std::numeric_limits<count_type>::max())\n")
1431 f
" throw std::invalid_argument(\"{info['window_name']}: list too large for encoded count\");\n"
1434 f
" header.{info['count_field_name']} = static_cast<count_type>(frames.size());\n"
1436 for name, _
in info[
"ctor_params"]:
1438 field_type
for field_name, field_type
in info[
"ctor_params"]
1439 if field_name == name)
1441 hdr.write(f
" footer.{info['count_field_name']} = 0;\n")
1442 hdr.write(
" data_frames = std::move(frames);\n")
1444 hdr.write(
"public:\n")
1445 hdr.write(f
" {info['window_name']}({frame_ctor_signature}) {{\n")
1446 hdr.write(f
" construct({helper_call});\n")
1448 hdr.write(f
" {info['window_name']}({value_ctor_signature}) {{\n")
1449 hdr.write(
" std::vector<data_frame> frames;\n")
1450 hdr.write(f
" frames.reserve({info['list_field_name']}.size());\n")
1451 hdr.write(f
" for (const auto &element : {info['list_field_name']}) {{\n")
1452 hdr.write(
" auto &frame = frames.emplace_back();\n")
1453 hdr.write(f
" frame.{info['list_field_name']} = element;\n")
1455 hdr.write(f
" construct({helper_call});\n")
1457 hdr.write(
" size_t numSegments() const override { return 3; }\n")
1458 hdr.write(
" esi::Segment segment(size_t idx) const override {\n")
1459 hdr.write(
" if (idx == 0)\n")
1461 " return {reinterpret_cast<const uint8_t *>(&header), sizeof(header)};\n"
1463 hdr.write(
" if (idx == 1)\n")
1465 " return {reinterpret_cast<const uint8_t *>(data_frames.data()),\n"
1467 hdr.write(
" data_frames.size() * sizeof(data_frame)};\n")
1468 hdr.write(
" if (idx == 2)\n")
1470 " return {reinterpret_cast<const uint8_t *>(&footer), sizeof(footer)};\n"
1473 f
" throw std::out_of_range(\"{info['window_name']}: invalid segment index\");\n"
1477 f
" static constexpr std::string_view _ESI_ID = {self._cpp_string_literal(self._unwrap_aliases(window_type.into_type).id)};\n"
1483 f
" static constexpr std::string_view _ESI_WINDOW_ID = {self._cpp_string_literal(window_type.id)};\n"
1490 """Emit accessors for the header and data fields of a window helper.
1492 Exposes each static header field as a scalar accessor, the count of data
1493 frames, and one vector-valued accessor per data field so decoded values
1494 are easy to inspect on the read side.
1496 list_field_name = info[
"list_field_name"]
1498 for field_name, field_type
in info[
"header_fields"]:
1501 if field_type
is None:
1507 if isinstance(unwrapped_header,
1510 f
" {cpp} {field_name}() const {{ return header.{field_name}; }}\n")
1513 f
" const {cpp} &{field_name}() const {{ return header.{field_name}; }}\n"
1516 f
" size_t {list_field_name}_count() const {{ return data_frames.size(); }}\n"
1518 for field_name, field_type
in info[
"data_fields"]:
1523 if field_name == list_field_name:
1524 elem_cpp =
"value_type"
1531 is_bitfield = (isinstance(unwrapped_data,
1533 unwrapped_data.bit_width % 8 != 0)
1535 projection = f
"[](const data_frame &f) {{ return f.{field_name}; }}"
1537 projection = f
"&data_frame::{field_name}"
1539 f
" auto {field_name}() const {{\n"
1540 f
" return std::views::transform(data_frames, {projection});\n"
1542 hdr.write(f
" std::vector<{elem_cpp}> {field_name}_vector() const {{\n"
1543 f
" std::vector<{elem_cpp}> out;\n"
1544 f
" out.reserve(data_frames.size());\n"
1545 f
" for (const auto &frame : data_frames)\n"
1546 f
" out.push_back(frame.{field_name});\n"
1551 """Emit a few bridge helpers + a `TypeDeserializer` alias.
1553 The actual decoder lives in `esi::SerialListTypeDeserializer<T>`, which
1554 walks the header/data/footer burst protocol generically. Each window
1555 helper only has to expose:
1557 - `_headerCount(const header_frame &)` -> `count_type`
1558 - `_fromFrames(const header_frame &, std::vector<data_frame> &&)`
1559 -> `std::unique_ptr<T>`
1561 plus a `friend class esi::SerialListTypeDeserializer<T>;` so the template
1562 can reach the (private) `header_frame` definition.
1564 window_name = info[
"window_name"]
1565 count_field_name = info[
"count_field_name"]
1566 ctor_args =
", ".join(f
"h.{name}" for name, _
in info[
"ctor_params"])
1568 ctor_args = f
"{ctor_args}, std::move(frames)"
1570 ctor_args =
"std::move(frames)"
1573 hdr.write(
"private:\n")
1575 " // Bridge helpers used by esi::SerialListTypeDeserializer<T>; the\n")
1577 " // template walks the serial-list burst protocol generically and\n")
1579 " // reaches into `header_frame` via the friend declaration below.\n")
1580 hdr.write(
" static count_type _headerCount(const header_frame &h) {\n")
1581 hdr.write(f
" return h.{count_field_name};\n")
1583 hdr.write(f
" static std::unique_ptr<{window_name}> _fromFrames(\n")
1585 " const header_frame &h, std::vector<data_frame> &&frames) {\n")
1586 hdr.write(f
" return std::make_unique<{window_name}>({ctor_args});\n")
1589 f
" friend class esi::SerialListTypeDeserializer<{window_name}>;\n\n")
1590 hdr.write(
"public:\n")
1592 f
" using TypeDeserializer = esi::SerialListTypeDeserializer<{window_name}>;\n"
1596 """Emit a using alias when the alias targets a different C++ type."""
1597 inner_wrapped = alias_type.inner_type
1600 if inner_wrapped
is not None:
1601 inner_cpp = self.
_cpp_type(inner_wrapped)
1602 if inner_cpp
is None:
1604 if inner_cpp != alias_name:
1605 hdr.write(f
"using {alias_name} = {inner_cpp};\n\n")
1608 """Emit the fully ordered types.h header into the output directory."""
1609 hdr_file = output_dir /
"types.h"
1610 with open(hdr_file,
"w")
as hdr:
1612 textwrap.dedent(f
"""
1613 // Generated header for {system_name} types.
1622 #include <stdexcept>
1623 #include <string_view>
1627 #include "esi/Common.h"
1628 #include "esi/TypedPorts.h"
1630 namespace {system_name} {{
1634 sys.stderr.write(
"Warning: cyclic type dependencies detected.\n")
1635 sys.stderr.write(
" Logically this should not be possible.\n")
1637 " Emitted code may fail to compile due to ordering issues.\n")
1649 except (ValueError, NotImplementedError)
as e:
1650 sys.stderr.write(f
"Warning: skipping type '{emit_type}': {e}\n")
1651 hdr.write(f
"// Unsupported type '{emit_type}': {e}\n\n")
1653 hdr.write(textwrap.dedent(f
"""
1654 }} // namespace {system_name}
1658def run(generator: Type[Generator] = CppGenerator,
1659 cmdline_args=sys.argv) -> int:
1660 """Create and run a generator reading options from the command line."""
1662 argparser = argparse.ArgumentParser(
1663 description=f
"Generate {generator.language} headers from an ESI manifest",
1664 formatter_class=argparse.RawDescriptionHelpFormatter,
1665 epilog=textwrap.dedent(
"""
1666 Can read the manifest from either a file OR a running accelerator.
1669 # To read the manifest from a file:
1670 esi-cppgen --file /path/to/manifest.json
1672 # To read the manifest from a running accelerator:
1673 esi-cppgen --platform cosim --connection localhost:1234
1676 argparser.add_argument(
"--file",
1679 help=
"Path to the manifest file.")
1680 argparser.add_argument(
1683 help=
"Name of platform for live accelerator connection.")
1684 argparser.add_argument(
1687 help=
"Connection string for live accelerator connection.")
1688 argparser.add_argument(
1692 help=
"Output directory for generated files. Recommend adding either `esi`"
1693 " or the system name to the end of the path so as to avoid header name"
1694 "conflicts. Defaults to `esi`")
1695 argparser.add_argument(
1698 default=
"esi_system",
1699 help=
"Name of the ESI system. For C++, this will be the namespace.")
1701 if (len(cmdline_args) <= 1):
1702 argparser.print_help()
1704 args = argparser.parse_args(cmdline_args[1:])
1706 if args.file
is not None and args.platform
is not None:
1707 print(
"Cannot specify both --file and --platform")
1710 conn: AcceleratorConnection
1711 if args.file
is not None:
1714 conn = Context.default().
connect(
"trace", f
"-{os.pathsep}{args.file}")
1715 elif args.platform
is not None:
1716 if args.connection
is None:
1717 print(
"Must specify --connection with --platform")
1719 conn = Context.default().
connect(args.platform, args.connection)
1721 print(
"Must specify either --file or --platform")
1724 output_dir = Path(args.output_dir)
1725 if output_dir.exists()
and not output_dir.is_dir():
1726 print(f
"Output directory {output_dir} is not a directory")
1728 if not output_dir.exists():
1729 output_dir.mkdir(parents=
True)
1731 gen = generator(conn)
1732 gen.generate(output_dir, args.system_name)
1736if __name__ ==
'__main__':
static void print(TypedAttr val, llvm::raw_ostream &os)
static mlir::Operation * resolve(Context &context, mlir::SymbolRefAttr sym)
static StringAttr append(StringAttr base, const Twine &suffix)
Return a attribute with the specified suffix appended.
_PortGroup _scalar_port_group(self, str member_name, port, appid)
None _emit_module_class(self, str name, str system_name, ModuleInfo module_info, List[_PortGroup] port_groups, TextIO out)
str _cpp_indexed_elem_type(self, port, Optional[str] alias_prefix=None)
Dict[str, object] _build_module_instance_map(self)
str _cpp_member_type(self, port, Optional[str] alias_prefix=None)
str get_consts_str(self, ModuleInfo module_info)
str _cpp_ctor_param_type(self, port)
str _sanitize_id(str name)
List[Tuple[str, str]] _port_using_aliases(self, str alias_prefix, port)
str _port_make_unique_arg(str member_name, port)
__init__(self, AcceleratorConnection conn)
_PortGroup _indexed_ports_group(self, str member_name, str appid_name, port_list)
write_modules(self, Path output_dir, str system_name)
generate(self, Path output_dir, str system_name)
str _cpp_ctor_param_suffix(port)
_PortGroup _mixed_struct_group(self, str member_name, str appid_name, port_list)
List[_PortGroup] _collect_port_groups(self, dict ports)
str _port_find_code(self, str member_name, port, appid)
bool _port_is_connectable(port)
None _emit_struct(self, TextIO hdr, types.StructType struct_type)
str _format_window_field_decl(self, str field_name, types.ESIType field_type)
_analyze_window(self, types.WindowType window_type)
None _emit_size_assert(self, TextIO hdr, str type_name, Optional[int] expected_bytes, str indent="")
None _emit_window_deserializer(self, TextIO hdr, info)
None _emit_window_data_accessors(self, TextIO hdr, info)
str _storage_type(self, int bit_width, bool signed)
str _std_array_type(self, types.ArrayType array_type)
str _get_bitvector_str(self, types.ESIType type)
None write_header(self, Path output_dir, str system_name)
str _format_window_ctor_param(self, str field_name, types.ESIType field_type)
None _emit_alias(self, TextIO hdr, types.TypeAlias alias_type)
None __init__(self, CppTypePlanner planner)
int _field_byte_width(self, types.ESIType field_type)
Tuple[str, List[int]] _array_base_and_dims(self, types.ArrayType array_type)
int _type_byte_width(self, types.ESIType wrapped)
types.ESIType _unwrap_aliases(self, types.ESIType wrapped)
str type_identifier(self, types.ESIType type)
None _emit_union(self, TextIO hdr, types.UnionType union_type)
str _cpp_string_literal(self, str value)
None _emit_window(self, TextIO hdr, types.WindowType window_type)
Optional[int] _safe_byte_width(self, types.ESIType esi_type)
None _emit_window_field_copy(self, TextIO hdr, str dest_expr, str src_expr, types.ESIType field_type)
str _cpp_type(self, types.ESIType wrapped)
str _auto_window_name(self, types.WindowType window_type)
Set[types.ESIType] _collect_decls_from_window(self, types.WindowType window_type)
types.ESIType _unwrap_aliases(self, types.ESIType wrapped)
str _sanitize_name(self, str name)
bool _is_supported_window(self, types.ESIType current_type)
None _visit_types(self, types.ESIType t, Set[str] visited, visit_fn)
str _reserve_name(self, str base, bool is_alias)
Tuple[List[types.ESIType], bool] _ordered_emit_types(self)
List[types.ESIType] _iter_type_children(self, types.ESIType t)
None __init__(self, type_table)
Set[types.ESIType] _collect_decls_from_type(self, types.ESIType wrapped)
str _auto_struct_name(self, types.StructType struct_type)
str _auto_union_name(self, types.UnionType union_type)
None _collect_windows(self, types.ESIType t, Set[str] visited)
None _collect_structs(self, types.ESIType t, Set[str] visited)
None _prepare_types(self, type_table)
None _collect_aliases(self, types.ESIType t, Set[str] visited)
generate(self, Path output_dir, str system_name)
__init__(self, AcceleratorConnection conn)
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
"AcceleratorConnection" connect(str platform, str connection_str)