def _compile_and_link(program_name: str, wiring: list, main_cpp: str): """Compiles and link the graph together with a provided top level file. Parameters ---------- program_name: str Name of the program to generate. wiring: list Wired graph generated via cog. main_cpp: str Top level main file. """ asyncio.run(_wait_for_build(wiring)) _main_h = wiring[program_name + ".h"] _main_a = wiring[program_name + ".a"] # Converting the main.cpp into a BuildArtifact with HostEnv(dir=path.dirname(main_cpp)) as env: _main_cpp = BuildArtifact(name=path.basename(main_cpp), env=env) with CPPEnv() as cpp: main = cpp.compile_and_link( [_main_h], [_main_a], _main_cpp ) return main
def compile_and_link(self, headers, objects, main_cpp): """Run gcc to compile and link and produce a complete runtime binary. Parameters ---------- headers : list :py:class:`BuildArtifact<deltasimulator.build_tools.BuildArtifact>` objects containing the header files. objects : list :py:class:`BuildArtifact<deltasimulator.build_tools.BuildArtifact>` objects containing the binary objects. main_cpp : BuildArtifact The C++ file for main. Returns ------- BuildArtifact The main runtime binary. """ input_files = self._write_files(headers+objects+[main_cpp]) top_name = main_cpp.name.split(".")[0] comp_flag = self._run_gcc(module_name=top_name, after=[input_files]) link_flag = self._link( top_name+".o", archive=objects, after=[comp_flag]) main = BuildArtifact("main", env=self, after=link_flag) return main # self._get_main(after=link_flag)
def _get_rom_init(self, top_v, after): """Check if there are any ROM init files and if so return them. Parameters ---------- top_v : BuildArtifact The Verilog code. after : Coroutine Process which builds the Verilated code. Returns ------- list :class:`BuildArtifact<deltasimulator.build_tools.BuildArtifact>` objects for the ROM init files. """ # Get the Verilog code and search for ROM init code by regex. # TODO: Find a nicer way of getting the init file name. data = asyncio.run(top_v.data) module_regex = b"([a-zA-Z0-9\\_]+)\\.init:\n" inits = [] for match in re.finditer(module_regex, data): rom_name = match[0][:-2].decode("utf8") inits.append(BuildArtifact(name=rom_name, env=self, after=after)) return inits
def test_migen_node(self): with DeltaGraph() as test_graph: c1 = DUT1(tb_num_iter=2000, name='counter1').call(i1=return_1000()) print_then_exit(c1.o1) _, serialised = serialize_graph(test_graph) top_v = BuildArtifact( name=f"{serialised.nodes[1].name}", data=serialised.bodies[1].migen.verilog.encode("utf-8")) with VerilatorEnv() as env: build_artifacts = env.verilate(top_v) asyncio.run(self.assert_build_correct(build_artifacts))
def _get_archive(self, after, name="main"): """Get the .a archive. Parameters ---------- after : Coroutine Coroutine to wait for before the archive is ready. name : Optional[str] Name of the .a file, by default "main" Returns ------- BuildArtifact The content of the .a file. """ return BuildArtifact(f"{name}.a", self, after=after)
def _get_main(self, after, name="main"): """Gets main runtime binary. Parameters ---------- after : Coroutine Coroutine to wait for before getting file. name : Optional[str] The name of the main runtime file, by default "main". Returns ------- BuildArtifact The binary main runtime. """ return BuildArtifact(name, self, after=after)
def _get_h(self, top_p, after): """Get the Header file for the node. Parameters ---------- top_p ``capnp`` object describing the node. after : Coroutine The process that constructs the Header file. Returns ------- bool Returns True upon completion. """ return BuildArtifact(f"{top_p.name}.h", self, after=after)
def _get_py(self, top_p, after): """Get the node's Python script. Parameters ---------- top_p ``capnp`` object describing the node. after : Coroutine Process that needs to complete before the Python script is ready. Returns ------- BuildArtifact The Python script for this node. """ return BuildArtifact(f"{top_p.name}.py", self, after=after)
def _get_verilated_o(self, after): """Once verilated.o is built, return the file. Parameters ---------- after : Coroutine :class:`Coroutine` which is building the verilated.o file. Returns ------- BuildArtifact The verilated.o binary object. """ return BuildArtifact(name="verilated.o", path="obj_dir/", env=self, after=after)
def _get_cpp(self, top_p, after): """Get the C++ code containing the node's SystemC module after it has been built. Parameters ---------- top_p ``capnp`` object describing the node. after : Coroutine Process which builds the C++ code. Returns ------- BuildArtifact The C++ code implementing the node's SystemC module. """ return BuildArtifact(f"{top_p.name}.cpp", self, after=after)
def _get_o(self, module_name, after): """After gcc is run, the binary object file is returned. Parameters ---------- module_name : str The name of the module. Note the .o file type will be appended to the string, so is not needed here. after : Coroutine Coroutine to wait for before the file is ready. Usually the process which builds the file. Returns ------- BuildArtifact The module's binary object. """ return BuildArtifact(f"{module_name}.o", env=self, after=after)
def _get_ALL_a(self, top_v, after): """Once the binary objects have been built return them in a .a archive. Parameters ---------- top_v : BuildArtifact Verilog code file. Only used for the file name. after : Coroutine :class:`Coroutine` which builds the binary objects. Returns ------- BuildArtifact The binary objects in a .a archive. """ mod_name = top_v.name.split(".")[0] h_name = "V" + mod_name + "__ALL.a" h_path = "obj_dir" return BuildArtifact(name=h_name, path=h_path, env=self, after=after)
def _get_h(self, top_v, after): """Get the header file generated by Verilator. Parameters ---------- top_v : BuildArtifact The Verilog code. after : Coroutine Process to wait for before the header file is generated. Returns ------- BuildArtifact The generated header file. """ mod_name = top_v.name.split(".")[0] h_name = "V" + mod_name + ".h" h_path = "obj_dir" return BuildArtifact(name=h_name, path=h_path, after=after, env=self)
def _get_cpp(self, top_v, after): """Returns the C++ file generated by Verilator. Parameters ---------- top_v : BuildArtifact The Verilog code. after : Coroutine Process to wait for before the C++ code is ready. Returns ------- BuildArtifact The C++ code generated. """ mod_name = top_v.name.split(".")[0] cpp_name = "V" + mod_name + ".cpp" cpp_path = "obj_dir" return BuildArtifact(name=cpp_name, path=cpp_path, after=after, env=self)
# write artifacts to build repository for build_artifact_name, build_artifact_data in wiring.items(): print(f"writing: {build_artifact_name} into {build_repo}") with open(build_repo + f"/{build_artifact_name}", "wb") as f: write(build_artifact_data, f) # write python bodies to build repository for py_build_artifact in node_bodies: print(f"py_build_artifact: {py_build_artifact}") print(f"py_build_artifact: {type(py_build_artifact)}") with open(build_repo + f"/{py_build_artifact.name}", "wb") as f: write(py_build_artifact, f) # write init roms to build repository for init_rom_artifact in node_inits: with open(build_repo + f"/{init_rom_artifact.name}", "wb") as f: write(init_rom_artifact, f) # start compilation step with HostEnv(dir=build_repo) as env: main_cpp = BuildArtifact(name=main_cpp_file, env=env) main_h = BuildArtifact(name=program_name + ".h", env=env) main_a = BuildArtifact(name=program_name + ".a", env=env) #verilated_o = BuildArtifact(name="verilated.o", env=env) with CPPEnv() as cpp: main = cpp.compile_and_link([main_h], [main_a], main_cpp) # Compilation workaround - the ar becomes meaningless for another build in a different container. finalize_build()
def generate_wiring( program: _DynamicStructBuilder, excluded_body_tags: List[object] = None, preferred_body_tags: List[object] = None ): """Creates the wiring of the nodes defined in a program. Parameters ---------- program: _DynamicStructBuilder A Deltaflow serialized graph. excluded_body_tags : typing.List[object] typing.List of keys to exclude from selection preferred_body_tags : typing.List[object] typing.List of keys to be preferred for selection if available Returns ------- node_bodies: list The bodies of the extracted nodes. node_inits: list All the ROM files found in the migen nodes, extracted as strings. This solves an incompatibility between some migen generated outputs and verilator. wiring: dict The graph wiring, used to generate a SystemC top level to wire the graph. """ node_headers = [] node_bodies = [] node_modules = [] node_objects = [] node_inits = [] exclusions = excluded_body_tags if excluded_body_tags is not None else [] preferred = preferred_body_tags if preferred_body_tags is not None else [] verilated_o = None exclusions = set(excluded_body_tags if excluded_body_tags is not None else []) preferred = set(preferred_body_tags if preferred_body_tags is not None else []) selected_node_bodies = [] for node in program.nodes: body_id = None if node.bodies: # If there are no node.bodies then it is what was previously # called a template node and cannot be pythonated not_excluded_list = [] for body_id in node.bodies: # If body has no excluded tag body_tags = set(dill.loads(program.bodies[body_id].tags)) if not exclusions & body_tags: not_excluded_list.append(body_id) if not_excluded_list: for body_id in not_excluded_list: # If body has a preferred tag body_tags = set(dill.loads(program.bodies[body_id].tags)) if preferred & body_tags: break # use the first body_id where there is a match else: body_id = not_excluded_list[0] else: raise AttributeError( f"All usable bodies for node '{node.name}' have been excluded!" ) which_body = program.bodies[body_id].which() if which_body in ['python', 'interactive']: with PythonatorEnv(program.bodies) as env: build_artifacts = env.pythonate(node, body_id) node_headers.append(build_artifacts["h"]) if "py" in build_artifacts: node_bodies.append(build_artifacts["py"]) node_modules.append(build_artifacts["cpp"]) node_objects.append(build_artifacts["o"]) elif which_body == 'migen': # This part is adopted from initial-example: top_v = BuildArtifact( name=f"{node.name}.v", data=program.bodies[body_id].migen.verilog.encode("utf8") ) with VerilatorEnv() as env: build_artifacts = env.verilate(top_v) node_headers.append(build_artifacts["h"]) node_modules.append(build_artifacts["cpp"]) node_objects.append(build_artifacts["ALL.a"]) node_inits += build_artifacts["init"] if not verilated_o: verilated_o = build_artifacts["verilated.o"] selected_node_bodies.append(body_id) with WiringEnv(program.nodes, selected_node_bodies, program.bodies, node_headers, node_objects, verilated_o, program.name) as env: wiring = env.wiring(program.graph) return node_bodies, node_inits, wiring