CIRCT 20.0.0git
Loading...
Searching...
No Matches
codegen.py
Go to the documentation of this file.
1# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
2# See https://llvm.org/LICENSE.txt for license information.
3# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5# Code generation from ESI manifests to source code. C++ header support included
6# with the runtime, though it is intended to be extensible for other languages.
7
8from typing import List, TextIO, Type, Optional
9from .accelerator import AcceleratorConnection
10from .esiCppAccel import ModuleInfo
11from . import types
12
13import argparse
14from pathlib import Path
15import textwrap
16import sys
17
18_thisdir = Path(__file__).absolute().resolve().parent
19
20
22 """Base class for all generators."""
23
24 language: Optional[str] = None
25
26 def __init__(self, conn: AcceleratorConnection):
27 self.manifest = conn.manifest()
28
29 def generate(self, output_dir: Path, system_name: str):
30 raise NotImplementedError("Generator.generate() must be overridden")
31
32
34 """Generate C++ headers from an ESI manifest."""
35
36 language = "C++"
37
38 # Supported bit widths for lone integer types.
39 int_width_support = set([8, 16, 32, 64])
40
41 def get_type_str(self, type: types.ESIType) -> str:
42 """Get the textual code for the storage class of a type.
43
44 Examples: uint32_t, int64_t, CustomStruct."""
45
46 if isinstance(type, (types.BitsType, types.IntType)):
47 if type.bit_width not in self.int_width_support:
48 raise ValueError(f"Unsupported integer width: {type.bit_width}")
49 if isinstance(type, (types.BitsType, types.UIntType)):
50 return f"uint{type.bit_width}_t"
51 return f"int{type.bit_width}_t"
52 raise NotImplementedError(f"Type '{type}' not supported for C++ generation")
53
54 def get_consts_str(self, module_info: ModuleInfo) -> str:
55 """Get the C++ code for a constant in a module."""
56 const_strs: List[str] = [
57 f"static constexpr {self.get_type_str(const.type)} "
58 f"{name} = 0x{const.value:x};"
59 for name, const in module_info.constants.items()
60 ]
61 return "\n".join(const_strs)
62
63 def write_modules(self, output_dir: Path, system_name: str):
64 """Write the C++ header. One for each module in the manifest."""
65
66 for module_info in self.manifest.module_infos:
67 s = f"""
68 /// Generated header for {system_name} module {module_info.name}.
69 #pragma once
70 #include "types.h"
71
72 namespace {system_name} {{
73 class {module_info.name} {{
74 public:
75 {self.get_consts_str(module_info)}
76 }};
77 }} // namespace {system_name}
78 """
79
80 hdr_file = output_dir / f"{module_info.name}.h"
81 with open(hdr_file, "w") as hdr:
82 hdr.write(textwrap.dedent(s))
83
84 def write_type(self, hdr: TextIO, type: types.ESIType):
85 if isinstance(type, (types.BitsType, types.IntType)):
86 # Bit vector types use standard C++ types.
87 return
88 raise NotImplementedError(f"Type '{type}' not supported for C++ generation")
89
90 def write_types(self, output_dir: Path, system_name: str):
91 hdr_file = output_dir / "types.h"
92 with open(hdr_file, "w") as hdr:
93 hdr.write(
94 textwrap.dedent(f"""
95 // Generated header for {system_name} types.
96 #pragma once
97
98 #include <cstdint>
99
100 namespace {system_name} {{
101 """))
102
103 for type in self.manifest.type_table:
104 try:
105 self.write_type(hdr, type)
106 except NotImplementedError:
107 sys.stderr.write(
108 f"Warning: type '{type}' not supported for C++ generation\n")
109
110 hdr.write(
111 textwrap.dedent(f"""
112 }} // namespace {system_name}
113 """))
114
115 def generate(self, output_dir: Path, system_name: str):
116 self.write_types(output_dir, system_name)
117 self.write_modules(output_dir, system_name)
118
119
120def run(generator: Type[Generator] = CppGenerator,
121 cmdline_args=sys.argv) -> int:
122 """Create and run a generator reading options from the command line."""
123
124 argparser = argparse.ArgumentParser(
125 description=f"Generate {generator.language} headers from an ESI manifest",
126 formatter_class=argparse.RawDescriptionHelpFormatter,
127 epilog=textwrap.dedent("""
128 Can read the manifest from either a file OR a running accelerator.
129
130 Usage examples:
131 # To read the manifest from a file:
132 esi-cppgen --file /path/to/manifest.json
133
134 # To read the manifest from a running accelerator:
135 esi-cppgen --platform cosim --connection localhost:1234
136 """))
137
138 argparser.add_argument("--file",
139 type=str,
140 default=None,
141 help="Path to the manifest file.")
142 argparser.add_argument(
143 "--platform",
144 type=str,
145 help="Name of platform for live accelerator connection.")
146 argparser.add_argument(
147 "--connection",
148 type=str,
149 help="Connection string for live accelerator connection.")
150 argparser.add_argument(
151 "--output-dir",
152 type=str,
153 default="esi",
154 help="Output directory for generated files. Recommend adding either `esi`"
155 " or the system name to the end of the path so as to avoid header name"
156 "conflicts. Defaults to `esi`")
157 argparser.add_argument(
158 "--system-name",
159 type=str,
160 default="esi_system",
161 help="Name of the ESI system. For C++, this will be the namespace.")
162
163 if (len(cmdline_args) <= 1):
164 argparser.print_help()
165 return 1
166 args = argparser.parse_args(cmdline_args[1:])
167
168 if args.file is not None and args.platform is not None:
169 print("Cannot specify both --file and --platform")
170 return 1
171
172 conn: AcceleratorConnection
173 if args.file is not None:
174 conn = AcceleratorConnection("trace", f"-:{args.file}")
175 elif args.platform is not None:
176 if args.connection is None:
177 print("Must specify --connection with --platform")
178 return 1
179 conn = AcceleratorConnection(args.platform, args.connection)
180 else:
181 print("Must specify either --file or --platform")
182 return 1
183
184 output_dir = Path(args.output_dir)
185 if output_dir.exists() and not output_dir.is_dir():
186 print(f"Output directory {output_dir} is not a directory")
187 return 1
188 if not output_dir.exists():
189 output_dir.mkdir(parents=True)
190
191 gen = generator(conn)
192 gen.generate(output_dir, args.system_name)
193 return 0
194
195
196if __name__ == '__main__':
197 sys.exit(run())
write_type(self, TextIO hdr, types.ESIType type)
Definition codegen.py:84
str get_type_str(self, types.ESIType type)
Definition codegen.py:41
str get_consts_str(self, ModuleInfo module_info)
Definition codegen.py:54
write_modules(self, Path output_dir, str system_name)
Definition codegen.py:63
generate(self, Path output_dir, str system_name)
Definition codegen.py:115
write_types(self, Path output_dir, str system_name)
Definition codegen.py:90
generate(self, Path output_dir, str system_name)
Definition codegen.py:29
__init__(self, AcceleratorConnection conn)
Definition codegen.py:26
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
Definition codegen.py:121