diff --git a/litex/soc/integration/builder.py b/litex/soc/integration/builder.py index ac0a8add3..0e64b53fb 100644 --- a/litex/soc/integration/builder.py +++ b/litex/soc/integration/builder.py @@ -22,67 +22,91 @@ from litex.build.tools import write_to_file from litex.soc.integration import export, soc_core from litex.soc.cores import cpu -__all__ = [ - "soc_software_packages", - "soc_directory", - "Builder", - "builder_args", - "builder_argdict" -] - - -soc_software_packages = [ - "libcompiler_rt", - "libbase", - "liblitedram", - "libliteeth", - "liblitespi", - "libfatfs", - "liblitesdcard", - "liblitesata", - "bios" -] - - -soc_directory = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) - +# Helpers ------------------------------------------------------------------------------------------ def _makefile_escape(s): return s.replace("\\", "\\\\") +def _create_dir(d): + os.makedirs(os.path.realpath(d), exist_ok=True) + +# Software Packages -------------------------------------------------------------------------------- + +soc_software_packages = [ + # Compiler-RT. + "libcompiler_rt", + + # LiteX cores. + "libbase", + + # LiteX Ecosystem cores. + "libfatfs", + "liblitespi", + "liblitedram", + "libliteeth", + "liblitesdcard", + "liblitesata", + + # BIOS. + "bios" +] + +# Builder ------------------------------------------------------------------------------------------ + +soc_directory = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +compiler_rt_directory = get_data_mod("software", "compiler_rt").data_location class Builder: def __init__(self, soc, + # Directories. output_dir = None, gateware_dir = None, software_dir = None, include_dir = None, generated_dir = None, + + # Compile Options. compile_software = True, compile_gateware = True, + + # Exports. csr_json = None, csr_csv = None, csr_svd = None, memory_x = None, + + # BIOS Options. bios_options = [], + + # Documentation. generate_doc = False): + self.soc = soc - # From Python doc: makedirs() will become confused if the path elements to create include '..' + # Directories. self.output_dir = os.path.abspath(output_dir or os.path.join("build", soc.platform.name)) self.gateware_dir = os.path.abspath(gateware_dir or os.path.join(self.output_dir, "gateware")) self.software_dir = os.path.abspath(software_dir or os.path.join(self.output_dir, "software")) self.include_dir = os.path.abspath(include_dir or os.path.join(self.software_dir, "include")) self.generated_dir = os.path.abspath(generated_dir or os.path.join(self.include_dir, "generated")) + # Compile Options. self.compile_software = compile_software self.compile_gateware = compile_gateware - self.csr_csv = csr_csv - self.csr_json = csr_json - self.csr_svd = csr_svd - self.memory_x = memory_x - self.bios_options = bios_options - self.generate_doc = generate_doc + + # Exports. + self.csr_csv = csr_csv + self.csr_json = csr_json + self.csr_svd = csr_svd + self.memory_x = memory_x + + # BIOS Options. + self.bios_options = bios_options + + # Documentation + self.generate_doc = generate_doc + + # List software packages. self.software_packages = [] for name in soc_software_packages: self.add_software_package(name) @@ -93,130 +117,164 @@ class Builder: self.software_packages.append((name, src_dir)) def _generate_includes(self): - os.makedirs(self.include_dir, exist_ok=True) - os.makedirs(self.generated_dir, exist_ok=True) + # Generate Include/Generated directories. + _create_dir(self.include_dir) + _create_dir(self.generated_dir) - if self.soc.cpu_type not in [None, "zynq7000"]: + # Generate BIOS files when the SoC uses it. + with_bios = self.soc.cpu_type not in [None, "zynq7000"] + if with_bios: + + # Generate Variables to variables.mak. variables_contents = [] def define(k, v): - variables_contents.append("{}={}\n".format(k, _makefile_escape(v))) + variables_contents.append("{}={}".format(k, _makefile_escape(v))) + # Define the CPU variables. for k, v in export.get_cpu_mak(self.soc.cpu, self.compile_software): define(k, v) - define( - "COMPILER_RT_DIRECTORY", - get_data_mod("software", "compiler_rt").data_location) - define("SOC_DIRECTORY", soc_directory) - variables_contents.append("export BUILDINC_DIRECTORY\n") + + # Define the SoC/Compiler-RT/Software/Include directories. + define("SOC_DIRECTORY", soc_directory) + define("COMPILER_RT_DIRECTORY", compiler_rt_directory) + variables_contents.append("export BUILDINC_DIRECTORY") define("BUILDINC_DIRECTORY", self.include_dir) for name, src_dir in self.software_packages: define(name.upper() + "_DIRECTORY", src_dir) + # Define the BIOS Options. for bios_option in self.bios_options: assert bios_option in ["TERM_NO_HIST", "TERM_MINI", "TERM_NO_COMPLETE"] define(bios_option, "1") - write_to_file( - os.path.join(self.generated_dir, "variables.mak"), - "".join(variables_contents)) - write_to_file( - os.path.join(self.generated_dir, "output_format.ld"), - export.get_linker_output_format(self.soc.cpu)) - write_to_file( - os.path.join(self.generated_dir, "regions.ld"), - export.get_linker_regions(self.soc.mem_regions)) + # Write to variables.mak. + write_to_file(os.path.join(self.generated_dir, "variables.mak"), "\n".join(variables_contents)) - write_to_file( - os.path.join(self.generated_dir, "mem.h"), - export.get_mem_header(self.soc.mem_regions)) - write_to_file( - os.path.join(self.generated_dir, "soc.h"), - export.get_soc_header(self.soc.constants)) - write_to_file( - os.path.join(self.generated_dir, "csr.h"), - export.get_csr_header( - regions = self.soc.csr_regions, - constants = self.soc.constants, - csr_base = self.soc.mem_regions['csr'].origin - ) - ) - write_to_file( - os.path.join(self.generated_dir, "git.h"), - export.get_git_header() - ) + # Generate Output Format to output_format.ld. + output_format_contents = export.get_linker_output_format(self.soc.cpu) + write_to_file(os.path.join(self.generated_dir, "output_format.ld"), output_format_contents) + # Generate Memory Regions to regions.ld. + regions_contents = export.get_linker_regions(self.soc.mem_regions) + write_to_file(os.path.join(self.generated_dir, "regions.ld"), regions_contents) + + # Generate Memory Regions to mem.h. + mem_contents = export.get_mem_header(self.soc.mem_regions) + write_to_file(os.path.join(self.generated_dir, "mem.h"), mem_contents) + + # Generate Memory Regions to memory.x if specified. + if self.memory_x is not None: + memory_x_contents = export.get_memory_x(self.soc) + write_to_file(os.path.realpath(self.memory_x), memory_x_contents) + + # Generate SoC Config/Constants to soc.h. + soc_contents = export.get_soc_header(self.soc.constants) + write_to_file(os.path.join(self.generated_dir, "soc.h"), soc_contents) + + # Generate CSR registers definitions/access functions to csr.h. + csr_contents = export.get_csr_header( + regions = self.soc.csr_regions, + constants = self.soc.constants, + csr_base = self.soc.mem_regions['csr'].origin) + write_to_file(os.path.join(self.generated_dir, "csr.h"), csr_contents) + + # Generate Git SHA1 of tools to git.h + git_contents = export.get_git_header() + write_to_file(os.path.join(self.generated_dir, "git.h"), git_contents) + + # Generate LiteDRAM C header to sdram_phy.h when the SoC use it. if hasattr(self.soc, "sdram"): from litedram.init import get_sdram_phy_c_header - write_to_file(os.path.join(self.generated_dir, "sdram_phy.h"), - get_sdram_phy_c_header( - self.soc.sdram.controller.settings.phy, - self.soc.sdram.controller.settings.timing)) + sdram_contents = get_sdram_phy_c_header( + self.soc.sdram.controller.settings.phy, + self.soc.sdram.controller.settings.timing) + write_to_file(os.path.join(self.generated_dir, "sdram_phy.h"), sdram_contents) def _generate_csr_map(self): + # JSON Export. if self.csr_json is not None: - csr_dir = os.path.dirname(os.path.realpath(self.csr_json)) - os.makedirs(csr_dir, exist_ok=True) - write_to_file(self.csr_json, export.get_csr_json(self.soc.csr_regions, self.soc.constants, self.soc.mem_regions)) + csr_json_contents = export.get_csr_json( + csr_regions = self.soc.csr_regions, + constants = self.soc.constants, + mem_regions = self.soc.mem_regions) + write_to_file(os.path.realpath(self.csr_json), csr_json_contents) + # CSV Export. if self.csr_csv is not None: - csr_dir = os.path.dirname(os.path.realpath(self.csr_csv)) - os.makedirs(csr_dir, exist_ok=True) - write_to_file(self.csr_csv, export.get_csr_csv(self.soc.csr_regions, self.soc.constants, self.soc.mem_regions)) + csr_csv_contents = export.get_csr_csv( + csr_regions = self.soc.csr_regions, + constants = self.soc.constants, + mem_regions = self.soc.mem_regions) + write_to_file(os.path.realpath(self.csr_csv), csr_csv_contents) + # SVD Export. if self.csr_svd is not None: - svd_dir = os.path.dirname(os.path.realpath(self.csr_svd)) - os.makedirs(svd_dir, exist_ok=True) - write_to_file(self.csr_svd, export.get_csr_svd(self.soc)) - - def _generate_mem_region_map(self): - if self.memory_x is not None: - memory_x_dir = os.path.dirname(os.path.realpath(self.memory_x)) - os.makedirs(memory_x_dir, exist_ok=True) - write_to_file(self.memory_x, export.get_memory_x(self.soc)) + csr_svd_contents = export.get_csr_svd(self.soc) + write_to_file(os.path.realpath(self.csr_svd), csr_svd_contents) def _prepare_rom_software(self): + # Create directories for all software packages. for name, src_dir in self.software_packages: - dst_dir = os.path.join(self.software_dir, name) - os.makedirs(dst_dir, exist_ok=True) + _create_dir(os.path.join(self.software_dir, name)) def _generate_rom_software(self, compile_bios=True): + # Compile all software packages. for name, src_dir in self.software_packages: + # Skip BIOS compilation when disabled. if name == "bios" and not compile_bios: - pass - else: - dst_dir = os.path.join(self.software_dir, name) - makefile = os.path.join(src_dir, "Makefile") - if self.compile_software: - subprocess.check_call(["make", "-C", dst_dir, "-f", makefile]) + continue + # Compile software package. + dst_dir = os.path.join(self.software_dir, name) + makefile = os.path.join(src_dir, "Makefile") + if self.compile_software: + subprocess.check_call(["make", "-C", dst_dir, "-f", makefile]) def _initialize_rom_software(self): + # Get BIOS data from compiled BIOS binary. bios_file = os.path.join(self.software_dir, "bios", "bios.bin") bios_data = soc_core.get_mem_data(bios_file, self.soc.cpu.endianness) + + # Initialize SoC with with BIOS data. self.soc.initialize_rom(bios_data) def build(self, **kwargs): + # Pass Output Directory to Platform. self.soc.platform.output_dir = self.output_dir - os.makedirs(self.gateware_dir, exist_ok=True) - os.makedirs(self.software_dir, exist_ok=True) + # Create Gateware/Software directories. + _create_dir(self.gateware_dir) + _create_dir(self.software_dir) + + # Finalize the SoC. self.soc.finalize() + # Generate Software Includes/Files. self._generate_includes() + + # Export SoC Mapping. self._generate_csr_map() - self._generate_mem_region_map() + + # Compile the BIOS when the SoC uses it. if self.soc.cpu_type is not None: if self.soc.cpu.use_rom: + # Prepare/Generate ROM software. self._prepare_rom_software() self._generate_rom_software(not self.soc.integrated_rom_initialized) + + # Initialize ROM. if self.soc.integrated_rom_size and self.compile_software: if not self.soc.integrated_rom_initialized: self._initialize_rom_software() + # Translate compile_gateware to run. if "run" not in kwargs: kwargs["run"] = self.compile_gateware + + # Build SoC and pass Verilog Name Space to do_exit. vns = self.soc.build(build_dir=self.gateware_dir, **kwargs) self.soc.do_exit(vns=vns) + # Generate SoC Documentation. if self.generate_doc: from litex.soc.doc import generate_docs doc_dir = os.path.join(self.output_dir, "doc") @@ -225,38 +283,21 @@ class Builder: return vns +# Builder Arguments -------------------------------------------------------------------------------- + def builder_args(parser): - parser.add_argument("--output-dir", default=None, - help="base output directory for generated " - "source files and binaries (customizable " - "with --{gateware,software,include,generated}-dir)") - parser.add_argument("--gateware-dir", default=None, - help="output directory for gateware files") - parser.add_argument("--software-dir", default=None, - help="base output directory for software files") - parser.add_argument("--include-dir", default=None, - help="output directory for header files") - parser.add_argument("--generated-dir", default=None, - help="output directory for various generated files") - parser.add_argument("--no-compile-software", action="store_true", - help="do not compile the software, only generate " - "build infrastructure") - parser.add_argument("--no-compile-gateware", action="store_true", - help="do not compile the gateware, only generate " - "HDL source files and build scripts") - parser.add_argument("--csr-csv", default=None, - help="store CSR map in CSV format into the " - "specified file") - parser.add_argument("--csr-json", default=None, - help="store CSR map in JSON format into the " - "specified file") - parser.add_argument("--csr-svd", default=None, - help="store CSR map in SVD format into the " - "specified file") - parser.add_argument("--memory-x", default=None, - help="store Mem regions in memory-x format into the " - "specified file") - parser.add_argument("--doc", action="store_true", help="Generate Documentation") + parser.add_argument("--output-dir", default=None, help="Base Output directory (customizable with --{gateware,software,include,generated}-dir).") + parser.add_argument("--gateware-dir", default=None, help="Output directory for Gateware files.") + parser.add_argument("--software-dir", default=None, help="Output directory for Software files.") + parser.add_argument("--include-dir", default=None, help="Output directory for Header files.") + parser.add_argument("--generated-dir", default=None, help="Output directory for Generated files.") + parser.add_argument("--no-compile-software", action="store_true", help="Disable Software compilation.") + parser.add_argument("--no-compile-gateware", action="store_true", help="Disable Gateware compilation.") + parser.add_argument("--csr-csv", default=None, help="Write SoC mapping to the specified CSV file.") + parser.add_argument("--csr-json", default=None, help="Write SoC mapping to the specified JSON file.") + parser.add_argument("--csr-svd", default=None, help="Write SoC mapping to the specified SVD file.") + parser.add_argument("--memory-x", default=None, help="Write SoC Memory Regions to the specified Memory-X file.") + parser.add_argument("--doc", action="store_true", help="Generate SoC Documentation.") def builder_argdict(args):