├── .gitattributes ├── .gitignore ├── LICENSE ├── README.asciidoc ├── lxbuildenv.py └── setup.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Text for humans 2 | LICENSE text eol=lf 3 | README.* text eol=lf 4 | 5 | # Exclave files 6 | *.interface text eol=lf 7 | *.jig text eol=lf 8 | *.logger text eol=lf 9 | *.scenario text eol=lf 10 | *.test text eol=lf 11 | *.trigger text eol=lf 12 | *.service text eol=lf 13 | 14 | # Source code 15 | Makefile text eol=lf 16 | *.mk text eol=lf 17 | *.mak text eol=lf 18 | *.py text eol=lf 19 | *.sh text eol=lf 20 | *.c text eol=lf 21 | *.S text eol=lf 22 | *.s text eol=lf 23 | *.h text eol=lf 24 | *.rs 25 | *.ld text eol=lf 26 | 27 | # Windows source code uses CRLF 28 | *.vcxproj text eol=crlf 29 | *.props text eol=crlf 30 | *.bat text eol=crlf 31 | 32 | # Other binary files 33 | *.png binary 34 | *.jpg binary 35 | *.bin binary 36 | *.elf binary 37 | *.bin binary 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # C compilation artifacts 2 | .obj/ 3 | *.o 4 | *.bin 5 | *.dfu 6 | *.ihex 7 | *.elf 8 | 9 | # Swapfiles 10 | *.swp 11 | *.swo 12 | *~ 13 | 14 | .vscode/ 15 | 16 | # Python cache files 17 | __pycache__/ 18 | *.pyc 19 | 20 | # Environment files generated by lxbuildenv 21 | .env 22 | 23 | # Output from LiteX 24 | build/ 25 | test/ 26 | 27 | # Output from yosys 28 | abc.history -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | Copyright 2018 Sean Cross 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 20 | 21 | 1. Definitions. 22 | 23 | "License" shall mean the terms and conditions for use, reproduction, 24 | and distribution as defined by Sections 1 through 9 of this document. 25 | 26 | "Licensor" shall mean the copyright owner or entity authorized by 27 | the copyright owner that is granting the License. 28 | 29 | "Legal Entity" shall mean the union of the acting entity and all 30 | other entities that control, are controlled by, or are under common 31 | control with that entity. For the purposes of this definition, 32 | "control" means (i) the power, direct or indirect, to cause the 33 | direction or management of such entity, whether by contract or 34 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 35 | outstanding shares, or (iii) beneficial ownership of such entity. 36 | 37 | "You" (or "Your") shall mean an individual or Legal Entity 38 | exercising permissions granted by this License. 39 | 40 | "Source" form shall mean the preferred form for making modifications, 41 | including but not limited to software source code, documentation 42 | source, and configuration files. 43 | 44 | "Object" form shall mean any form resulting from mechanical 45 | transformation or translation of a Source form, including but 46 | not limited to compiled object code, generated documentation, 47 | and conversions to other media types. 48 | 49 | "Work" shall mean the work of authorship, whether in Source or 50 | Object form, made available under the License, as indicated by a 51 | copyright notice that is included in or attached to the work 52 | (an example is provided in the Appendix below). 53 | 54 | "Derivative Works" shall mean any work, whether in Source or Object 55 | form, that is based on (or derived from) the Work and for which the 56 | editorial revisions, annotations, elaborations, or other modifications 57 | represent, as a whole, an original work of authorship. For the purposes 58 | of this License, Derivative Works shall not include works that remain 59 | separable from, or merely link (or bind by name) to the interfaces of, 60 | the Work and Derivative Works thereof. 61 | 62 | "Contribution" shall mean any work of authorship, including 63 | the original version of the Work and any modifications or additions 64 | to that Work or Derivative Works thereof, that is intentionally 65 | submitted to Licensor for inclusion in the Work by the copyright owner 66 | or by an individual or Legal Entity authorized to submit on behalf of 67 | the copyright owner. For the purposes of this definition, "submitted" 68 | means any form of electronic, verbal, or written communication sent 69 | to the Licensor or its representatives, including but not limited to 70 | communication on electronic mailing lists, source code control systems, 71 | and issue tracking systems that are managed by, or on behalf of, the 72 | Licensor for the purpose of discussing and improving the Work, but 73 | excluding communication that is conspicuously marked or otherwise 74 | designated in writing by the copyright owner as "Not a Contribution." 75 | 76 | "Contributor" shall mean Licensor and any individual or Legal Entity 77 | on behalf of whom a Contribution has been received by Licensor and 78 | subsequently incorporated within the Work. 79 | 80 | 2. Grant of Copyright License. Subject to the terms and conditions of 81 | this License, each Contributor hereby grants to You a perpetual, 82 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 83 | copyright license to reproduce, prepare Derivative Works of, 84 | publicly display, publicly perform, sublicense, and distribute the 85 | Work and such Derivative Works in Source or Object form. 86 | 87 | 3. Grant of Patent License. Subject to the terms and conditions of 88 | this License, each Contributor hereby grants to You a perpetual, 89 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 90 | (except as stated in this section) patent license to make, have made, 91 | use, offer to sell, sell, import, and otherwise transfer the Work, 92 | where such license applies only to those patent claims licensable 93 | by such Contributor that are necessarily infringed by their 94 | Contribution(s) alone or by combination of their Contribution(s) 95 | with the Work to which such Contribution(s) was submitted. If You 96 | institute patent litigation against any entity (including a 97 | cross-claim or counterclaim in a lawsuit) alleging that the Work 98 | or a Contribution incorporated within the Work constitutes direct 99 | or contributory patent infringement, then any patent licenses 100 | granted to You under this License for that Work shall terminate 101 | as of the date such litigation is filed. 102 | 103 | 4. Redistribution. You may reproduce and distribute copies of the 104 | Work or Derivative Works thereof in any medium, with or without 105 | modifications, and in Source or Object form, provided that You 106 | meet the following conditions: 107 | 108 | (a) You must give any other recipients of the Work or 109 | Derivative Works a copy of this License; and 110 | 111 | (b) You must cause any modified files to carry prominent notices 112 | stating that You changed the files; and 113 | 114 | (c) You must retain, in the Source form of any Derivative Works 115 | that You distribute, all copyright, patent, trademark, and 116 | attribution notices from the Source form of the Work, 117 | excluding those notices that do not pertain to any part of 118 | the Derivative Works; and 119 | 120 | (d) If the Work includes a "NOTICE" text file as part of its 121 | distribution, then any Derivative Works that You distribute must 122 | include a readable copy of the attribution notices contained 123 | within such NOTICE file, excluding those notices that do not 124 | pertain to any part of the Derivative Works, in at least one 125 | of the following places: within a NOTICE text file distributed 126 | as part of the Derivative Works; within the Source form or 127 | documentation, if provided along with the Derivative Works; or, 128 | within a display generated by the Derivative Works, if and 129 | wherever such third-party notices normally appear. The contents 130 | of the NOTICE file are for informational purposes only and 131 | do not modify the License. You may add Your own attribution 132 | notices within Derivative Works that You distribute, alongside 133 | or as an addendum to the NOTICE text from the Work, provided 134 | that such additional attribution notices cannot be construed 135 | as modifying the License. 136 | 137 | You may add Your own copyright statement to Your modifications and 138 | may provide additional or different license terms and conditions 139 | for use, reproduction, or distribution of Your modifications, or 140 | for any such Derivative Works as a whole, provided Your use, 141 | reproduction, and distribution of the Work otherwise complies with 142 | the conditions stated in this License. 143 | 144 | 5. Submission of Contributions. Unless You explicitly state otherwise, 145 | any Contribution intentionally submitted for inclusion in the Work 146 | by You to the Licensor shall be under the terms and conditions of 147 | this License, without any additional terms or conditions. 148 | Notwithstanding the above, nothing herein shall supersede or modify 149 | the terms of any separate license agreement you may have executed 150 | with Licensor regarding such Contributions. 151 | 152 | 6. Trademarks. This License does not grant permission to use the trade 153 | names, trademarks, service marks, or product names of the Licensor, 154 | except as required for reasonable and customary use in describing the 155 | origin of the Work and reproducing the content of the NOTICE file. 156 | 157 | 7. Disclaimer of Warranty. Unless required by applicable law or 158 | agreed to in writing, Licensor provides the Work (and each 159 | Contributor provides its Contributions) on an "AS IS" BASIS, 160 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 161 | implied, including, without limitation, any warranties or conditions 162 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 163 | PARTICULAR PURPOSE. You are solely responsible for determining the 164 | appropriateness of using or redistributing the Work and assume any 165 | risks associated with Your exercise of permissions under this License. 166 | 167 | 8. Limitation of Liability. In no event and under no legal theory, 168 | whether in tort (including negligence), contract, or otherwise, 169 | unless required by applicable law (such as deliberate and grossly 170 | negligent acts) or agreed to in writing, shall any Contributor be 171 | liable to You for damages, including any direct, indirect, special, 172 | incidental, or consequential damages of any character arising as a 173 | result of this License or out of the use or inability to use the 174 | Work (including but not limited to damages for loss of goodwill, 175 | work stoppage, computer failure or malfunction, or any and all 176 | other commercial damages or losses), even if such Contributor 177 | has been advised of the possibility of such damages. 178 | 179 | 9. Accepting Warranty or Additional Liability. While redistributing 180 | the Work or Derivative Works thereof, You may choose to offer, 181 | and charge a fee for, acceptance of support, warranty, indemnity, 182 | or other liability obligations and/or rights consistent with this 183 | License. However, in accepting such obligations, You may act only 184 | on Your own behalf and on Your sole responsibility, not on behalf 185 | of any other Contributor, and only if You agree to indemnify, 186 | defend, and hold each Contributor harmless for any liability 187 | incurred by, or claims asserted against, such Contributor by reason 188 | of your accepting any such warranty or additional liability. 189 | 190 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /README.asciidoc: -------------------------------------------------------------------------------- 1 | :toc: 2 | :toc-placement!: 3 | ifdef::env-github[] 4 | :tip-caption: :bulb: 5 | :note-caption: :information_source: 6 | :important-caption: :heavy_exclamation_mark: 7 | :caution-caption: :fire: 8 | :warning-caption: :warning: 9 | endif::[] 10 | 11 | # lxbuildenv - Simple LiteX Build Environment 12 | 13 | lxbuildenv is a Python module that takes care of setting up your build environment for LiteX. It is simple to use, cross-platform, and self-contained. 14 | 15 | ## Usage - Initial Setup 16 | 17 | NOTE: The initial setup process is, admittedly, a little rough. Patches are welcome. 18 | 19 | You can execute `lxbuildenv.py` directly. Run `python lxbuildenv.py --init` to create a new project. This will perform the following steps: 20 | 21 | 1. Ask you for a main entrypoint name, if `--main` was not set 22 | 1. Create a git repository, if one does not already exist, unless `--no-git` was set 23 | 1. Create a `bin` directory and populate it, if `--no-bin` was not set 24 | 1. Create an initial template program 25 | 26 | From this point onwards, you do not need to invoke `lxbuildenv.py`. However, it must still be present in order for your program to run. 27 | 28 | ## Usage - Running your Program 29 | 30 | The purpose of `lxbuildenv.py` is to make it easier to get started on a project. It's also designed to help you work on support libraries. It tries to use system dependencies whenever possible, including using system Python and system compilers. 31 | 32 | As such, `lxbuildenv.py` does some tricky things with `PYTHONPATH`. It also re-executes itself to ensure `PYTHONHASHSEED` is set. Additionally, it ensures you have any dependencies installed already. 33 | 34 | It does not download dependencies for you -- for certain software that's not even legally allowed. Instead it makes sure the software exists and is usable, and where to obtain it if it's not available. 35 | 36 | The `lxbuildenv.py` script adds several command line arguments to your program. These all begin with `--lx-`. To list all parameters provided by `lxbuildenv.py`, run `--lx-help`. 37 | 38 | WARNING: Because it adjusts the import path, you must `import lxbuildenv` before you import any other packages. 39 | 40 | ### Specifying Dependencies 41 | 42 | Dependencies are specified by adding a variable in your main file called `LX_DEPENDENCIES`. The fact that this is a variable rather than some other method should set off alarm bells and tell you something funny is going on in `lxbuildenv.py`. This is true. 43 | 44 | As part of its initialization, `lxbuildenv.py` will read in your program and look for this variable. It will then check any dependencies you've specified to ensure they are met, and may patch up the environment if necessary. For example, if you specify `vivado` as a dependency, `lxbuildenv` will make sure it can run the `vivado` program, and will add `/opt/Xilinx/.../` or `C:\\Xilinx\\...` to the `PATH` as necessary. 45 | 46 | To list all possible dependencies, run `python lxbuildenv.py --lx-print-deps`. If you want to try building something without having all the necessary dependencies, add `--lx-ignore-deps` to your build. 47 | 48 | ## Usage - Tools 49 | 50 | As part of its setup, `lxbuildenv.py` creates a `bin/` directory. These are all very thin wrappers around existing tools. You can run these tools using `python`. For example, `python bin/litex_server`. -------------------------------------------------------------------------------- /lxbuildenv.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This script enables easy, cross-platform building without the need 4 | # to install third-party Python modules. 5 | 6 | LXBUILDENV_VERSION = '2020.6.1.1' 7 | import sys 8 | import os 9 | import subprocess 10 | import argparse 11 | 12 | 13 | DEPS_DIR = "deps" 14 | 15 | DEFAULT_DEPS = { 16 | 'migen': 'https://github.com/m-labs/migen.git', 17 | 'litex': 'https://github.com/enjoy-digital/litex.git', 18 | 'litedram': 'https://github.com/enjoy-digital/litedram.git', 19 | 'litex_boards': 'https://github.com/litex-hub/litex-boards.git', 20 | } 21 | 22 | OPTIONAL_DEPS = { 23 | 'liteeth': 'https://github.com/enjoy-digital/liteeth.git', 24 | 'liteusb': 'https://github.com/enjoy-digital/liteusb.git', 25 | 'litepcie': 'https://github.com/enjoy-digital/litepcie.git', 26 | 'litesdcard': 'https://github.com/enjoy-digital/litesdcard.git', 27 | 'liteiclink': 'https://github.com/enjoy-digital/liteiclink.git', 28 | 'litescope': 'https://github.com/enjoy-digital/litescope.git', 29 | 'litevideo': 'https://github.com/enjoy-digital/litevideo.git', 30 | 'pyserial': 'https://github.com/pyserial/pyserial.git', 31 | 'usb': 'https://github.com/pyusb/pyusb.git', 32 | } 33 | 34 | # Obtain the path to this script, plus a trailing separator. This will 35 | # be used later on to construct various environment variables for paths 36 | # to a variety of support directories. 37 | script_path = os.path.dirname(os.path.realpath(__file__)) + os.path.sep 38 | 39 | # Look through the specified file for known variables to get the dependency list 40 | def read_configuration(filename, args): 41 | import ast 42 | 43 | # Always check the Python version 44 | dependencies = { 45 | 'python': 1 46 | } 47 | configuration = { 48 | 'skip-git': False 49 | } 50 | main_src = "" 51 | 52 | try: 53 | with open(sys.argv[0], 'r') as f: 54 | main_src = f.read() 55 | main_ast = ast.parse(main_src, filename=filename) 56 | except: 57 | configuration['dependencies'] = list(dependencies.keys()) 58 | return configuration 59 | 60 | # Iterate through the top-level nodes looking for variables named 61 | # LX_DEPENDENCIES or LX_DEPENDENCY and get the values that are 62 | # assigned to them. 63 | for node in ast.iter_child_nodes(main_ast): 64 | if isinstance(node, ast.Assign): 65 | value = node.value 66 | for target in node.targets: 67 | if isinstance(target, ast.Name): 68 | if target.id == "LX_DEPENDENCIES" or target.id == "LX_DEPENDENCY": 69 | if isinstance(value, (ast.List, ast.Tuple)): 70 | for elt in value.elts: 71 | if isinstance(elt, ast.Str): 72 | dependencies[elt.s] = 1 73 | elif isinstance(value, ast.Str): 74 | dependencies[value.s] = 1 75 | elif target.id == "LX_CONFIGURATION" or target.id == "LX_CONFIG": 76 | if isinstance(value, (ast.List, ast.Tuple)): 77 | for elt in value.elts: 78 | if isinstance(elt, ast.Str): 79 | configuration[elt.s] = True 80 | elif isinstance(value, ast.Str): 81 | configuration[value.s] = True 82 | 83 | # Set up sub-dependencies 84 | if 'riscv' in dependencies: 85 | dependencies['make'] = 1 86 | if args.lx_check_git or (not configuration['skip-git'] and not args.lx_ignore_git): 87 | dependencies['git'] = 1 88 | configuration['dependencies'] = list(dependencies.keys()) 89 | return configuration 90 | 91 | def get_python_path(script_path, args): 92 | # Python has no concept of a local dependency path, such as the C `-I`` 93 | # switch, or the nodejs `node_modules` path, or the rust cargo registry. 94 | # Instead, it relies on an environment variable to append to the search 95 | # path. 96 | # Construct this variable by adding each subdirectory under the `deps/` 97 | # directory to the PYTHONPATH environment variable. 98 | python_path = [] 99 | if os.path.isdir(script_path + DEPS_DIR): 100 | for dep in os.listdir(script_path + DEPS_DIR): 101 | dep = script_path + DEPS_DIR + os.path.sep + dep 102 | if os.path.isdir(dep): 103 | python_path.append(dep) 104 | return python_path 105 | 106 | def fixup_env(script_path, args): 107 | os.environ["PYTHONPATH"] = os.pathsep.join(get_python_path(script_path, 0)) 108 | 109 | # Set the "LXBUILDENV_REEXEC" variable to prevent the script from continuously 110 | # reinvoking itself. 111 | os.environ["LXBUILDENV_REEXEC"] = "1" 112 | 113 | # Python randomizes the order in which it traverses hashes, and Migen uses 114 | # hashes an awful lot when bringing together modules. As such, the order 115 | # in which Migen generates its output Verilog will change with every run, 116 | # and the addresses for various modules will change. 117 | # Make builds deterministic so that the generated Verilog code won't change 118 | # across runs. 119 | os.environ["PYTHONHASHSEED"] = "1" 120 | 121 | # Some Makefiles are invoked as part of the build process, and those Makefiles 122 | # occasionally have calls to Python. Ensure those Makefiles use the same 123 | # interpreter that this script is using. 124 | os.environ["PYTHON"] = sys.executable 125 | 126 | # Set the environment variable "V" to 1. This causes Makefiles to print 127 | # the commands they run, which makes them easier to debug. 128 | if "lx_verbose" in args and args.lx_verbose: 129 | os.environ["V"] = "1" 130 | 131 | # If the user just wanted to print the environment variables, do that and quit. 132 | if args.lx_print_env: 133 | print("PYTHONPATH={}".format(os.environ["PYTHONPATH"])) 134 | print("PYTHONHASHSEED={}".format(os.environ["PYTHONHASHSEED"])) 135 | print("PYTHON={}".format(sys.executable)) 136 | print("LXBUILDENV_REEXEC={}".format(os.environ["LXBUILDENV_REEXEC"])) 137 | 138 | sys.exit(0) 139 | 140 | # Equivalent to the powershell Get-Command, and kinda like `which` 141 | def get_command(cmd): 142 | if os.name == 'nt': 143 | path_ext = os.environ["PATHEXT"].split(os.pathsep) 144 | else: 145 | path_ext = [""] 146 | for ext in path_ext: 147 | for path in os.environ["PATH"].split(os.pathsep): 148 | if os.path.exists(path + os.path.sep + cmd + ext): 149 | return path + os.path.sep + cmd + ext 150 | return None 151 | 152 | def check_python_version(args): 153 | import platform 154 | # Litex / Migen require Python 3.5 or newer. Ensure we're running 155 | # under a compatible version of Python. 156 | if sys.version_info[:3] < (3, 5): 157 | return (False, 158 | "python: You need Python 3.5+ (version {} found)".format(sys.version_info[:3])) 159 | return (True, "python 3.5+: ok (Python {} found)".format(platform.python_version())) 160 | 161 | def check_vivado(args): 162 | vivado_path = get_command("vivado") 163 | if vivado_path == None: 164 | # Look for the default Vivado install directory 165 | if os.name == 'nt': 166 | base_dir = r"C:\Xilinx\Vivado" 167 | else: 168 | base_dir = "/opt/Xilinx/Vivado" 169 | if os.path.exists(base_dir): 170 | for file in os.listdir(base_dir): 171 | bin_dir = base_dir + os.path.sep + file + os.path.sep + "bin" 172 | if os.path.exists(bin_dir + os.path.sep + "vivado"): 173 | os.environ["PATH"] += os.pathsep + bin_dir 174 | vivado_path = bin_dir 175 | break 176 | if vivado_path == None: 177 | return (False, "toolchain not found in your PATH", "download it from https://www.xilinx.com/support/download.html") 178 | return (True, "found at {}".format(vivado_path)) 179 | 180 | def check_cmd(args, cmd, name=None, fix=None): 181 | if name is None: 182 | name = cmd 183 | path = get_command(cmd) 184 | if path == None: 185 | return (False, name + " not found in your PATH", fix) 186 | return (True, "found at {}".format(path)) 187 | 188 | def check_make(args): 189 | return check_cmd(args, "make", "GNU Make") 190 | 191 | def check_riscv(args): 192 | riscv64 = check_cmd(args, "riscv64-unknown-elf-gcc", "riscv toolchain", "download it from https://www.sifive.com/boards/") 193 | if riscv64[0] == True: 194 | return riscv64 195 | 196 | riscv32 = check_cmd(args, "riscv32-unknown-elf-gcc", "riscv toolchain", "download it from https://www.sifive.com/boards/") 197 | if riscv32[0] == True: 198 | return riscv32 199 | 200 | # See https://xpack.github.io/riscv-none-embed-gcc/#riscv64-unknown-elf-gcc 201 | xpm_riscv = check_cmd(args, "riscv-none-embed-gcc", "xPack GNU RISC-V Embedded GCC", "install it from https://xpack.github.io/riscv-none-embed-gcc/install/") 202 | if xpm_riscv[0] == True: 203 | return xpm_riscv 204 | 205 | return riscv64 206 | 207 | def check_yosys(args): 208 | return check_cmd(args, "yosys") 209 | 210 | def check_arachne(args): 211 | return check_cmd(args, "arachne-pnr") 212 | 213 | def check_git(args): 214 | return check_cmd(args, "git") 215 | 216 | def check_icestorm(args): 217 | return check_cmd(args, "icepack") 218 | 219 | def check_nextpnr_ice40(args): 220 | return check_cmd(args, "nextpnr-ice40") 221 | 222 | def check_nextpnr_ecp5(args): 223 | return check_cmd(args, "nextpnr-ecp5") 224 | 225 | dependency_checkers = { 226 | 'python': check_python_version, 227 | 'vivado': check_vivado, 228 | 'make': check_make, 229 | 'git': check_git, 230 | 'riscv': check_riscv, 231 | 'yosys': check_yosys, 232 | 'arachne-pnr': check_arachne, 233 | 'icestorm': check_icestorm, 234 | 'nextpnr-ice40': check_nextpnr_ice40, 235 | 'nextpnr-ecp5': check_nextpnr_ecp5, 236 | } 237 | 238 | # Validate that the required dependencies (Vivado, compilers, etc.) 239 | # have been installed. 240 | def check_dependencies(args, dependency_list): 241 | dependency_errors = 0 242 | for dependency_name in dependency_list: 243 | if not dependency_name in dependency_checkers: 244 | print('lxbuildenv: WARNING: Unrecognized dependency "{}"'.format(dependency_name)) 245 | continue 246 | result = dependency_checkers[dependency_name](args) 247 | if result[0] == False: 248 | if len(result) > 2: 249 | print('lxbuildenv: {}: {} -- {}'.format(dependency_name, result[1], result[2])) 250 | else: 251 | print('lxbuildenv: {}: {}'.format(dependency_name, result[1])) 252 | dependency_errors = dependency_errors + 1 253 | 254 | elif args.lx_check_deps or args.lx_verbose: 255 | print('lxbuildenv: dependency: {}: {}'.format(dependency_name, result[1])) 256 | if dependency_errors > 0: 257 | if args.lx_ignore_deps: 258 | if not args.lx_quiet: 259 | print('lxbuildenv: {} missing dependencies were found but continuing anyway'.format(dependency_errors)) 260 | else: 261 | if not args.lx_quiet: 262 | print('lxbuildenv: To ignore dependencies, re-run with "--lx-ignore-deps"') 263 | raise SystemExit(str(dependency_errors) + 264 | " missing dependencies were found") 265 | 266 | if args.lx_check_deps: 267 | sys.exit(0) 268 | 269 | # Return True if the given tree needs to be initialized 270 | def check_module(root_path, depth, verbose=False, recursive=True, breadcrumbs=[]): 271 | if verbose: 272 | print('git-dep: checking if "{}" requires updating (depth: {})...'.format(root_path, depth)) 273 | 274 | # If the directory isn't a valid git repo, initialization is required 275 | if not os.path.exists(root_path): 276 | if verbose: 277 | print('git-dep: subdirectory {} does not exist, so starting update'.format(root_path)) 278 | return True 279 | git_dir_cmd = subprocess.Popen(["git", "rev-parse", "--show-toplevel"], 280 | cwd=root_path, 281 | stdout=subprocess.PIPE, 282 | stderr=subprocess.PIPE) 283 | (git_stdout, _) = git_dir_cmd.communicate() 284 | if git_dir_cmd.wait() != 0: 285 | if verbose: 286 | print('git-dep: missing git directory, starting update...') 287 | return True 288 | git_dir = git_stdout.decode().strip() 289 | 290 | if git_dir in breadcrumbs: 291 | if verbose: 292 | print('git-dep: root path {} is not in git path'.format(root_path)) 293 | return True 294 | breadcrumbs.append(git_dir) 295 | 296 | if not os.path.exists(git_dir + os.path.sep + '.git'): 297 | if verbose: 298 | print('git-dep: .git not found in "{}"'.format(git_dir)) 299 | return True 300 | 301 | # If there are no submodules, no initialization needs to be done 302 | if not os.path.isfile(git_dir + os.path.sep + '.gitmodules'): 303 | if verbose: 304 | print('git-dep: .gitmodules not found in "{}", so not updating'.format(git_dir)) 305 | return False 306 | 307 | # Loop through the gitmodules to check all submodules 308 | if recursive or depth == 0: 309 | gitmodules = open(git_dir + os.path.sep + '.gitmodules', 'r') 310 | for line in gitmodules: 311 | parts = line.split("=", 2) 312 | if parts[0].strip() == "path": 313 | path = parts[1].strip() 314 | if check_module(git_dir + os.path.sep + path, depth + 1, verbose=verbose, recursive=recursive, breadcrumbs=breadcrumbs): 315 | return True 316 | return False 317 | 318 | # Determine whether we need to invoke "git submodules init --recurse" 319 | def check_submodules(script_path, args): 320 | if check_module(script_path, 0, verbose=args.lx_verbose, recursive=args.lx_recursive_git): 321 | if not args.lx_quiet: 322 | print("lxbuildenv: Missing git submodules -- updating") 323 | print("lxbuildenv: To ignore git issues, re-run with --lx-ignore-git") 324 | 325 | git_cmd = ["git", "submodule", "update", "--init"] 326 | if args.lx_recursive_git: 327 | git_cmd.append("--recursive") 328 | subprocess.Popen(git_cmd, cwd=script_path).wait() 329 | elif args.lx_verbose: 330 | if not args.lx_quiet: 331 | print("lxbuildenv: Submodule check: Submodules found") 332 | 333 | 334 | def lx_git(cmd, *args): 335 | import subprocess 336 | git_cmd = ["git", cmd] 337 | if args is not None: 338 | for arg in args: 339 | git_cmd = git_cmd + [arg] 340 | subprocess.call(git_cmd) 341 | 342 | def lx_print_deps(): 343 | print('lxbuildenv: Supported dependencies:') 344 | for dep in dependency_checkers.keys(): 345 | print('lxbuildenv: {}'.format(dep)) 346 | print('lxbuildenv: To define a dependency, add a variable inside {} at the top level called LX_DEPENDENCIES and assign it a list or tuple.'.format(sys.argv[0])) 347 | print('lxbuildenv: For example:') 348 | print('lxbuildenv: LX_DEPENDENCIES = ("riscv", "vivado")') 349 | 350 | 351 | def lx_main(args): 352 | if args.lx_print_env: 353 | fixup_env(script_path, args) 354 | 355 | elif args.lx_print_deps: 356 | lx_print_deps() 357 | 358 | elif args.lx_run is not None: 359 | script_name=args.lx_run[0] 360 | config = read_configuration(script_name, args) 361 | 362 | fixup_env(script_path, args) 363 | if not config['skip-git']: 364 | check_submodules(script_path, args) 365 | 366 | try: 367 | sys.exit(subprocess.Popen( 368 | [sys.executable] + [script_name] + args.lx_run[1:]).wait()) 369 | except: 370 | sys.exit(1) 371 | elif args.init: 372 | if args.main is None: 373 | main_name = os.getcwd().split(os.path.sep)[-1] + '.py' 374 | new_main_name = input('lxbuildenv: What would you like your main program to be called? [' + main_name + '] ') 375 | if new_main_name is not None and new_main_name != "": 376 | main_name = new_main_name 377 | else: 378 | main_name = args.main 379 | if not main_name.endswith('.py'): 380 | main_name = main_name + '.py' 381 | 382 | if args.no_git: 383 | print("lxbuildenv: skipping git initialization") 384 | else: 385 | if not os.path.exists(DEPS_DIR): 386 | os.mkdir(DEPS_DIR) 387 | 388 | if not os.path.exists(".git"): 389 | print("lxbuildenv: initializing git repository") 390 | lx_git('init') 391 | else: 392 | print("lxbuildenv: using existing git repository") 393 | lx_git('add', str(__file__)) 394 | 395 | for dep_name, dep_url in DEFAULT_DEPS.items(): 396 | dest_path = '{}{}{}'.format(DEPS_DIR, '/', dep_name) 397 | if not os.path.exists(dest_path): 398 | lx_git('submodule', 'add', dep_url, dest_path) 399 | lx_git('add', dest_path) 400 | 401 | if args.lx_recursive_git: 402 | lx_git('submodule', 'update', '--init', '--recursive') 403 | else: 404 | lx_git('submodule', 'update', '--init') 405 | 406 | if args.no_bin: 407 | print("lxbuildenv: skipping bin/ initialization") 408 | elif os.path.exists("bin"): 409 | print("lxbuildenv: bin/ directory exists -- remove bin/ directory to re-initialize") 410 | else: 411 | bin_tools = { 412 | 'mkmscimg': 'litex.soc.software.mkmscimg', 413 | 'litex_term': 'litex.tools.litex_term', 414 | 'litex_server': 'litex.tools.litex_server', 415 | 'litex_sim': 'litex.tools.litex_sim', 416 | 'litex_read_verilog': 'litex.tools.litex_read_verilog', 417 | 'litex_simple': 'litex.boards.targets.simple', 418 | } 419 | bin_template = """#!/usr/bin/env python3 420 | 421 | import sys 422 | import os 423 | 424 | # This script lives in the "bin" directory, but uses a helper script in the parent 425 | # directory. Obtain the current path so we can get the absolute parent path. 426 | script_path = os.path.dirname(os.path.realpath( 427 | __file__)) + os.path.sep + os.path.pardir + os.path.sep 428 | sys.path.insert(0, script_path) 429 | import lxbuildenv 430 | 431 | """ 432 | print("lxbuildenv: Creating binaries") 433 | os.mkdir("bin") 434 | for bin_name, python_module in bin_tools.items(): 435 | with open('bin' + os.path.sep + bin_name, 'w', newline='\n') as new_bin: 436 | new_bin.write(bin_template) 437 | new_bin.write('from ' + python_module + ' import main\n') 438 | new_bin.write('main()\n') 439 | import stat 440 | os.chmod('bin' + os.path.sep + bin_name, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) 441 | if not args.no_git: 442 | lx_git('add', '--chmod=+x', 'bin' + os.path.sep + bin_name) 443 | 444 | if os.path.exists(main_name): 445 | print("lxbuildenv: skipping creation of {}: file exists".format(main_name)) 446 | else: 447 | print("lxbuildenv: creating main program {}".format(main_name)) 448 | with open(main_name, 'w') as m: 449 | program_template = """#!/usr/bin/env python3 450 | # This variable defines all the external programs that this module 451 | # relies on. lxbuildenv reads this variable in order to ensure 452 | # the build will finish without exiting due to missing third-party 453 | # programs. 454 | LX_DEPENDENCIES = ["riscv", "vivado"] 455 | 456 | # Import lxbuildenv to integrate the deps/ directory 457 | import lxbuildenv 458 | 459 | # Disable pylint's E1101, which breaks completely on migen 460 | #pylint:disable=E1101 461 | 462 | from migen import * 463 | from litex.build.xilinx import VivadoProgrammer, XilinxPlatform 464 | from litex.build.generic_platform import Pins, IOStandard 465 | from litex.soc.integration import SoCSDRAM 466 | from litex.soc.integration.builder import Builder 467 | from litex.soc.integration.soc_core import csr_map_update 468 | 469 | _io = [ 470 | ("clk50", 0, Pins("J19"), IOStandard("LVCMOS33")), 471 | ] 472 | 473 | class Platform(XilinxPlatform): 474 | def __init__(self, toolchain="vivado", programmer="vivado", part="35"): 475 | part = "xc7a" + part + "t-fgg484-2" 476 | def create_programmer(self): 477 | if self.programmer == "vivado": 478 | return VivadoProgrammer(flash_part="n25q128-3.3v-spi-x1_x2_x4") 479 | else: 480 | raise ValueError("{} programmer is not supported" 481 | .format(self.programmer)) 482 | 483 | def do_finalize(self, fragment): 484 | XilinxPlatform.do_finalize(self, fragment) 485 | 486 | class BaseSoC(SoCSDRAM): 487 | csr_peripherals = [ 488 | "ddrphy", 489 | # "dna", 490 | "xadc", 491 | "cpu_or_bridge", 492 | ] 493 | csr_map_update(SoCSDRAM.csr_map, csr_peripherals) 494 | 495 | def __init__(self, platform, **kwargs): 496 | clk_freq = int(100e6) 497 | 498 | def main(): 499 | platform = Platform() 500 | soc = BaseSoC(platform) 501 | builder = Builder(soc, output_dir="build", csr_csv="test/csr.csv") 502 | vns = builder.build() 503 | soc.do_exit(vns) 504 | 505 | if __name__ == "__main__": 506 | main() 507 | """ 508 | m.write(program_template) 509 | if not args.no_git: 510 | lx_git("add", main_name) 511 | else: 512 | return False 513 | return True 514 | 515 | # For the main command, parse args and hand it off to main() 516 | def main(): 517 | parser = argparse.ArgumentParser( 518 | description="Wrap Python code to enable quickstart", 519 | add_help=False) 520 | parser.add_argument( 521 | "-h", "--help", '--lx-help', help="show this help message and exit", action="help" 522 | ) 523 | parser.add_argument( 524 | '-i', '--init', '--lx-init', help='initialize a new project', action="store_true" 525 | ) 526 | parser.add_argument( 527 | '-m', '--main', '--lx-main', help='name of main project' 528 | ) 529 | parser.add_argument( 530 | '--no-bin', '--lx-no-bin', help="don't create a bin/ directory" 531 | ) 532 | parser.add_argument( 533 | '--no-git', '--lx-no-git', help="Don't create a git repository" 534 | ) 535 | parser.add_argument( 536 | '-e', '--print-env', '--lx-print-env', dest="lx_print_env", help="print environment variable listing for pycharm, vscode, or bash", action="store_true" 537 | ) 538 | parser.add_argument( 539 | '-d', '--print-deps', '--lx-print-deps', dest="lx_print_deps", help="print all possible dependencies and then exit", action="store_true" 540 | ) 541 | parser.add_argument( 542 | "--lx-verbose", help="increase verboseness of some processes", action="store_true" 543 | ) 544 | parser.add_argument( 545 | '-r', '--run', '--lx-run', dest='lx_run', help="run the given script under lxbuildenv", nargs=argparse.REMAINDER 546 | ) 547 | parser.add_argument( 548 | "--lx-recursive-git", help="recursively check out submodules", action="store_true" 549 | ) 550 | args = parser.parse_args() 551 | 552 | if not lx_main(args): 553 | parser.print_help() 554 | 555 | if __name__ == "__main__": 556 | main() 557 | 558 | elif not os.path.isfile(sys.argv[0]): 559 | print("lxbuildenv doesn't operate while in interactive mode") 560 | 561 | elif "LXBUILDENV_REEXEC" not in os.environ: 562 | parser = argparse.ArgumentParser( 563 | description="Wrap Python code to enable quickstart", 564 | add_help=False) 565 | parser.add_argument( 566 | "--lx-verbose", help="increase verboseness of some processes", action="store_true" 567 | ) 568 | parser.add_argument( 569 | "--lx-quiet", help="decrease verboseness of some processes", action="store_true" 570 | ) 571 | parser.add_argument( 572 | "--lx-print-env", help="print environment variable listing for pycharm, vscode, or bash", action="store_true" 573 | ) 574 | parser.add_argument( 575 | "--lx-check-deps", help="check build environment for dependencies such as compiler and fpga tools and then exit", action="store_true" 576 | ) 577 | parser.add_argument( 578 | "--lx-print-deps", help="print all possible dependencies and then exit", action="store_true" 579 | ) 580 | parser.add_argument( 581 | "--lx-help", action="help" 582 | ) 583 | parser.add_argument( 584 | "--lx-ignore-deps", help="try building even if dependencies are missing", action="store_true" 585 | ) 586 | parser.add_argument( 587 | "--lx-ignore-git", help="don't do a check of the git repo", action="store_true" 588 | ) 589 | parser.add_argument( 590 | "--lx-check-git", help="force a git check even if it's otherwise disabled", action="store_true" 591 | ) 592 | parser.add_argument( 593 | "--lx-recursive-git", help="recursively check out submodules", action="store_true" 594 | ) 595 | (args, rest) = parser.parse_known_args() 596 | 597 | if not args.lx_quiet: 598 | print("lxbuildenv: v{} (run {} --lx-help for help)".format(LXBUILDENV_VERSION, sys.argv[0])) 599 | 600 | if args.lx_print_deps: 601 | lx_print_deps() 602 | sys.exit(0) 603 | 604 | config = read_configuration(sys.argv[0], args) 605 | deps = config['dependencies'] 606 | 607 | fixup_env(script_path, args) 608 | check_dependencies(args, deps) 609 | if args.lx_check_git: 610 | check_submodules(script_path, args) 611 | elif config['skip-git']: 612 | if not args.lx_quiet: 613 | print('lxbuildenv: Skipping git configuration because "skip-git" was found in LX_CONFIGURATION') 614 | print('lxbuildenv: To fetch from git, run {} --lx-check-git'.format(" ".join(sys.argv))) 615 | elif args.lx_ignore_git: 616 | if not args.lx_quiet: 617 | print('lxbuildenv: Skipping git configuration because "--lx-ignore-git" Was specified') 618 | else: 619 | check_submodules(script_path, args) 620 | 621 | try: 622 | sys.exit(subprocess.Popen( 623 | [sys.executable] + [sys.argv[0]] + rest).wait()) 624 | except Exception as e: 625 | print(e) 626 | sys.exit(1) 627 | else: 628 | # Overwrite the deps directory. 629 | # Because we're running with a predefined PYTHONPATH, you'd think that 630 | # the DEPS_DIR would be first. 631 | # Unfortunately, setuptools causes the sitewide packages to take precedence 632 | # over the PYTHONPATH variable. 633 | # Work around this bug by inserting paths into the first index. 634 | for path in get_python_path(script_path, None): 635 | sys.path.insert(0, path) 636 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | from setuptools import setup 5 | from setuptools import find_packages 6 | 7 | 8 | if sys.version_info[:3] < (3, 5): 9 | raise SystemExit("You need Python 3.5+") 10 | 11 | print() 12 | 13 | setup( 14 | name="lxbuildenv", 15 | description="Simplified build environment for LiteX", 16 | long_description=open("README.asciidoc").read(), 17 | author="Sean Cross", 18 | author_email="sean@xobs.io", 19 | url="http://xobs.io", 20 | download_url="https://github.com/xobs/lxbuildenv", 21 | license="BSD", 22 | platforms=["Any"], 23 | keywords="HDL ASIC FPGA hardware design", 24 | classifiers=[ 25 | "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", 26 | "Environment :: Console", 27 | "Development Status :: Alpha", 28 | "Intended Audience :: Developers", 29 | "License :: OSI Approved :: BSD License", 30 | "Operating System :: OS Independent", 31 | "Programming Language :: Python", 32 | ], 33 | py_modules=["lxbuildenv"], 34 | install_requires=[], 35 | entry_points={ 36 | "console_scripts": [ 37 | "lxbuildenv=lxbuildenv:main", 38 | ], 39 | }, 40 | ) 41 | --------------------------------------------------------------------------------