diff --git a/.btd.yml b/.btd.yml deleted file mode 100644 index a9be9d62..00000000 --- a/.btd.yml +++ /dev/null @@ -1,5 +0,0 @@ -input: doc -output: _build -target: gh-pages -formats: [ html ] -theme: https://codeload.github.com/buildthedocs/sphinx.theme/tar.gz/v0 diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml deleted file mode 100644 index ee0ecc54..00000000 --- a/.github/workflows/doc.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: 'doc' - -on: - push: - branches: - - main - -jobs: - linux: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: buildthedocs/btd@v0 - with: - token: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/upload-artifact@master - with: - name: doc - path: doc/_build/html diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..bf55bcfb --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,26 @@ +name: 'docs' + +on: + push: + paths: + - 'docs/**' + - 'pyfpga/project.py' + branches: + - main + - dev + +jobs: + docs: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Install dependencies + run: pip install . && pip install sphinx sphinx-rtd-theme + - name: Build documentation + run: make docs + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: docs/build/html diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0564dc22..3907a5c4 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,13 +7,9 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - name: Checkout repository + uses: actions/checkout@v4 - name: Install dependencies - run: | - pip install pycodestyle - pip install pylint - pip install . - - name: Lint - run: | - pycodestyle fpga examples test - pylint fpga + run: pip install pycodestyle pylint + - name: Run linters + run: make lint diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 96b267ed..b04f05be 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,28 +5,29 @@ on: jobs: test: - runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8, 3.9] + os: ['ubuntu', 'windows'] + pyver: ['3.8', '3.9', '3.10', '3.11', '3.12'] + exclude: + - os: 'windows' + pyver: '3.8' + - os: 'windows' + pyver: '3.9' + - os: 'windows' + pyver: '3.10' + - os: 'windows' + pyver: '3.11' + runs-on: ${{ matrix.os }}-latest + name: ${{ matrix.os }} | ${{ matrix.pyver }} steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + - name: Checkout repository + uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.pyver }} + uses: actions/setup-python@v5 with: - python-version: ${{ matrix.python-version }} + python-version: ${{ matrix.pyver }} - name: Install dependencies - run: | - pip install pytest - pip install . - - name: Pull container images - run: | - docker pull hdlc/prjtrellis - docker pull hdlc/ghdl:yosys - docker pull hdlc/icestorm - docker pull hdlc/nextpnr:ecp5 - docker pull hdlc/nextpnr:ice40 - - name: Test - run: | - pytest - make -C examples + run: pip install . && pip install pytest + - name: Run tests + run: make test diff --git a/.gitignore b/.gitignore index 2e7693ab..964a59fd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ *build ignore* +results venv __pycache__ -*.egg-info -_theme diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index 40d501d1..00000000 --- a/.pylintrc +++ /dev/null @@ -1,16 +0,0 @@ -[MASTER] -ignore=ignore -jobs=0 -suggestion-mode=yes - -[REPORTS] -score=no - -[MESSAGES CONTROL] -disable=duplicate-code, - import-outside-toplevel, - raise-missing-from, - too-many-arguments, - too-many-branches, - too-many-instance-attributes, - too-many-locals diff --git a/Makefile b/Makefile index 278baebc..7893bb51 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,24 @@ #!/usr/bin/make -check: - pycodestyle fpga examples test - pylint -s n fpga +.PHONY: docs + +all: docs lint test + +docs: + cd docs; make html + +lint: + pycodestyle pyfpga examples tests + pylint -s n pyfpga git diff --check --cached - pytest test + +test: + pytest + cd examples/projects && bash regress.sh --notool clean: py3clean . - rm -fr build .pytest_cache + rm -fr docs/build + rm -fr .pytest_cache + rm -fr `find . -name results` + rm -fr `find . -name __pycache__` diff --git a/README.md b/README.md index 8222587b..28441fcf 100644 --- a/README.md +++ b/README.md @@ -1,132 +1,77 @@ # PyFPGA [![License](https://img.shields.io/badge/License-GPL--3.0-darkgreen?style=flat-square)](LICENSE) -![GDHL](https://img.shields.io/badge/GHDL-last-brightgreen.svg?style=flat-square) -![icestorm](https://img.shields.io/badge/icestorm-last-brightgreen.svg?style=flat-square) -![nextpnr](https://img.shields.io/badge/nextpnr-last-brightgreen.svg?style=flat-square) -![prjtrellis](https://img.shields.io/badge/prjtrellis-last-brightgreen.svg?style=flat-square) -![Yosys](https://img.shields.io/badge/Yosys-last-brightgreen.svg?style=flat-square) - +![Diamond](https://img.shields.io/badge/Diamond-3.13-blue.svg?style=flat-square) ![ISE](https://img.shields.io/badge/ISE-14.7-blue.svg?style=flat-square) -![Libero](https://img.shields.io/badge/Libero--Soc-12.2-blue.svg?style=flat-square) -![Quartus](https://img.shields.io/badge/Quartus--Prime-19.1-blue.svg?style=flat-square) -![Vivado](https://img.shields.io/badge/Vivado-2019.2-blue.svg?style=flat-square) - -PyFPGA is a **Python** Class for **vendor-independent FPGA development**. -It allows using **a single project file** and **programmatically** executing -**synthesis**, **implementation**, generation of **bitstream** and/or -**transference** to supported boards. +![Libero](https://img.shields.io/badge/Libero--Soc-2024.1-blue.svg?style=flat-square) +![Quartus](https://img.shields.io/badge/Quartus--Prime-23.1-blue.svg?style=flat-square) +![Vivado](https://img.shields.io/badge/Vivado-2022.1-blue.svg?style=flat-square) -- The workflow is command-line centric. -- It's friendly with *Version Control Systems* and *Continuous Integration* (CI). -- Allows reproducibility and repeatability. -- Consumes fewer system resources than GUI based workflows. +![Openflow](https://img.shields.io/badge/Openflow-GHDL%20%7C%20Yosys%20%7C%20nextpnr%20%7C%20icestorm%20%7C%20prjtrellis-darkgreen.svg?style=flat-square) -Create your custom FPGA Tool using a workflow tailored to your needs! +PyFPGA is an abstraction layer for working with FPGA development tools in a vendor-agnostic, programmatic way. It is a Python package that provides: +* One **class** per supported tool for **project creation**, **synthesis**, **place and route**, **bitstream generation**, and **programming**. +* A set of **command-line helpers** for simple projects or quick evaluations. -> **WARNING:** (2022-05-15) PyFPGA is in the process of being strongly rewritten/simplified. -> Most changes are internal, but the API (`Project` class) will change. +With PyFPGA, you can create your own FPGA development workflow tailored to your needs! -## Usage +Some of its benefits are: +* It provides a unified API between tools/devices. +* It's **Version Control Systems** and **Continuous Integration** friendly. +* It ensures reproducibility and repeatability. +* It consumes fewer system resources than GUI-based workflows. -A minimal example of how to use PyFPGA: +## Basic example ```py -from fpga import Project +from pyfpga import Vivado -# Specify the backend tool and an optional project name -prj = Project('vivado', 'example') - -# Set the device/part +prj = Vivado('example') prj.set_part('xc7z010-1-clg400') - -# Add HDL sources to the project -prj.add_files('location1/*.v') -prj.add_files('location2/top.v') - -# Optionally add constraint files to the project -prj.add_files('location3/example.xdc') - -# Set the top-level unit name +prj.add_vlog('location1/*.v') +prj.add_vlog('location2/top.v') +prj.add_cons('location3/example.xdc') prj.set_top('Top') - -# Generate the bitstream running the tool -prj.generate() +prj.make() ``` -Now, you can read the [docs](https://pyfpga.github.io/pyfpga/) or find -more examples in subdir [examples](examples). +The next steps are to read the [docs](https://pyfpga.github.io/pyfpga) or take a look at [examples](examples). -The API implemented by the `Project class` provides: +## Support -- A constructor where the TOOL must be specified and an optional PROJECT NAME can be indicated -- Methods to set the target device PART, to add multiple HDL, Constraint and Tcl files to the - project (in case of VHDL an optional PACKAGE NAME can be specified) and to specify the TOP-LEVEL -- Methods to specify a different OUTPUT directory or get some project configurations -- Methods to generate a bitstream and transfer it to a device (running the selected EDA Tool) -- The capability of specifying an optimization strategy (area, power or speed) when the bitstream - is generated -- A method to add Verilog Included File directories -- A method to specify generics/parameters values -- Methods to add Tcl commands in up to six different parts of the Flow (workaround for not yet - implemented features) -- Optional logging capabilities which include the display of the Tool Execution Time +PyFPGA is a Python package developed having GNU/Linux platform on mind, but it should run well on any POSIX-compatible OS, and probably others! +If you encounter compatibility issues, please inform us via the [issues](https://github.com/PyFPGA/pyfpga/issues) tracker. -## Support +For a comprehensive list of supported tools, features and limitations, please refer to the [tools support](https://pyfpga.github.io/pyfpga/tools.html) page. -PyFPGA is a Python 3 package, which is developed on Debian GNU/Linux. -It should run on any other POSIX compatible OS and probably also on different OS. -Should you achieve either success of failure on non-POSIX systems, please let us know through the -[issue](https://github.com/PyFPGA/pyfpga/issues) tracker. - -- The whole development flow (from reading HDL and constraint sources to producing a bitstream) - can be performed with ISE (Xilinx), Vivado (Xilinx), Quarts Prime (Intel/Altera), Libero-SoC - (Microsemi) and/or with open-source tools. -- GDHL (`--synth`) allows converting VHDL sources into a VHDL 1993 netlist. -- Yosys allows synthesising Verilog and VHDL (using `ghdl-yosys-plugin`) and supports multiple - output formats: JSON, Verilog, EDIF, etc. - - nextpnr can be used for implementation of JSON netlists. - - Also, ISE and Vivado are supported for implementation of Verilog netlists. -- Transferring bitstreams and programming devices: - - ISE (Impact) can be used for programming FPGAs and/or memories (BPI and SPI) through JTAG. - - Vivado, Quartus and iceprog (IceStorm, for ice40 devices) can be used to programming FPGAs. - - Programming with Libero-SoC and programming ECP5 devices (prjtrellis, openocd) is not yet - supported. - -**Notes:** - -- The open-source tools are supported trough container images from the -[ghdl/docker](https://github.com/ghdl/docker) project, so -[Docker](https://www.docker.com/) ~~or [Podman](https://podman.io/)~~ must be -installed. The same workflow can be used in CI services. -- ISE, Libero-Soc, Quartus Prime and Vivado, must be ready to be executed from -the terminal (installed and well configured). +> **NOTE:** +> PyFPGA assumes that the underlying tools required for operation are ready to be executed from the running terminal. +> This includes having the tools installed, properly configured and licensed (when needed). ## Installation -PyFPGA requires Python `>=3.6`. For now, it's only available as a git repository -hosted on GitHub. It can be installed with pip: +PyFPGA requires Python>=3.8. + +At the moment, it's only available as a git repository hosted on GitHub. It can be installed with pip: ``` pip install 'git+https://github.com/PyFPGA/pyfpga#egg=pyfpga' ``` -> On GNU/Linux, installing pip packages on the system requires `sudo`. -> Alternatively, use `--local` for installing PyFPGA in your HOME. - -You can get a copy of the repository either through git clone or downloading a -tarball/zipfile: +Alternatively, you can get a copy of the repository either through git clone or downloading a tarball/zipfile, and then: ``` git clone https://github.com/PyFPGA/pyfpga.git cd pyfpga +pip install -e . ``` -Then, use pip from the root of the repo: +> With `-e` (`--editable`) your application is installed into site-packages via a kind of symlink. +> That allows pulling changes through git or changing the branch, avoiding the need to reinstall the package. -``` -pip install -e . -``` +## Similar projects -> With `-e` (`--editable`) your application is installed into site-packages via -> a kind of symlink. That allows pulling changes through git or changing the -> branch, without the need to reinstall the package. +* [edalize](https://github.com/olofk/edalize): an abstraction library for interfacing EDA tools. +* [Hdlmake](https://ohwr.org/project/hdl-make): tool for generating multi-purpose makefiles for FPGA projects. +* HDL On Git ([Hog](https://gitlab.com/hog-cern/Hog)): a set of Tcl/Shell scripts plus a suitable methodology to handle HDL designs in a GitLab repository. +* IPbus Builder ([IPBB](https://github.com/ipbus/ipbb)): a tool for streamlining the synthesis, implementation and simulation of modular firmware projects over multiple platforms. +* [tsfpga](https://github.com/tsfpga/tsfpga): a flexible and scalable development platform for modern FPGA projects. diff --git a/doc/Makefile b/doc/Makefile deleted file mode 100644 index 247b174d..00000000 --- a/doc/Makefile +++ /dev/null @@ -1,57 +0,0 @@ -CP=cp - -# Sphinx options. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees -T -D language=en $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -all: pyfpga.info - -#--- - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - -#--- - -html: - PYTHONPATH=$(shell pwd)/.. $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - -#--- - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - -#--- - -texi: pyfpga.texi -pyfpga.texi: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - $(CP) $(BUILDDIR)/texinfo/PyFPGA.texi $@ - -info: pyfpga.info -pyfpga.info: pyfpga.texi - makeinfo -o $@ $< - -dvi: pyfpga.dvi -pyfpga.dvi: pyfpga.texi - texi2dvi $< - -pyfpga.ps: pyfpga.dvi - dvips $< - -pdf: pyfpga.pdf -pyfpga.pdf: pyfpga.dvi - dvipdf $< - -#--- - -clean: - $(RM) *~ *.dvi *.info *.aux *.cp *.fn *.ky *.log - $(RM) *.pdf *.pg *.toc *.tp *.vr *.texi - $(RM) -rf _build diff --git a/doc/advanced.rst b/doc/advanced.rst deleted file mode 100644 index 0cfea3af..00000000 --- a/doc/advanced.rst +++ /dev/null @@ -1,161 +0,0 @@ -.. program:: pyfpga - -.. _advanced: - -Advanced usage -############## - -Multi project managment -======================= - -.. code-block:: python - - PROJECTS = { - '': Project( - '', - '', - { - 'outdir': '', - 'part': '' - 'paths': [ - '', - ... - '' - ], - 'vhdl': [ - ['', ''], - '', - ... - '' - ], - 'verilog': [ - '', - ... - '' - ], - 'constraint': [ - '', - ... - '' - ], - 'params': { - '': '', - ... - '': '' - }, - 'top': '' - } - ) - '': Project( - ... - ) - } - -.. _hooks: - -Hooks -===== - -The following table depicts the parts of the *Project Creation* and the -*Design Flow* internally performed by PyFPGA. - -+--------------------------+----------------------+ -| Project Creation | Design Flow | -+==========================+======================+ -| Part specification | **preflow** hook | -+--------------------------+----------------------+ -| **prefile** hook | Synthesis | -+--------------------------+----------------------+ -| Files addition | **postsyn** hook | -+--------------------------+----------------------+ -| Top specification | Implementation | -+--------------------------+----------------------+ -| Parameters specification | **postimp** hook | -+--------------------------+----------------------+ -| **project** hook | Bitstream generation | -+--------------------------+----------------------+ -| | **postbit** hook | -+--------------------------+----------------------+ - -If the provided API if not enough or suitable for your project, you can -specify additional *hooks* in different parts of the flow, using: - -.. code-block:: python - - prj.add_hook(hook, phase) - -.. NOTE:: - - * Valid vaues for *phase* are ``prefile``, ``project`` (default), ``preflow``, - ``postsyn``, ``postimp`` and ``postbit``. - * The *hook* string must be a valid command (supported by the used tool). - * If more than one *hook* is needed in the same *phase*, you can call this - method several times (the commands will be executed in order). - -Parameters -========== - -The generics/parameters of the project can be optionally changed with: - -.. code-block:: python - - prj.set_param('param1', value1) - ... - prj.set_param('paramN', valueN) - -Generate options -================ - -The method ``generate`` (previously seen at the end of -[Basic usage](#basic-usage) section) has optional parameters: - -.. code-block:: python - - prj.generate(to_task, from_task, capture) - -With *to_task* and *from_taks* (with default values ``bit`` and ``prj``), -you are selecting the first and last task to execute when `generate` is -invoqued. The order and available tasks are ``prj``, ``syn``, ``imp`` and ``bit``. -It can be useful in at least two cases: - -* Maybe you created a file project with the GUI of the Tool and only want to - run the Design Flow, so you can use: ``generate(to_task='bit', from_task='syn')`` - -* Despite that a method to insert particular commands is provided, you would - want to perform some processing from Python between tasks, using something - like: - -.. code-block:: python - - prj.generate(to_task='syn', from_task='prj') - #Some other Python commands here - prj.generate(to_task='bit', from_task='syn') - -In case of *capture*, it is useful to catch execution messages to be -post-processed or saved to a file: - -.. code-block:: python - - result = prj.generate(capture=True) - print(result) - -In case of *capture*, it is useful to catch execution messages to be -post-processed or saved to a file. - -Exceptions -========== - -Finally, you must run the bitstream generation or its transfer. Both of them -are time-consuming tasks, performed by a backend tool, which could fail. -Exceptions are raised in such cases, that should be ideally caught to avoid -abnormal program termination. - -.. code-block:: python - - try: - prj.generate() - prj.transfer() - except Exception as e: - print('{} ({})'.format(type(e).__name__, e)) - -And wait for the backend Tool to accomplish its task. diff --git a/doc/api.rst b/doc/api.rst deleted file mode 100644 index 1be17600..00000000 --- a/doc/api.rst +++ /dev/null @@ -1,8 +0,0 @@ -.. program:: pyfpga - -.. _api: - -API Reference -############# - -.. automodule:: fpga.project diff --git a/doc/basic.rst b/doc/basic.rst deleted file mode 100644 index a0d081ba..00000000 --- a/doc/basic.rst +++ /dev/null @@ -1,153 +0,0 @@ -.. program:: pyfpga - -.. _basic: - -Basic usage -########### - -Project Creation -================ - -The first steps are import the module and instantiate the ``Project`` *class*, -specifying the *TOOL* to use and, optionally, a *PROJECT NAME* (the *tool* -name is used when *no name* is provided). - -.. code-block:: python - - from fpga.project import Project - - prj = Project('vivado', 'projectName') - -.. NOTE:: - - The supported tool are: ``ghdl``, ``ise``, ``libero``, ``openflow``, - ``quartus``, ``vivado``, ``yosys``, ``yosys-ise`` and ``yosys-vivado``. - -By default, the directory where the project is generated is called ``build`` -and is located in the same place that the script, but another name and location -can be specified. - -.. code-block:: python - - prj.set_outdir('../temp') - -Next, the FPGA part would be specified: - -.. code-block:: python - - prj.set_part('xc7k160t-3-fbg484') - -.. NOTE:: - - You can use the default FPGA part for a quick test or make a lazy comparison - between tools, but generally, you will want to specify a particular one. - Examples about how to specify a part according the tool, are (default values - when ``set_part`` is not employed): - - * **Ise:** ``xc7k160t-3-fbg484`` (*device-speed-package*) - * **Libero:** ``mpf100t-1-fcg484`` (*device-speed-package*) - * **Openflow:** ``hx8k-ct256`` (*device-package*) - * **Quartus:** ``10cl120zf780i8g`` (*part*) - * **Vivado:** ``xc7k160t-3-fbg484`` (*part*) - -The files addition method allows specifying one or more HDL or constraint files -(also block designs in case of Vivado). -It uses ``glob`` internally, which makes available the use of wildcards. -The path to their location must be relative to the Python script, and there -are optional parameters to indicate the file type (``vhdl``, ``verilog``, -``constraint`` or ``design``), which is automatically detected based on the -file extension, and if it is a member of a VHDL package. - -.. code-block:: python - - prj.add_files('hdl/blinking.vhdl', library='examples') - prj.add_files('hdl/examples_pkg.vhdl', library='examples') - prj.add_files('hdl/top.vhdl') - -.. NOTE:: - - * In some cases, the files order could be a problem, so take into account to - change the order if needed. - * If a file seems unsupported, you can always use the ``prefile`` or - ``project`` :ref:`hooks`. - * In case of Verilog, ``add_path`` can be used to specify where to search for - included files. - -Finally, the top-level must be specified: - -.. code-block:: python - - prj.set_top('Top') - -.. NOTE:: - - A relative path to a valid VHDL/Verilog file is also accepted by ``set_top``, - to automatically extract the top-level name. - -Project generation -================== - -Next step if to generate the project. In the most basic form, you can run the -following to get a bitstream: - -.. code-block:: python - - prj.generate() - -Additionally, you can specify which task to perform: - -.. code-block:: python - - prj.generate('syn') - -.. NOTE:: - - The valid values are: - - * ``prj``: to generate only a project file (only supported for privative tools) - * ``syn``: to performs synthesis. - * ``imp``: to performs synthesis and implementation (place and route, - optimizations and static timming analysis when available). - * ``bit``: (default) to perform synthesis, implementation and bitstream generation. - -Bitstream transfer -================== - -This method is in charge of run the needed tool to transfer a bitstream to a -device (commonly an FPGA, but memories are also supported in some cases). -It has up to four main optional parameters: - -.. code-block:: python - - prj.transfer(devtype, position, part, width) - -Where *devtype* is ``fpga`` by default but can also be ``spi``, ``bpi``, etc, if -supported. An integer number can be used to specify the *position* (1) in the -Jtag chain. When a memory is used as *devtype*, the *part* name and the -*width* in bits must be also specified. - -.. NOTE:: - - * In Xilinx, `spi` and `bpi` memories are out of the Jtag chain and are - programmed through the FPGA. You must specify the FPGA *position*. - * In a Linux systems, you need to have permission over the device - (udev rule, be a part of a group, etc). - -Logging capabilities -==================== - -PyFPGA uses the `logging `_ -module, with a *NULL* handler and the *INFO* level by default. -Messages can be enabled with: - -.. code-block:: python - - import logging - - logging.basicConfig() - -You can enable *DEBUG* messages adding: - -.. code-block:: python - - logging.getLogger('fpga.project').level = logging.DEBUG diff --git a/doc/conf.py b/doc/conf.py deleted file mode 100644 index ffead9cc..00000000 --- a/doc/conf.py +++ /dev/null @@ -1,129 +0,0 @@ -# -*- coding: utf-8 -*- - -import sys, re -from pathlib import Path -#from json import dump, loads - -sys.path.insert(0, str(Path('.').resolve())) - -# -- General configuration ------------------------------------------------ - -needs_sphinx = '3.0' - -extensions = [ - #'recommonmark', - 'sphinx.ext.autodoc', - 'sphinx.ext.extlinks', - 'sphinx.ext.intersphinx', - 'sphinx.ext.todo', - 'sphinx.ext.graphviz', - 'sphinx.ext.mathjax', - 'sphinx.ext.ifconfig', - 'sphinx.ext.viewcode', -] - -autodoc_default_options = { - "members": True, - 'undoc-members': True, - #'private-members': True, - 'inherited-members': True, -} - -templates_path = ['_templates'] - -source_suffix = { - '.rst': 'restructuredtext', - # '.txt': 'markdown', - #'.md': 'markdown', -} - -master_doc = 'index' - -roject = u'PyFPGA' -copyright = u'2019-2021, Rodrigo Alejandro Melo and contributors' -author = u'Rodrigo Alejandro Melo and contributors' - -version = "latest" -release = version # The full version, including alpha/beta/rc tags. - -language = None - -exclude_patterns = [] - -todo_include_todos = True -todo_link_only = True - -# -- Options for HTML output ---------------------------------------------- - -html_logo = "images/logo.png" - -html_theme_options = { - 'logo_only': True, - 'home_breadcrumbs': False, - 'vcs_pageview_mode': 'blob', -} - -html_context = { - 'gitlab_user': 'rodrigomelo9', - 'gitlab_repo': 'pyfpga', - 'gitlab_version': 'master', - 'conf_py_path': '/doc/', - 'display_gitlab': True -} -#ctx = Path(__file__).resolve().parent / 'context.json' -#if ctx.is_file(): -# html_context.update(loads(ctx.open('r').read())) - -html_theme_path = ["."] -html_theme = "_theme" - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -#html_static_path = ['_static'] - -# Output file base name for HTML help builder. -htmlhelp_basename = 'PyFPGAdoc' - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - 'papersize': 'a4paper', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'PyFPGA.tex', u'PyFPGA Documentation', author, 'manual'), -] - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'PyFPGA', u'PyFPGA Documentation', [author], 1) -] - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'PyFPGA', u'PyFPGA Documentation', author, 'PyFPGA', 'Python Class for vendor-independent FPGA development', 'Miscellaneous'), -] - -# -- Sphinx.Ext.InterSphinx ----------------------------------------------- - -#intersphinx_mapping = { -# 'python': ('https://docs.python.org/3.8/', None), -# 'ghdl': ('https://ghdl.github.io/ghdl', None), -# 'vunit': ('https://vunit.github.io', None), -# 'matplotlib': ('https://matplotlib.org/', None) -#} - -# -- Sphinx.Ext.ExtLinks -------------------------------------------------- -extlinks = { - 'wikipedia': ('https://en.wikipedia.org/wiki/%s', None), - 'repo': ('https://github.com/PyFPGA/pyfpga/blob/main/%s', None) -} diff --git a/doc/dev.rst b/doc/dev.rst deleted file mode 100644 index 39486ea3..00000000 --- a/doc/dev.rst +++ /dev/null @@ -1,75 +0,0 @@ -.. program:: pyfpga - -Development Notes -################# - -To install a local clone of the repository (for development): - -.. code-block:: shell - - git clone https://gitlab.com/rodrigomelo9/pyfpga.git - cd pyfpga - sudo pip install -e . - -.. NOTE:: - With `-e` (`--editable`) your application is installed into site-packages - via a kind of symlink, so you do not need to reinstall it after changes. - -PyFPGA uses PEP8 guidelines. - -The following is an overview of the main PyFPGA components and its -relationship, which is explained in the sub-sections of this document. - -.. figure:: images/schema.png - :align: center - :alt: PyFPGA components - - PyFPGA components - -fpga/tool/template.tcl -====================== - -Many (all?) FPGA development Tools provides a Tcl (Tool Command Language) -interface for the Bitstream generation. -This multi-vendor master Tcl was developed, where the different commands to -solve the complete workflow were encapsulated into procedures -(using the ``fpga_*`` prefix to avoid namespace conflicts). - -.. NOTE:: - To add a new Tool, a *case* in the *switch* of each ``fpga_*`` must be - provided. - -.. NOTE:: - This file is compliant with Tcl 8.4 because is the oldest used by a - supported FPGA Tool (Xilinx ISE). - -fpga/tool/.py -=================== - -A base class (``__init__.py``) was developed to provides a uniform API to be -implemented for each supported Tool. -Also, validation of values is performed here. - -Classes to supports each Tool (``.py``) implements the base class, ideally -setting a few variables. - -.. NOTE:: - Transfer of the bitstream to a device is not always performed by a Tcl - script, so special methods must be developed, following the proposed API. - -fpga/project.py -=============== - -This class implements the Application Programming Interface (API) which is -employed to manage an FPGA project. It solves high-level things such as -collect several files using glob, setting and use of a working/output -directory and time measurement. - -FAQ -==== - -How to deal with deprecated Tcl commands? ------------------------------------------ - -From Vivado 2019.1 to 2019.2, ``open_hw`` changed to ``open_hw_manager``: -``if { [ catch { open_hw_manager } ] } { open_hw }`` diff --git a/doc/index.rst b/doc/index.rst deleted file mode 100644 index 8975c13b..00000000 --- a/doc/index.rst +++ /dev/null @@ -1,32 +0,0 @@ -.. program:: pyfpga - -PyFPGA Documentation -#################### - -.. image:: images/logo.png - :width: 200 px - :align: center - :target: https://github.com/PyFPGA/pyfpga - -.. raw:: html - -

- -

- -
- -.. ATTENTION:: - - (2022-05-15) PyFPGA is in the process of being strongly rewritten/simplified. - Most changes are internal, but the API (`Project` class) will change. - -.. toctree:: - - intro - basic - advanced - tools - api - dev diff --git a/doc/intro.rst b/doc/intro.rst deleted file mode 100644 index db1d89d4..00000000 --- a/doc/intro.rst +++ /dev/null @@ -1,87 +0,0 @@ -.. program:: pyfpga - -Introduction -############ - -.. ATTENTION:: - - PyFPGA assumes that the backend Tool is ready to run. - This implies, depending on the operating system, things such as: - - * Tool installed. - * A valid License configured. - * Tool available in the system PATH. - * GNU/Linux: extra packages installed, environment variables assigned - and permissions granted on devices (to transfer the bitstream). - -Detailed support ----------------- - -+------------------------------+---------+----------+------------+-----------+----------+ -| | ISE | Libero | Openflow | Quartus | Vivado | -+==============================+=========+==========+============+===========+==========+ -|**add_files** | | | | | | -+------------------------------+---------+----------+------------+-----------+----------+ -|``vhdl`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|``verilog`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|``system_verilog`` | ``TBD`` | ``TBD`` | ``TBD`` | ``TBD`` | ``TBD`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|``constraint`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|``block_design`` | ``NY`` | ``NY`` | ``NY`` | ``NY`` | ``Yes`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|**set_param** | | | | | | -+------------------------------+---------+----------+------------+-----------+----------+ -|``boolean`` (*VHDL/Verilog*) | ``TBD`` | ``TBD`` |``TBD`` | ``TBD`` | ``TBD`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|``integer`` (*VHDL/Verilog*) | ``TBD`` | ``TBD`` |``TBD`` | ``TBD`` | ``TBD`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|``string`` (*VHDL/Verilog*) | ``TBD`` | ``TBD`` |``TBD`` | ``TBD`` | ``TBD`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|``real`` (*VHDL/Verilog*) | ``TBD`` | ``TBD`` |``TBD`` | ``TBD`` | ``TBD`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|``std_logic`` (*VHDL*) | ``TBD`` | ``TBD`` |``TBD`` | ``TBD`` | ``TBD`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|``std_logic_vector`` (*VHDL*) | ``TBD`` | ``TBD`` |``TBD`` | ``TBD`` | ``TBD`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|**add_path** | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|**set_define** (*Verilog*) | ``TBI`` | ``TBI`` | ``TBI`` | ``TBI`` | ``TBI`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|**set_arch** (*VHDL*) | ``TBI`` | ``TBI`` | ``TBI`` | ``TBI`` | ``TBI`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|**generate** | | | | | | -+------------------------------+---------+----------+------------+-----------+----------+ -|``prj`` | ``Yes`` | ``Yes`` | ``No`` | ``Yes`` | ``Yes`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|``syn`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|``imp`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|``bit`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|**transfer** | | | | | | -+------------------------------+---------+----------+------------+-----------+----------+ -|``fpga`` | ``Yes`` | ``NY`` | ``Yes`` | ``Yes`` | ``Yes`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|``spi`` | ``Yes`` | ``NY`` | ``NY`` | ``NY`` | ``NY`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|``bpi`` | ``Yes`` | ``NY`` | ``NY`` | ``NY`` | ``NY`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|``detect`` | ``Yes`` | ``NY`` | ``NY`` | ``Yes`` | ``Yes`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|``unlock`` | ``Yes`` | ``No`` | ``No`` | ``No`` | ``No`` | -+------------------------------+---------+----------+------------+-----------+----------+ - -* ``Yes``: already supported -* ``No``: no plans (or unneeded) -* ``NY``: Not yet, but maybe someday -* ``TBD``: To Be Defined -* ``TBI``: To Be Implemented - -Next Steps ----------- - -You can read the :ref:`basic` and :ref:`advanced` sections, check the detailed :ref:`api` or start with the available :repo:`Examples `. diff --git a/doc/requirements.txt b/doc/requirements.txt deleted file mode 100644 index 2406283f..00000000 --- a/doc/requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -#-r ../requirements.txt -sphinx>=3.0.0 -recommonmark -python-dateutil -# sphinxcontrib-textstyle>=0.2.1 -# sphinxcontrib-spelling>=2.2.0 -# changelog>=0.3.5 \ No newline at end of file diff --git a/doc/tools.rst b/doc/tools.rst deleted file mode 100644 index 365a4844..00000000 --- a/doc/tools.rst +++ /dev/null @@ -1,34 +0,0 @@ -.. program:: pyfpga - -Per tool usage -############## - -+---------------+-----------+---------+-----+-----------------------------------------------+ -| Tools | Vendor | Version | Tcl | Comment | -+===============+===========+=========+=====+===============================================+ -| ISE | Xilinx | 14.7 | 8.4 | Discontinued in 2013 | -+---------------+-----------+---------+-----+-----------------------------------------------+ -| Libero-SoC | Microsemi | 12.2 | 8.5 | Important changes in version 12.0 (2019) | -+---------------+-----------+---------+-----+-----------------------------------------------+ -| Quartus Prime | Intel | 19.1 | 8.6 | Known as Quartus II until version 15.0 (2015) | -+---------------+-----------+---------+-----+-----------------------------------------------+ -| Vivado | Xilinx | 2019.1 | 8.5 | Introduced in 2012, it superseded ISE | -+---------------+-----------+---------+-----+-----------------------------------------------+ -| Yosys | | 0.9-dev | 8.6 | The open-source synthesizer | -+---------------+-----------+---------+-----+-----------------------------------------------+ - - -* ISE supports devices starting from Spartan 3/Virtex 4 until some first members of the 7 series. - Previous Spartan/Virtex devices were supported until version 10. Vivado supports devices starting - from the 7 series. - -* Libero-SoC had a fork for PolarFire devices which was merged in version 12.0 (2019). - Libero SoC v12.0 and later supports PolarFire, RTG4, SmartFusion2 and IGLOO2 FPGA families. - Libero SoC v11.9 and earlier are the alternative to work with SmartFusion, IGLOO, ProASIC3 and - Fusion families. - Libero IDE v9.2 (2016) was the last version of the previous tool to work with antifuse and older - flash devices. - -* Since the change from Quartus II to Prime, three editions are available: Pro (for Agilex, - Stratix 10, Arria 10 and Cyclone GX devices), Standard (for Cyclone 10 LP and earlier devices) - and Lite (a high-volume low-end subset of the Standard edition). diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..13352008 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,17 @@ +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = build +HELPERS = $(BUILDDIR)/hdl2bit $(BUILDDIR)/prj2bit $(BUILDDIR)/bitprog + +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +$(HELPERS): + @mkdir -p $(@D) + @python3 ../pyfpga/helpers/$(@F).py -h > $@ + +%: Makefile $(HELPERS) + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/advanced.rst b/docs/advanced.rst new file mode 100644 index 00000000..84046c48 --- /dev/null +++ b/docs/advanced.rst @@ -0,0 +1,56 @@ +Advanced usage +============== + +The flow implemented by PyFPGA should be sufficient for most cases, but further customizations are possible and discussed in this section. + +Hooks +----- + +Hooks allow you to insert custom code at specific stages of the project lifecycle. The available hooks are: + ++---------+---------------------------------------------------------------------------------------------+ +| Stage | Description | ++=========+=============================================================================================+ +| precfg | Code inserted after project creation and before files inclusion (e.g., specify HDL version) | ++---------+---------------------------------------------------------------------------------------------+ +| postcfg | Code inserted after files inclusion (e.g., additional project configurations) | ++---------+---------------------------------------------------------------------------------------------+ +| presyn | Code inserted before synthesis (e.g., synthesis-specific options) | ++---------+---------------------------------------------------------------------------------------------+ +| postsyn | Code inserted after synthesis (e.g., report generation) | ++---------+---------------------------------------------------------------------------------------------+ +| prepar | Code inserted before place and route (e.g., place-and-route-specific options) | ++---------+---------------------------------------------------------------------------------------------+ +| postpar | Code inserted after place and route (e.g., report generation) | ++---------+---------------------------------------------------------------------------------------------+ +| prebit | Code inserted before bitstream generation (e.g., bitstream-specific options) | ++---------+---------------------------------------------------------------------------------------------+ +| postbit | Code inserted after bitstream generation (e.g., report generation) | ++---------+---------------------------------------------------------------------------------------------+ + +You can specify hooks for a specific stage either line-by-line: + +.. code-block:: python + + prj.add_hook('presyn', 'COMMAND1') + prj.add_hook('presyn', 'COMMAND2') + prj.add_hook('presyn', 'COMMAND3') + +Or in a multi-line format: + +.. code-block:: python + + prj.add_hook('presyn', """ + COMMAND1 + COMMAND2 + COMMAND3 + """) + +Options +------- + +Options allow you to specify additional settings to fine-tune certain commands. The available options are: + +.. ATTENTION:: + + This feature is WIP. diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 00000000..ef62c294 --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,4 @@ +API Reference +============= + +.. automodule:: pyfpga.project diff --git a/docs/basic.rst b/docs/basic.rst new file mode 100644 index 00000000..0b40c35f --- /dev/null +++ b/docs/basic.rst @@ -0,0 +1,146 @@ +Basic usage +=========== + +Project Configuration +--------------------- + +The first steps involve importing the necessary module to support the desired tool and instantiating the corresponding ``class``: + +.. code-block:: python + + from pyfpga.vivado import Vivado + + prj = Vivado('PRJNAME', odir='OUTDIR') + +In the example, we are using Vivado, specifying the optional parameters +``project name`` (``tool name`` if omitted) and ``output directory`` +(``results`` by default). + +Next step is to specify the target FPGA device: + +.. code-block:: python + + prj.set_part('xc7k160t-3-fbg484') + +.. note:: + + Default parts are provided for each supported tool. + +HDL source files are added using one of the following methods: + +.. code-block:: python + + prj.add_vhdl('PATH_TO_FILES_GLOB_COMPATIBLE', 'OPTIONAL_LIBNAME') + prj.add_vlog('PATH_TO_FILES_GLOB_COMPATIBLE') + prj.add_slog('PATH_TO_FILES_GLOB_COMPATIBLE') + +In these methods, you provide a path to the files. The path can include wildcards (like ``*.vhdl``), allowing you to match multiple files at once. +In case of ``add_vhdl``, you can also optionally specify a library name where the files will be included. + +.. note:: + + Internally, the methods that specify files use `glob`_ to support wildcards and `Path`_ to obtain absolute paths. + + .. _glob: https://docs.python.org/3/library/glob.html + .. _Path: https://docs.python.org/3/library/pathlib.html + +.. hint:: + + Files are processed in the order they are added. If a file is specified more than once, + it is removed from its previous position and placed at the end of the list. + This allows you to ensure that a file is processed after others when necessary + (e.g., placing a top-level at the end) or to customize options + (e.g., removing a VHDL library specification in case of a top-level) + when multiple files are added using a wildcard. + +Generics/parameters can be specified with: + +.. code-block:: python + + prj.add_param('PARAMNAME', 'PARAMVALUE') + +For Verilog and SystemVerilog, the following methods are also available: + +.. code-block:: python + + prj.add_include('PATH_TO_A_DIRECTORY') + prj.add_define('DEFNAME', 'DEFVALUE') + +Constraint source files are included using the following: + +.. code-block:: python + + prj.add_cons('PATH_TO_FILES_GLOB_COMPATIBLE') + +Finally, the top-level can be specified as follows: + +.. code-block:: python + + prj.set_top('Top') + +.. note:: + + The order of the methods described in this section is not significant. + They will be arranged in the required order by the underlying template. + +Bitstream generation +-------------------- + +After configuring the project, you can run the following to generate a bitstream: + +.. code-block:: python + + prj.make() + +By default, this method performs **project creation**, **synthesis**, **place and route**, +and **bitstream generation**. +However, you can optionally specify both the initial and final stages, as follows: + +.. code-block:: python + + prj.make(first='syn', last='par') + +.. note:: + + Valid values are: + + * ``cfg``: generates the project file + * ``syn``: performs synthesis + * ``par``: performs place and route + * ``bit``: performs bitstream generation + +.. note:: + + After executing this method, you will find the file ``.tcl`` + (``.sh`` in some cases) in the output directory. + For debugging purposes, if things do not work as expected, you can review this file. + +Bitstream programming +--------------------- + +The final step is programming the FPGA: + +.. code-block:: python + + prj.prog('BITSTREAM', 'POSITION') + +Both ``BITSTREAM`` and ``POSITION`` are optional. +If ``BITSTREAM`` is not specified, PyFPGA will attempt to discover it based on project information. +The ``POSITION`` parameter is not always required (depends on the tool being used). + +.. note:: + + After executing this method, you will find the file ``prog.tcl`` + (``-prog.sh`` in some cases) in the output directory. + For debugging purposes, if things do not work as expected, you can review this file. + +Debugging +--------- + +Under the hood, `logging`_ is employed. To enable debug messages, you can use: + +.. code-block:: python + + prj.set_debug() + +.. _logging: https://docs.python.org/3/library/logging.html diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..530ce742 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +import sys, re +from pathlib import Path + +sys.path.insert(0, str(Path.cwd().resolve().parent)) + +# -- Project information ----------------------------------------------------- + +project = 'PyFPGA' +copyright = '2016-2024, PyFPGA Project' +author = 'PyFPGA contributors' + +# -- General configuration --------------------------------------------------- + +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.extlinks', + 'sphinx.ext.intersphinx', + 'sphinx.ext.todo', + 'sphinx.ext.viewcode', +] + +autodoc_default_options = { + "members": True, + 'undoc-members': True, + 'inherited-members': True, +} + +extlinks = { + 'repositoy': ('https://github.com/PyFPGA/pyfpga/tree/main/%s', None) +} + +exclude_patterns = ['build'] + +# -- Options for HTML output ------------------------------------------------- + +html_theme = 'sphinx_rtd_theme' +html_static_path = ['images'] diff --git a/docs/extending.rst b/docs/extending.rst new file mode 100644 index 00000000..746ffc5d --- /dev/null +++ b/docs/extending.rst @@ -0,0 +1,76 @@ +Extending +========= + +.. note:: + + All classes inherit from ``Project`` (``project.py``). + +This is a guide on how to add support for a new TOOL. + +Add support for the new tool +---------------------------- + +.. code-block:: bash + + pyfpga/templates/.jinja + pyfpga/templates/-prog.jinja + pyfpga/.py + pyfpga/factory.py # UPDATE + pyfpga/helpers/prj2bit.py # UPDATE + +Add tests and a tool mock-up +---------------------------- + +.. code-block:: bash + + tests/test_tools.py # UPDATE + tests/support.py # UPDATE if exceptions are needed + tests/mocks/ + +Add examples +------------ + +.. code-block:: bash + + examples/sources/cons//timing. + examples/sources/cons//clk. + examples/sources/cons//led. + examples/projects/.py + examples/projects/regress.sh # UPDATE + examples/helpers/.sh + examples/hooks/.py # OPTIONAL + +Verify the code +--------------- + +Run it at the root of the repo. + +.. code-block:: bash + + make docs + make lint + make test + +.. tip:: + + You can simply run ``make`` to perform all the operations. + Running ``make clean`` will remove all the generated files. + +Verify the functionality +------------------------ + +.. code-block:: bash + + cd examples/projects/ + bash regress.sh + cd ../../tests/ + python3 support.py --tool + +Updated the documentation +------------------------- + +.. code-block:: bash + + README.md + docs/intro.rst + docs/tools.rst diff --git a/docs/helpers.rst b/docs/helpers.rst new file mode 100644 index 00000000..619f2c35 --- /dev/null +++ b/docs/helpers.rst @@ -0,0 +1,17 @@ +Helpers +======= + +hdl2bit +------- + +.. literalinclude:: build/hdl2bit + +prj2bit +------- + +.. literalinclude:: build/prj2bit + +bitprog +------- + +.. literalinclude:: build/bitprog diff --git a/docs/images/Makefile b/docs/images/Makefile new file mode 100644 index 00000000..2f5b759b --- /dev/null +++ b/docs/images/Makefile @@ -0,0 +1,15 @@ +#!/usr/bin/make + +FILES = $(wildcard *.dot) +FILES := $(basename $(FILES)) +FILES := $(addsuffix .svg,$(FILES)) + +ODIR = . + +vpath %.svg $(ODIR) + +%.svg: %.dot + @mkdir -p $(ODIR) + dot -Tsvg $< -o $(ODIR)/$@ + +all: $(FILES) diff --git a/doc/images/images.fodg b/docs/images/images.fodg similarity index 100% rename from doc/images/images.fodg rename to docs/images/images.fodg diff --git a/doc/images/logo.fodg b/docs/images/logo.fodg similarity index 100% rename from doc/images/logo.fodg rename to docs/images/logo.fodg diff --git a/doc/images/logo.png b/docs/images/logo.png similarity index 100% rename from doc/images/logo.png rename to docs/images/logo.png diff --git a/docs/images/openflow.dot b/docs/images/openflow.dot new file mode 100644 index 00000000..1d1dec16 --- /dev/null +++ b/docs/images/openflow.dot @@ -0,0 +1,28 @@ +digraph openflow { + graph [ranksep=0.25]; + node [shape = doublecircle]; + node [shape = rectangle]; + GHDL "ghdl-yosys-plugin" Yosys "nextpnr-ice40" "nextpnr-ecp5" icetime icepack iceprog eccpack; + node [shape = note ]; + VHDL Verilog; + node [shape = box3d ]; + ice40; + node [shape = oval]; + "bit-ice40" [label=".bit"]; + "bit-ecp5" [label=".bit"]; + VHDL -> {GHDL "ghdl-yosys-plugin"}; + GHDL -> "ghdl-yosys-plugin"; + "ghdl-yosys-plugin" -> Yosys; + Verilog -> Yosys; + Yosys -> ".json"; + ".json" -> {"nextpnr-ice40" "nextpnr-ecp5"}; + "nextpnr-ice40" -> ".asc"; + "nextpnr-ecp5" -> ".config"; + ".asc" -> {icetime icepack}; + icepack -> "bit-ice40"; + "bit-ice40" -> iceprog; + iceprog -> ice40; + ".config" -> eccpack; + eccpack -> "bit-ecp5"; + {rank = same; GHDL ; "ghdl-yosys-plugin"; Yosys;} +} diff --git a/docs/images/openflow.svg b/docs/images/openflow.svg new file mode 100644 index 00000000..3ee12ac3 --- /dev/null +++ b/docs/images/openflow.svg @@ -0,0 +1,224 @@ + + + + + + +openflow + + + +GHDL + +GHDL + + + +ghdl-yosys-plugin + +ghdl-yosys-plugin + + + +GHDL->ghdl-yosys-plugin + + + + + +Yosys + +Yosys + + + +ghdl-yosys-plugin->Yosys + + + + + +.json + +.json + + + +Yosys->.json + + + + + +nextpnr-ice40 + +nextpnr-ice40 + + + +.asc + +.asc + + + +nextpnr-ice40->.asc + + + + + +nextpnr-ecp5 + +nextpnr-ecp5 + + + +.config + +.config + + + +nextpnr-ecp5->.config + + + + + +icetime + +icetime + + + +icepack + +icepack + + + +bit-ice40 + +.bit + + + +icepack->bit-ice40 + + + + + +iceprog + +iceprog + + + +ice40 + + + + +ice40 + + + +iceprog->ice40 + + + + + +eccpack + +eccpack + + + +bit-ecp5 + +.bit + + + +eccpack->bit-ecp5 + + + + + +VHDL + + + +VHDL + + + +VHDL->GHDL + + + + + +VHDL->ghdl-yosys-plugin + + + + + +Verilog + + + +Verilog + + + +Verilog->Yosys + + + + + +bit-ice40->iceprog + + + + + +.json->nextpnr-ice40 + + + + + +.json->nextpnr-ecp5 + + + + + +.asc->icetime + + + + + +.asc->icepack + + + + + +.config->eccpack + + + + + diff --git a/doc/images/schema.png b/docs/images/schema.png similarity index 100% rename from doc/images/schema.png rename to docs/images/schema.png diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..3bfdf197 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,24 @@ +PyFPGA's documentation +====================== + +.. image:: images/logo.png + :width: 200 px + :align: center + :target: https://github.com/PyFPGA/pyfpga + +.. toctree:: + + intro + basic + advanced + helpers + api + tools + internals + extending + +.. |timestamp| date:: %Y-%m-%d %H:%M (%Z) + +.. note:: + + Documentation generated on |timestamp|. diff --git a/docs/internals.rst b/docs/internals.rst new file mode 100644 index 00000000..504b69ec --- /dev/null +++ b/docs/internals.rst @@ -0,0 +1,78 @@ +Internals +========= + +Underlying steps +---------------- + +.. code-block:: + + project creation [options] + project configuration + part + precfg hook + params + defines + includes + files [options] + top + postcfg hook + project close + + project open + presyn hook + synthesis [options] + postsyn hook + prepar hook + place_and_route [options] + postpar hook + prebit hook + bitstream [options] + postbit hook + project close + +Internal data structure +----------------------- + +.. code-block:: + + data = { + 'part': 'PARTNAME', + 'includes': ['DIR1', 'DIR2', 'DIR3'], + 'files': { + 'FILE1': {'hdl': 'vhdl', 'lib': 'LIB1', 'opt': 'OPTS'}, + 'FILE2': {'hdl': 'vlog', 'opt': 'OPTS'}, + 'FILE3': {'hdl': 'slog', 'opt': 'OPTS'} + }, + 'top': 'TOPNAME', + 'constraints': { + 'FILE1': {'opt': 'OPTS'}, + 'FILE2': {'opt': 'OPTS'}, + 'FILE3': {'opt': 'OPTS'} + }, + 'params': { + 'PAR1': 'VAL1', + 'PAR2': 'VAL2', + 'PAR3': 'VAL3' + }, + 'defines': { + 'DEF1': 'VAL1', + 'DEF2': 'VAL2', + 'DEF3': 'VAL3' + }, + 'hooks': { + 'precfg': ['CMD1', 'CMD2'], + 'postcfg': ['CMD1', 'CMD2'], + 'presyn': ['CMD1', 'CMD2'], + 'postsyn': ['CMD1', 'CMD2'], + 'prepar': ['CMD1', 'CMD2'], + 'postpar': ['CMD1', 'CMD2'], + 'prebit': ['CMD1', 'CMD2'], + 'postbit': ['CMD1', 'CMD2'] + }, + 'options': { + 'prj': 'OPTS', + 'syn': 'OPTS', + 'pre': 'OPTS', + 'pre': 'OPTS' + } + } diff --git a/docs/intro.rst b/docs/intro.rst new file mode 100644 index 00000000..abc7e945 --- /dev/null +++ b/docs/intro.rst @@ -0,0 +1,26 @@ +Introduction +============ + +PyFPGA is a Python package that provides an abstraction layer for working with FPGA development tools in a vendor-agnostic, programmatic way. It includes: + +* A **class** for each supported tool, enabling **project creation**, **synthesis**, **place and route**, **bitstream generation**, and **programming**. +* A set of **command-line** helpers for simple projects or quick evaluations. + +With PyFPGA, you can create your own FPGA development workflow tailored to your needs. Some of its key benefits include: + +* A unified API across different tools and devices. +* Compatibility with *Version Control Systems* and *Continuous Integration*. +* Ensured reproducibility and repeatability. +* Lower resource consumption compared to GUI-based workflows. + +It currently supports vendor tools such as ``Diamond``, ``Ise``, ``Quartus``, ``Libero``, and ``Vivado``, as well as ``Openflow``, a solution based on *Free/Libre and Open Source Software* (**FLOSS**). + +.. ATTENTION:: + + PyFPGA assumes that the backend tool is ready to run. + This implies, depending on the operating system, the following: + + * The tool is installed. + * A valid license, if needed, is configured. + * The tool is available in the system PATH. + * On GNU/Linux: required packages are installed, environment variables are set, and permissions are granted for devices (to transfer the bitstream). diff --git a/docs/tools.rst b/docs/tools.rst new file mode 100644 index 00000000..38da3ef3 --- /dev/null +++ b/docs/tools.rst @@ -0,0 +1,174 @@ +Tools +===== + +.. list-table:: Default PyFPGA's parts per tool + :header-rows: 1 + + * - Tool + - Vendor + - Default device + - Name format + * - Diamond + - Lattice + - LFXP2-5E-5TN144C + - device-speed-package + * - ISE + - Xilinx + - XC7K160T-3-FBG484 + - device-speed-package + * - Libero + - Microchip/Microsemi + - MPF100T-1-FCG484 + - device-speed-package + * - Openflow + - FLOSS + - HX8K-CT256 + - device-package + * - Quartus + - Intel/Altera + - 10M50SCE144I7G + - part + * - Vivado + - AMD/Xilinx + - XC7K160T-3-FBG484 + - device-speed-package + +Diamond +------- + +`Diamond downloads `_ + +Diamond is the previous generation EDA tool from Lattice. + +Example: + +.. code:: + + from pyfpga.diamond import Diamond + + prj = Diamond() + +ISE +--- + +`ISE downloads `_ + +ISE (*Integrated Software Environment*) is the previous Xilinx's EDA, superseded by Vivado. +The last version is ISE 14.7, launched in October 2013. +It supports devices starting from Spartan 3/Virtex 4 until some of the first members of the 7 series (all the 7 series and above are supported by Vivado). +Previous Spartan/Virtex devices were supported until version 10. + +.. attention:: + + ISE supports Verilog 2001 and VHDL 1993, but not SystemVerilog. + +Example: + +.. code:: + + from pyfpga.ise import Ise + + prj = Ise() + +Valid PART formats: + +.. code:: + + -- + -- + +Libero +------ + +`Libero downloads `_ + +Libero-SoC (Microsemi, acquired by Microchip in 2018) is the evolution of Libero-IDE (Actel, acquired by Microsemi in 2010). +PyFPGA supports Libero-SoC starting from 12.0, which supports most modern families. +For other devices, Libero-SoC 11.9 or Libero-IDE v9.2 are needed, but these versions are not supported by PyFPGA. + +Example: + +.. code:: + + from pyfpga.libero import Libero + + prj = Libero() + +Valid PART formats: + +.. code:: + + - + - + -- + -- + - + - + -- + -- + +Openflow +-------- + +`Docker downloads `_ + +Openflow is the combination of different Free/Libre and Open Source (FLOSS) tools: + +.. image:: images/openflow.svg + :width: 70% + :align: center + +* Yosys for synthesis, with ghdl-yosys-plugin for VHDL support. +* nextpnr in its ice40 and ecp5 versions. +* Projects icestorm and Trellis. + +It relies on Docker and fine-grain containers. + +.. attention:: + + It is currently the only flow not solved using Tcl (it uses docker in a bash script instead). + +Example: + +.. code:: + + from pyfpga.openflow import Openflow + + prj = Openflow() + +Valid PART formats: + +.. code:: + + - + +Quartus +------- + +`Quartus downloads `_ + +Quartus Prime (Intel) is the continuation of Quartus II (Altera) and is divided into the Pro, Standard, and Lite editions, each supporting different families. + +Example: + +.. code:: + + from pyfpga.quartus import Quartus + + prj = Quartus() + +Vivado +------ + +`Vivado downloads `_ + +Vivado is the current EDA tool from Xilinx, which has superseded ISE and supports the 7 series and above. +It is included with Vitis, the SDK for embedded applications. + +Example: + +.. code:: + + from pyfpga.vivado import Vivado + + prj = Vivado() diff --git a/examples/Makefile b/examples/Makefile deleted file mode 100644 index 77edba5c..00000000 --- a/examples/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/make - - -DIRS=$(wildcard */) - -all: - @$(foreach DIR, $(DIRS), make -C $(DIR);) diff --git a/examples/README.md b/examples/README.md index e648ca00..2ec68b5e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,55 +1,11 @@ -# PyFPGA examples +# PyFPGA Examples -## Tool-specific examples +In this section, you will find: -Led blinking examples where a Bitstream is generated and transfer to a -supported board. It shows the inclusion of Constraints files. +* `projects`: basic but complete examples for each supported tool. +* `helpers`: examples of the PyFPGA helpers. +* `hooks`: how to use this feature. +* `misc`: miscellaneous examples. -* [ghdl](ghdl): VHDL synthesis with GDHL (`--synth`) -* [ise](ise): Spartan-6 FPGA LX9 MicroBoard (Avnet) -* [libero](libero): Digi-Key SmartFusion2 Maker Board (Digi-Key) -* [openflow](openflow): - * IceStick (`icestorm.py`) - * EDU-CIAA-FPGA (`icestorm.py --board edu-ciaa-fpga`) - * OrangeCrab-r0.2 (`prjtrellis.py`) - * ECP5 Evaluation Board (`prjtrellis.py --board ecp5evn`) -* [quartus](quartus): DE10Nano (Terasic) -* [vivado](vivado): Zybo (Digilent) -* [yosys](yosys): - * Verilog synthesis with Yosys (using `ghdl-yosys-plugin` for VHDL) - * Spartan-6 FPGA LX9 MicroBoard (`ise.py`) - * Zybo (`vivado.py`) - -## Multi-project examples - -Examples where more than a project is solved in the same script. - -* [multi/projects.py](multi/projects.py): it uses a dict with three project -names where different tools, part names, files and top-level names can be -specified. In this manner, you can manage alternatives or sub-products of your -design in a single place. -* [multi/verilog.py](multi/verilog.py): here the same set of Verilog files are -synthesised with all the available tools, which is useful to make comparations -and check portability. -* [multi/vhdl.py](multi/vhdl.py): the same concept that the previous one, but -using VHDL instead of Verilog files. The main difference is how to deal with -VHDL libraries. -* [multi/parameters.py](multi/parameters.py): VHDL and Verilog files are -synthesized changing the value of its generics/parameters. -* [multi/memory.py](multi/memory.py): it tests the Memory Content Files -inclusion capability of the supported tools. - -## Hooks examples - -* [hooks/strategies.py](hooks/strategies.py): the same HDL is synthesized by -different tools, changing the optimization strategy (`area`, `power` and -`speed`). - -## Helpers - -Examples to exercise developed helper tools such as `hdl2bit`, `prj2bit` and -`bitprog`. - -## Miscellaneous examples - -* [misc/capture.py](misc/capture.py): it shows how to capture the execution messages. +For an example where all the tools are employed based on the same code, you can check +[support.py](../tests/support.py) (located under the [tests](../tests) directory). diff --git a/examples/configs.yml b/examples/configs.yml deleted file mode 100644 index 9feaf6b7..00000000 --- a/examples/configs.yml +++ /dev/null @@ -1,26 +0,0 @@ -openflow: - oci: - engine: - command: docker - volumes: ["$HOME:$HOME"] - work: $PWD - containers: - ghdl: "ghdl/synth:beta" - yosys: "ghdl/synth:beta" - nextpnr-ice40: "ghdl/synth:nextpnr-ice40" - icetime: "ghdl/synth:icestorm" - icepack: "ghdl/synth:icestorm" - iceprog: "--device /dev/bus/usb ghdl/synth:prog" - nextpnr-ecp5: "ghdl/synth:nextpnr-ecp5" - ecppack: "ghdl/synth:trellis" - openocd: "--device /dev/bus/usb ghdl/synth:prog" - tools: - ghdl: ghdl - yosys: yosys - nextpnr-ice40: nextpnr-ice40 - icetime: icetime - icepack: icepack - iceprog: iceprog - nextpnr-ecp5: nextpnr-ecp5 - ecppack: ecppack - openocd: openocd diff --git a/examples/ghdl/Makefile b/examples/ghdl/Makefile deleted file mode 100644 index cd77fa54..00000000 --- a/examples/ghdl/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/make - -SCRIPTS = $(wildcard *.py) - -all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT);) diff --git a/examples/ghdl/ghdl.py b/examples/ghdl/ghdl.py deleted file mode 100644 index 9962d1bf..00000000 --- a/examples/ghdl/ghdl.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Generic GHDL example project.""" - -import logging - -from fpga.project import Project - -logging.basicConfig() - -prj = Project('ghdl') -prj.set_outdir('../../build/ghdl') - -prj.add_files('../../hdl/blinking.vhdl', library='examples') -prj.add_files('../../hdl/examples_pkg.vhdl', library='examples') -prj.add_files('../../hdl/top.vhdl') -prj.set_top('Top') - -try: - prj.generate() -except RuntimeError: - print('ERROR:generate:Docker not found') diff --git a/examples/helpers/Makefile b/examples/helpers/Makefile deleted file mode 100644 index dbec8388..00000000 --- a/examples/helpers/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/make - -OUTDIR = ../../build/helpers -HLPDIR = ../../fpga/helpers -HDLDIR = ../../hdl - -all: hdl2bit-vhdl hdl2bit-verilog prj2bit bitprog - -hdl2bit-vhdl: - python3 $(HLPDIR)/hdl2bit.py \ - --outdir $(OUTDIR) \ - --tool ise --run syn \ - -f $(HDLDIR)/*.vhdl,examples $(HDLDIR)/top.vhdl - -hdl2bit-verilog: - python3 $(HLPDIR)/hdl2bit.py \ - --outdir $(OUTDIR) \ - --tool ise --run syn \ - -i $(HDLDIR)/headers1 -i $(HDLDIR)/headers2 \ - -f $(HDLDIR)/blinking.v $(HDLDIR)/top.v - -prj2bit: - mkdir -p $(OUTDIR) - cp ise.xise quartus.* vivado.xpr $(OUTDIR) - python3 $(HLPDIR)/prj2bit.py --run syn $(OUTDIR)/ise.xise - python3 $(HLPDIR)/prj2bit.py --run syn $(OUTDIR)/quartus.qpf - python3 $(HLPDIR)/prj2bit.py --run syn $(OUTDIR)/vivado.xpr - -bitprog: - python3 $(HLPDIR)/bitprog.py \ - --outdir $(OUTDIR) \ - --tool ise \ - -d fpga -p 2 \ - ../../README.md - -clean: - python3 $(HLPDIR)/prj2bit.py --clean $(OUTDIR)/ise.xise - python3 $(HLPDIR)/prj2bit.py --clean $(OUTDIR)/quartus.qpf - python3 $(HLPDIR)/prj2bit.py --clean $(OUTDIR)/vivado.xpr diff --git a/examples/helpers/ise.sh b/examples/helpers/ise.sh new file mode 100644 index 00000000..a366c6e9 --- /dev/null +++ b/examples/helpers/ise.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e + +HDIR=../../pyfpga/helpers + +python3 $HDIR/hdl2bit.py -t ise -o results/ise-vlog -p xc6slx16-3-csg32 \ + -i ../sources/vlog/include1 -i ../sources/vlog/include2 \ + -f ../sources/vlog/blink.v -f ../sources/vlog/top.v \ + -f ../sources/cons/nexys3/timing.xcf -f ../sources/cons/nexys3/clk.ucf -f ../sources/cons/nexys3/led.ucf \ + --define DEFINE1 1 --define DEFINE2 1 --param FREQ 125000000 --param SECS 1 Top + +python3 $HDIR/hdl2bit.py -t ise -o results/ise-vhdl -p xc6slx16-3-csg32 --project example \ + -f ../sources/vhdl/blink.vhdl,blink_lib -f ../sources/vhdl/blink_pkg.vhdl,blink_lib -f ../sources/vhdl/top.vhdl \ + -f ../sources/cons/nexys3/timing.xcf -f ../sources/cons/nexys3/clk.ucf -f ../sources/cons/nexys3/led.ucf \ + --param FREQ 125000000 --param SECS 1 --last cfg Top + +python3 $HDIR/prj2bit.py results/ise-vhdl/example.xise diff --git a/examples/helpers/ise.xise b/examples/helpers/ise.xise deleted file mode 100644 index 6233d9b1..00000000 --- a/examples/helpers/ise.xise +++ /dev/null @@ -1,353 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/helpers/libero.sh b/examples/helpers/libero.sh new file mode 100644 index 00000000..8d270c48 --- /dev/null +++ b/examples/helpers/libero.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e + +HDIR=../../pyfpga/helpers + +python3 $HDIR/hdl2bit.py -t libero -o results/libero-vlog -p m2s010-1-tq144 \ + -i ../sources/vlog/include1 -i ../sources/vlog/include2 \ + -f ../sources/vlog/blink.v -f ../sources/vlog/top.v \ + -f ../sources/cons/maker/timing.sdc -f ../sources/cons/maker/clk.pdc -f ../sources/cons/maker/led.pdc \ + --define DEFINE1 1 --define DEFINE2 1 --param FREQ 125000000 --param SECS 1 Top + +python3 $HDIR/hdl2bit.py -t libero -o results/libero-vhdl -p m2s010-1-tq144 --project example \ + -f ../sources/vhdl/blink.vhdl,blink_lib -f ../sources/vhdl/blink_pkg.vhdl,blink_lib -f ../sources/vhdl/top.vhdl \ + -f ../sources/cons/maker/timing.sdc -f ../sources/cons/maker/clk.pdc -f ../sources/cons/maker/led.pdc \ + --param FREQ 125000000 --param SECS 1 --last cfg Top + +python3 $HDIR/prj2bit.py results/libero-vhdl/libero/example.prjx diff --git a/examples/helpers/openflow.sh b/examples/helpers/openflow.sh new file mode 100644 index 00000000..7b959a1a --- /dev/null +++ b/examples/helpers/openflow.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +set -e + +HDIR=../../pyfpga/helpers + +python3 $HDIR/hdl2bit.py -t openflow -o results/openflow-vlog -p hx1k-tq144 \ + -i ../sources/vlog/include1 -i ../sources/vlog/include2 \ + -f ../sources/vlog/blink.v -f ../sources/vlog/top.v \ + -f ../sources/cons/icestick/clk.pcf -f ../sources/cons/icestick/led.pcf \ + --define DEFINE1 1 --define DEFINE2 1 --param FREQ 100000000 --param SECS 1 Top diff --git a/examples/helpers/quartus.qpf b/examples/helpers/quartus.qpf deleted file mode 100644 index 4abe28eb..00000000 --- a/examples/helpers/quartus.qpf +++ /dev/null @@ -1,3 +0,0 @@ -QUARTUS_VERSION = "18.1" -DATE = "22:03:07 March 16, 2020" -PROJECT_REVISION = "quartus" diff --git a/examples/helpers/quartus.qsf b/examples/helpers/quartus.qsf deleted file mode 100644 index 3c046b57..00000000 --- a/examples/helpers/quartus.qsf +++ /dev/null @@ -1,24 +0,0 @@ -set_global_assignment -name FAMILY "Cyclone V" -set_global_assignment -name DEVICE 5CSEBA6U23I7 -set_global_assignment -name TOP_LEVEL_ENTITY top -set_global_assignment -name ORIGINAL_QUARTUS_VERSION 18.1.0 -set_global_assignment -name PROJECT_CREATION_TIME_DATE "22:03:07 MARCH 16, 2020" -set_global_assignment -name LAST_QUARTUS_VERSION "18.1.0 Lite Edition" -set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files -set_global_assignment -name MIN_CORE_JUNCTION_TEMP "-40" -set_global_assignment -name MAX_CORE_JUNCTION_TEMP 100 -set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR 256 -set_global_assignment -name EDA_SIMULATION_TOOL "ModelSim-Altera (Verilog)" -set_global_assignment -name EDA_TIME_SCALE "1 ps" -section_id eda_simulation -set_global_assignment -name EDA_OUTPUT_DATA_FORMAT "VERILOG HDL" -section_id eda_simulation -set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "23 MM HEAT SINK WITH 200 LFPM AIRFLOW" -set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)" -set_global_assignment -name TCL_SCRIPT_FILE ../../examples/quartus/de10nano.tcl -set_global_assignment -name SDC_FILE ../../examples/quartus/de10nano.sdc -set_global_assignment -name VHDL_FILE ../../hdl/top.vhdl -set_global_assignment -name VHDL_FILE ../../hdl/examples_pkg.vhdl -library examples -set_global_assignment -name VHDL_FILE ../../hdl/blinking.vhdl -library examples -set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top -set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top -set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top -set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top diff --git a/examples/helpers/quartus.sh b/examples/helpers/quartus.sh new file mode 100644 index 00000000..b532ed83 --- /dev/null +++ b/examples/helpers/quartus.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e + +HDIR=../../pyfpga/helpers + +python3 $HDIR/hdl2bit.py -t quartus -o results/quartus-vlog -p 5CSEBA6U23I7 \ + -i ../sources/vlog/include1 -i ../sources/vlog/include2 \ + -f ../sources/vlog/blink.v -f ../sources/vlog/top.v \ + -f ../sources/cons/de10nano/timing.sdc -f ../sources/cons/de10nano/clk.tcl -f ../sources/cons/de10nano/led.tcl \ + --define DEFINE1 1 --define DEFINE2 1 --param FREQ 125000000 --param SECS 1 Top + +python3 $HDIR/hdl2bit.py -t quartus -o results/quartus-vhdl -p 5CSEBA6U23I7 --project example \ + -f ../sources/vhdl/blink.vhdl,blink_lib -f ../sources/vhdl/blink_pkg.vhdl,blink_lib -f ../sources/vhdl/top.vhdl \ + -f ../sources/cons/de10nano/timing.sdc -f ../sources/cons/de10nano/clk.tcl -f ../sources/cons/de10nano/led.tcl \ + --param FREQ 125000000 --param SECS 1 --last cfg Top + +python3 $HDIR/prj2bit.py results/quartus-vhdl/example.qpf diff --git a/examples/helpers/vivado.sh b/examples/helpers/vivado.sh new file mode 100644 index 00000000..868599a3 --- /dev/null +++ b/examples/helpers/vivado.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e + +HDIR=../../pyfpga/helpers + +python3 $HDIR/hdl2bit.py -t vivado -o results/vivado-vlog -p xc7z010-1-clg400 \ + -i ../sources/vlog/include1 -i ../sources/vlog/include2 \ + -f ../sources/vlog/blink.v -f ../sources/vlog/top.v \ + -f ../sources/cons/ZYBO/timing.xdc -f ../sources/cons/ZYBO/clk.xdc -f ../sources/cons/ZYBO/led.xdc \ + --define DEFINE1 1 --define DEFINE2 1 --param FREQ 125000000 --param SECS 1 Top + +python3 $HDIR/hdl2bit.py -t vivado -o results/vivado-vhdl -p xc7z010-1-clg400 --project example \ + -f ../sources/vhdl/blink.vhdl,blink_lib -f ../sources/vhdl/blink_pkg.vhdl,blink_lib -f ../sources/vhdl/top.vhdl \ + -f ../sources/cons/ZYBO/timing.xdc -f ../sources/cons/ZYBO/clk.xdc -f ../sources/cons/ZYBO/led.xdc \ + --param FREQ 125000000 --param SECS 1 --last cfg Top + +python3 $HDIR/prj2bit.py results/vivado-vhdl/example.xpr diff --git a/examples/helpers/vivado.xpr b/examples/helpers/vivado.xpr deleted file mode 100644 index 521ddb20..00000000 --- a/examples/helpers/vivado.xpr +++ /dev/null @@ -1,204 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Vivado Synthesis Defaults - - - - - - - - - - - Default settings for Implementation. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - default_dashboard - - - diff --git a/examples/hooks/Makefile b/examples/hooks/Makefile deleted file mode 100644 index cd77fa54..00000000 --- a/examples/hooks/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/make - -SCRIPTS = $(wildcard *.py) - -all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT);) diff --git a/examples/hooks/diamond.py b/examples/hooks/diamond.py new file mode 100644 index 00000000..ce4f3558 --- /dev/null +++ b/examples/hooks/diamond.py @@ -0,0 +1,51 @@ +"""Diamond hooks examples.""" + +from pyfpga.diamond import Diamond + +prj = Diamond() + +hooks = { + "reports": """ +prj_run Map -task MapTrace -forceOne +prj_run PAR -task PARTrace -forceOne +prj_run PAR -task IOTiming -forceOne + """, + + "netlist_simulation": """ +prj_run Map -task MapVerilogSimFile +prj_run Map -task MapVHDLSimFile -forceOne +prj_run Export -task TimingSimFileVHD -forceOne +prj_run Export -task TimingSimFileVlg -forceOne +prj_run Export -task IBIS -forceOne + """, + + "progfile_ecp5u": """ +prj_run Export -task Promgen -forceOne + """, + + "progfile_machxo2": """ +prj_run Export -task Jedecgen -forceOne + """ +} + +prj.set_part('LFXP2-5E-5TN144C') + +prj.add_param('FREQ', '50000000') +prj.add_param('SECS', '1') + +prj.add_cons('../sources/cons/brevia2/clk.lpf') +prj.add_cons('../sources/cons/brevia2/led.lpf') + +prj.add_include('../sources/vlog/include1') +prj.add_include('../sources/vlog/include2') +prj.add_vlog('../sources/vlog/*.v') + +prj.add_define('DEFINE1', '1') +prj.add_define('DEFINE2', '1') + +prj.set_top('Top') + +for hook_name, hook in hooks.items(): + prj.add_hook('postpar', hook) + +prj.make() diff --git a/examples/hooks/strategies.py b/examples/hooks/strategies.py index 89954eba..018b60eb 100644 --- a/examples/hooks/strategies.py +++ b/examples/hooks/strategies.py @@ -105,7 +105,4 @@ 'project' ) PRJ.add_hook(commands[tool][strategy], 'project') - try: - PRJ.generate(to_task='syn') - except RuntimeError: - print('ERROR:generate:{} not found'.format(tool)) + PRJ.generate(to_task='syn') diff --git a/examples/hooks/vivado-elab.py b/examples/hooks/vivado-elab.py new file mode 100644 index 00000000..f21c550f --- /dev/null +++ b/examples/hooks/vivado-elab.py @@ -0,0 +1,23 @@ +"""Vivado elaboration example.""" + +from pyfpga.vivado import Vivado + + +prj = Vivado() + +prj.set_part('xc7z010-1-clg400') + +prj.add_param('FREQ', '125000000') +prj.add_param('SECS', '1') +prj.add_define('DEFINE1', '1') +prj.add_define('DEFINE2', '1') + +prj.add_include('../sources/slog/include1') +prj.add_include('../sources/slog/include2') +prj.add_slog('../sources/slog/*.sv') + +prj.set_top('Top') + +prj.add_hook('presyn', 'synth_design -rtl -rtl_skip_mlo; exit 0') + +prj.make() diff --git a/examples/hooks/vivado.py b/examples/hooks/vivado.py new file mode 100644 index 00000000..9d239247 --- /dev/null +++ b/examples/hooks/vivado.py @@ -0,0 +1,42 @@ +"""Vivado hooks examples.""" + +from pathlib import Path +from pyfpga.vivado import Vivado + + +prj = Vivado() + +prj.set_part('xc7z010-1-clg400') + +prj.add_param('FREQ', '125000000') +prj.add_param('SECS', '1') +prj.add_define('DEFINE1', '1') +prj.add_define('DEFINE2', '1') + +prj.add_include('../sources/slog/include1') +prj.add_include('../sources/slog/include2') +prj.add_slog('../sources/slog/*.sv') + +prj.add_cons('../sources/cons/ZYBO/timing.xdc') +prj.add_cons('../sources/cons/ZYBO/clk.xdc') +prj.add_cons('../sources/cons/ZYBO/led.xdc') + +prj.set_top('Top') + +prj.add_hook('precfg', ''' +set obj [get_runs synth_1] +set_property strategy "Flow_AreaOptimized_high" $obj +set_property "steps.synth_design.args.directive" "AreaOptimized_high" $obj +set_property "steps.synth_design.args.control_set_opt_threshold" "1" $obj +set obj [get_runs impl_1] +set_property strategy "Area_Explore" $obj +set_property "steps.opt_design.args.directive" "ExploreArea" $obj +''') + +place = ['../sources/cons/ZYBO/clk.xdc', '../sources/cons/ZYBO/led.xdc'] +prj.add_hook('postcfg', f''' +set_property USED_IN_SYNTHESIS FALSE [get_files {Path(place[0]).resolve()}] +set_property USED_IN_SYNTHESIS FALSE [get_files {Path(place[1]).resolve()}] +''') + +prj.make() diff --git a/examples/ise/Makefile b/examples/ise/Makefile deleted file mode 100644 index 230d1cf7..00000000 --- a/examples/ise/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/make - -SCRIPTS = $(wildcard *.py) - -all: - python3 ise.py - python3 ise.py --board s6micro diff --git a/examples/ise/ise.py b/examples/ise/ise.py deleted file mode 100644 index 14a95ae0..00000000 --- a/examples/ise/ise.py +++ /dev/null @@ -1,52 +0,0 @@ -"""ISE example project.""" - -import argparse -import logging - -from fpga.project import Project - -logging.basicConfig() - -parser = argparse.ArgumentParser() -parser.add_argument( - '--action', choices=['generate', 'transfer', 'all'], default='generate', -) -parser.add_argument( - '--board', - choices=['nexys3', 's6micro'], - default='nexys3' -) -args = parser.parse_args() - -BOARDS = { - 'nexys3': ['XC6SLX16-3-CSG324', 'nexys3.ucf', 'nexys3.xcf'], - 's6micro': ['XC6SLX9-2-CSG324', 's6micro.ucf', 's6micro.xcf'] -} - -prj = Project('ise') -prj.set_part(BOARDS[args.board][0]) - -prj.set_outdir('../../build/ise-{}'.format(args.board)) - -prj.add_files('../../hdl/blinking.vhdl', library='examples') -prj.add_files('../../hdl/examples_pkg.vhdl', library='examples') -prj.add_files('../../hdl/top.vhdl') -prj.set_top('Top') - -prj.add_files(BOARDS[args.board][1]) -prj.add_files(BOARDS[args.board][2]) - -if args.action in ['generate', 'all']: - try: - prj.generate() - except RuntimeError: - print('ERROR:generate:ISE not found') - -if args.action in ['transfer', 'all']: - try: - prj.transfer('fpga') - # prj.transfer('detect') - # prj.transfer('unlock') - # prj.transfer('spi', 1, 'N25Q128', 4) - except RuntimeError: - print('ERROR:transfer:ISE not found') diff --git a/examples/ise/nexys3.ucf b/examples/ise/nexys3.ucf deleted file mode 100644 index 298bd77a..00000000 --- a/examples/ise/nexys3.ucf +++ /dev/null @@ -1,7 +0,0 @@ -# User Constraints File -# -# Used during the implementation to specify timing, placement and pinout -# constraints. - -NET "clk_i" LOC = "V10"; -NET "led_o" LOC = "U16"; diff --git a/examples/ise/nexys3.xcf b/examples/ise/nexys3.xcf deleted file mode 100644 index d8b19371..00000000 --- a/examples/ise/nexys3.xcf +++ /dev/null @@ -1,6 +0,0 @@ -# Xilinx Constraint File -# -# Used during the synthesis (XST) to specify timing and synthesis constraints. - -NET "clk_i" TNM_NET = "clk_i"; -TIMESPEC "TS_clk_i" = PERIOD "clk_i" 10.00 ns HIGH 50%; diff --git a/examples/ise/s6micro.ucf b/examples/ise/s6micro.ucf deleted file mode 100644 index 18a5cc0f..00000000 --- a/examples/ise/s6micro.ucf +++ /dev/null @@ -1,7 +0,0 @@ -# User Constraints File -# -# Used during the implementation to specify timing, placement and pinout -# constraints. - -NET "clk_i" LOC = "V10"; -NET "led_o" LOC = "C2"; diff --git a/examples/ise/s6micro.xcf b/examples/ise/s6micro.xcf deleted file mode 100644 index 6849968d..00000000 --- a/examples/ise/s6micro.xcf +++ /dev/null @@ -1,6 +0,0 @@ -# Xilinx Constraint File -# -# Used during the synthesis (XST) to specify timing and synthesis constraints. - -NET "clk_i" TNM_NET = "clk_i"; -TIMESPEC "TS_clk_i" = PERIOD "clk_i" 20.00 ns HIGH 50%; diff --git a/examples/libero/Makefile b/examples/libero/Makefile deleted file mode 100644 index cd77fa54..00000000 --- a/examples/libero/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/make - -SCRIPTS = $(wildcard *.py) - -all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT);) diff --git a/examples/libero/libero.py b/examples/libero/libero.py deleted file mode 100644 index 7f0b39bb..00000000 --- a/examples/libero/libero.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Libero example project.""" - -import argparse -import logging - -from fpga.project import Project - -logging.basicConfig() - -parser = argparse.ArgumentParser() -parser.add_argument( - '--action', choices=['generate', 'transfer', 'all'], default='generate', -) -args = parser.parse_args() - -prj = Project('libero') -prj.set_part('m2s010-1-tq144') - -prj.set_outdir('../../build/libero') - -prj.add_files('../../hdl/blinking.vhdl', library='examples') -prj.add_files('../../hdl/examples_pkg.vhdl', library='examples') -prj.add_files('../../hdl/top.vhdl') -prj.set_top('Top') -prj.add_files('mkr.pdc') -prj.add_files('mkr.sdc') - -if args.action in ['generate', 'all']: - try: - prj.generate() - except RuntimeError: - print('ERROR:generate:Libero not found') - -if args.action in ['transfer', 'all']: - print('ERROR:transfer:Not yet implemented') diff --git a/examples/libero/mkr.pdc b/examples/libero/mkr.pdc deleted file mode 100644 index 78acfe06..00000000 --- a/examples/libero/mkr.pdc +++ /dev/null @@ -1,4 +0,0 @@ -# Physical Design Constraints - -set_io clk_i -DIRECTION INPUT -pinname 23 -fixed yes -set_io led_o -DIRECTION OUTPUT -pinname 117 -fixed yes diff --git a/examples/libero/mkr.sdc b/examples/libero/mkr.sdc deleted file mode 100644 index db18e27b..00000000 --- a/examples/libero/mkr.sdc +++ /dev/null @@ -1,6 +0,0 @@ -# Synopsys Design Constraint -# -# Is a Tcl-based format used by Synopsys tools to specify the design intent -# and timing constraints. - -create_clock -period 20 [get_ports clk_i] diff --git a/examples/misc/Makefile b/examples/misc/Makefile deleted file mode 100644 index cd77fa54..00000000 --- a/examples/misc/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/make - -SCRIPTS = $(wildcard *.py) - -all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT);) diff --git a/examples/misc/capture.py b/examples/misc/capture.py deleted file mode 100644 index 8caa5a56..00000000 --- a/examples/misc/capture.py +++ /dev/null @@ -1,29 +0,0 @@ -"""PyFPGA capture example. - -Example about how to capture the execution messages to be post-processed -or saved into a file. -""" - -import logging - -from fpga.project import Project - -logging.basicConfig() - -PRJ = Project('ise', 'capture') -PRJ.set_outdir('../../build/capture') - -PRJ.add_files('../../hdl/*.vhdl', library='examples') -PRJ.set_top('Top') - -try: - output = PRJ.generate(to_task='syn', capture=True) - print(output) -except RuntimeError: - print('ERROR:generate:ISE not found') - -try: - output = PRJ.transfer(devtype='detect', capture=True) - print(output) -except RuntimeError: - print('ERROR:transfer:ISE not found') diff --git a/examples/yosys/ise.py b/examples/misc/yosys-ise.py similarity index 76% rename from examples/yosys/ise.py rename to examples/misc/yosys-ise.py index b48d78b5..6a900be9 100644 --- a/examples/yosys/ise.py +++ b/examples/misc/yosys-ise.py @@ -21,8 +21,8 @@ prj.set_part('XC6SLX9-2-CSG324') if args.lang == 'verilog': - prj.add_path('../../hdl/headers1') - prj.add_path('../../hdl/headers2') + prj.add_vlog_include('../../hdl/headers1') + prj.add_vlog_include('../../hdl/headers2') prj.add_files('../../hdl/blinking.v') prj.add_files('../../hdl/top.v') else: # args.lang == 'vhdl' @@ -34,13 +34,7 @@ prj.set_top('Top') if args.action in ['generate', 'all']: - try: - prj.generate() - except RuntimeError: - print('ERROR:generate:Docker or ISE not found') + prj.generate() if args.action in ['transfer', 'all']: - try: - prj.transfer() - except RuntimeError: - print('ERROR:transfer:ISE not found') + prj.transfer() diff --git a/examples/yosys/vivado.py b/examples/misc/yosys-vivado.py similarity index 75% rename from examples/yosys/vivado.py rename to examples/misc/yosys-vivado.py index 1a5ea22f..4dfae60a 100644 --- a/examples/yosys/vivado.py +++ b/examples/misc/yosys-vivado.py @@ -21,8 +21,8 @@ prj.set_part('xc7z010-1-clg400') if args.lang == 'verilog': - prj.add_path('../../hdl/headers1') - prj.add_path('../../hdl/headers2') + prj.add_vlog_include('../../hdl/headers1') + prj.add_vlog_include('../../hdl/headers2') prj.add_files('../../hdl/blinking.v') prj.add_files('../../hdl/top.v') else: # args.lang == 'vhdl' @@ -34,13 +34,7 @@ prj.set_top('Top') if args.action in ['generate', 'all']: - try: - prj.generate() - except RuntimeError: - print('ERROR:generate:Docker or Vivado not found') + prj.generate() if args.action in ['transfer', 'all']: - try: - prj.transfer() - except RuntimeError: - print('ERROR:transfer:Vivado not found') + prj.transfer() diff --git a/examples/multi/Makefile b/examples/multi/Makefile deleted file mode 100644 index cd77fa54..00000000 --- a/examples/multi/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/make - -SCRIPTS = $(wildcard *.py) - -all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT);) diff --git a/examples/multi/memory.py b/examples/multi/memory.py deleted file mode 100644 index fe67ee57..00000000 --- a/examples/multi/memory.py +++ /dev/null @@ -1,27 +0,0 @@ -"""PyFPGA example about Memory Content Files inclusion. - -This example is mainly used as a test of this feature through the different -tools. -""" - -import logging - -from fpga.project import Project, TOOLS - -logging.basicConfig() - -for hdl in ['vhdl', 'verilog']: - for tool in TOOLS: - if tool == 'ghdl' and hdl == 'verilog': - continue - PRJ = Project(tool) - PRJ.set_outdir('../../build/multi/memory/%s/%s' % (tool, hdl)) - if hdl == 'vhdl': - PRJ.add_files('../../hdl/ram.vhdl') - else: - PRJ.add_files('../../hdl/ram.v') - PRJ.set_top('ram') - try: - PRJ.generate(to_task='syn') - except RuntimeError: - print('ERROR:generate:{} not found'.format(tool)) diff --git a/examples/multi/parameters.py b/examples/multi/parameters.py deleted file mode 100644 index 45d73ad9..00000000 --- a/examples/multi/parameters.py +++ /dev/null @@ -1,47 +0,0 @@ -"""PyFPGA Multi Vendor example where parameters are changed. - -The main idea of a multi-vendor project is to implements the same HDL code -with different tools, to make comparisons. The project name is not important -and the default devices are used. In this example, VHDL and Verilog -files are synthesized changing the value of its generics/parameters. -""" - -import logging - -from fpga.project import Project, TOOLS - -logging.basicConfig() - -for hdl in ['vhdl', 'verilog']: - for tool in TOOLS: - if tool == 'ghdl': - continue - if hdl == 'vhdl': - if tool in ['openflow', 'yosys', 'yosys-ise', 'yosys-vivado']: - continue - PRJ = Project(tool) - PRJ.set_param('FREQ', '50000000') - PRJ.set_param('SECS', '2') - PRJ.set_outdir('../../build/multi/params/%s/%s' % (tool, hdl)) - if hdl == 'vhdl': - PRJ.add_files('../../hdl/blinking.vhdl') - else: - PRJ.add_path('../../hdl/headers1') - PRJ.add_path('../../hdl/headers2') - PRJ.add_files('../../hdl/blinking.v') - PRJ.set_top('Blinking') - # PRJ.set_param('INT', '15') - # PRJ.set_param('REA', '1.5') - # PRJ.set_param('LOG', "'1'") - # PRJ.set_param('VEC', '"10101010"') - # PRJ.set_param('STR', '"WXYZ"') - # PRJ.set_outdir('../../build/multi/params/%s/%s' % (tool, hdl)) - # if hdl == 'vhdl': - # PRJ.add_files('../../hdl/fakes/generics.vhdl') - # else: - # PRJ.add_files('../../hdl/fakes/parameters.v') - # PRJ.set_top('Params') - try: - PRJ.generate(to_task='syn') - except RuntimeError: - print('ERROR:generate:{} not found'.format(tool)) diff --git a/examples/multi/projects.py b/examples/multi/projects.py deleted file mode 100644 index 97cc83ec..00000000 --- a/examples/multi/projects.py +++ /dev/null @@ -1,63 +0,0 @@ -"""PyFPGA Multi Project example. - -The main idea of a multi-project is to manage more than one project with the -same script. -""" - -import logging - -from fpga.project import Project - -logging.basicConfig() - -PROJECTS = { - 'prj1': Project( - 'vivado', - 'vivado-prj', - { - 'outdir': '../../build/multi/projects/vivado', - 'part': 'xc7k70t-3-fbg484', - 'vhdl': [ - ['../../hdl/blinking.vhdl', 'examples'], - ['../../hdl/examples_pkg.vhdl', 'examples'], - '../../hdl/top.vhdl' - ], - 'top': 'Top' - } - ), - 'prj2': Project( - 'ise', - 'ise-prj', - { - 'outdir': '../../build/multi/projects/ise', - 'part': 'xc6slx9-2-csg324', - 'vhdl': [ - '../../hdl/blinking.vhdl' - ], - 'top': 'Blinking' - } - ), - 'prj3': Project( - 'quartus', - 'qurtus-prj', - { - 'outdir': '../../build/multi/projects/quartus', - 'part': '5CEBA2F17A7', - 'paths': [ - '../../hdl/headers1', - '../../hdl/headers2' - ], - 'verilog': [ - '../../hdl/blinking.v', - '../../hdl/top.v' - ], - 'top': 'Top' - } - ) -} - -for prj in PROJECTS: - try: - PROJECTS[prj].generate('syn') - except RuntimeError: - print('ERROR:generate:tool not found') diff --git a/examples/multi/verilog.py b/examples/multi/verilog.py deleted file mode 100644 index bfce523e..00000000 --- a/examples/multi/verilog.py +++ /dev/null @@ -1,27 +0,0 @@ -"""PyFPGA Multi Vendor Verilog example. - -The main idea of a multi-vendor project is to implements the same HDL code -with different tools, to make comparisons. The project name is not important -and the default devices could be used. -""" - -import logging - -from fpga.project import Project, TOOLS - -logging.basicConfig() - -for tool in TOOLS: - if tool == 'ghdl': - continue - PRJ = Project(tool) - PRJ.set_outdir('../../build/multi/verilog/%s' % tool) - PRJ.add_path('../../hdl/headers1') - PRJ.add_path('../../hdl/headers2') - PRJ.add_files('../../hdl/blinking.v') - PRJ.add_files('../../hdl/top.v') - PRJ.set_top('Top') - try: - PRJ.generate(to_task='syn') - except RuntimeError: - print('ERROR:generate:{} not found'.format(tool)) diff --git a/examples/multi/vhdl.py b/examples/multi/vhdl.py deleted file mode 100644 index b2563e41..00000000 --- a/examples/multi/vhdl.py +++ /dev/null @@ -1,24 +0,0 @@ -"""PyFPGA Multi Vendor VHDL example. - -The main idea of a multi-vendor project is to implements the same HDL code -with different tools, to make comparisons. The project name is not important -and the default devices are used. -""" - -import logging - -from fpga.project import Project, TOOLS - -logging.basicConfig() - -for tool in TOOLS: - PRJ = Project(tool) - PRJ.set_outdir('../../build/multi/vhdl/%s' % tool) - PRJ.add_files('../../hdl/blinking.vhdl', library='examples') - PRJ.add_files('../../hdl/examples_pkg.vhdl', library='examples') - PRJ.add_files('../../hdl/top.vhdl') - PRJ.set_top('Top') - try: - PRJ.generate(to_task='syn') - except RuntimeError: - print('ERROR:generate:{} not found'.format(tool)) diff --git a/examples/openflow/Makefile b/examples/openflow/Makefile deleted file mode 100644 index 05df0f94..00000000 --- a/examples/openflow/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/make - -all: - python3 icestorm.py --lang verilog - python3 icestorm.py --lang vhdl - python3 prjtrellis.py --board ecp5evn - python3 prjtrellis.py --lang vhdl diff --git a/examples/openflow/ecp5evn.lpf b/examples/openflow/ecp5evn.lpf deleted file mode 100644 index 1b0195e3..00000000 --- a/examples/openflow/ecp5evn.lpf +++ /dev/null @@ -1,5 +0,0 @@ -LOCATE COMP "clk_i" SITE "A10"; -IOBUF PORT "clk_i" IO_TYPE=LVCMOS33; - -LOCATE COMP "led_o" SITE "A13"; -IOBUF PORT "led_o" IO_TYPE=LVCMOS25; diff --git a/examples/openflow/icestorm.py b/examples/openflow/icestorm.py deleted file mode 100644 index 05d5f96b..00000000 --- a/examples/openflow/icestorm.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Icestorm example project.""" - -import argparse -import logging - -from fpga.project import Project - -logging.basicConfig() - -parser = argparse.ArgumentParser() -parser.add_argument( - '--action', choices=['generate', 'transfer', 'all'], default='generate' -) -parser.add_argument( - '--lang', choices=['verilog', 'vhdl'], default='verilog' -) -parser.add_argument( - '--board', - choices=['icestick', 'edu-ciaa-fpga'], - default='icestick' -) -args = parser.parse_args() - -BOARDS = { - 'icestick': ['hx1k-tq144', 'icestick.pcf'], - 'edu-ciaa-fpga': ['hx4k-tq144', 'edu-ciaa-fpga.pcf'] -} - -prj = Project('openflow') -prj.set_outdir('../../build/icestorm-{}-{}'.format(args.board, args.lang)) -prj.set_part(BOARDS[args.board][0]) - -if args.lang == 'verilog': - prj.add_path('../../hdl/headers1') - prj.add_path('../../hdl/headers2') - prj.add_files('../../hdl/blinking.v') - prj.add_files('../../hdl/top.v') -else: # args.lang == 'vhdl' - prj.add_files('../../hdl/blinking.vhdl', library='examples') - prj.add_files('../../hdl/examples_pkg.vhdl', library='examples') - prj.add_files('../../hdl/top.vhdl') - -prj.add_files(BOARDS[args.board][1]) -prj.set_top('Top') - -if args.action in ['generate', 'all']: - try: - prj.generate() - except RuntimeError: - print('ERROR:generate:Docker not found') - -if args.action in ['transfer', 'all']: - try: - prj.transfer() - except RuntimeError: - print('ERROR:transfer:Docker not found') diff --git a/examples/openflow/orangecrab_r0.2.lpf b/examples/openflow/orangecrab_r0.2.lpf deleted file mode 100644 index 573a3e2a..00000000 --- a/examples/openflow/orangecrab_r0.2.lpf +++ /dev/null @@ -1,5 +0,0 @@ -LOCATE COMP "clk_i" SITE "A9"; -IOBUF PORT "clk_i" IO_TYPE=LVCMOS33; - -LOCATE COMP "led_o" SITE "K4"; -IOBUF PORT "led_o" IO_TYPE=LVCMOS33; diff --git a/examples/openflow/prjtrellis.py b/examples/openflow/prjtrellis.py deleted file mode 100644 index 59eb8311..00000000 --- a/examples/openflow/prjtrellis.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Trellis example project.""" - -import argparse -import logging - -from fpga.project import Project - -logging.basicConfig() - -parser = argparse.ArgumentParser() -parser.add_argument( - '--action', choices=['generate', 'transfer', 'all'], default='generate' -) -parser.add_argument( - '--lang', choices=['verilog', 'vhdl'], default='verilog' -) -parser.add_argument( - '--board', - choices=['orangecrab', 'ecp5evn'], - default='orangecrab' -) -args = parser.parse_args() - -BOARDS = { - 'orangecrab': ['25k-CSFBGA285', 'orangecrab_r0.2.lpf'], - 'ecp5evn': ['um5g-85k-CABGA381', 'ecp5evn.lpf'] -} - -prj = Project('openflow') -prj.set_outdir('../../build/prjtrellis-{}-{}'.format(args.board, args.lang)) -prj.set_part(BOARDS[args.board][0]) - -if args.lang == 'verilog': - prj.add_path('../../hdl/headers1') - prj.add_path('../../hdl/headers2') - prj.add_files('../../hdl/blinking.v') - prj.add_files('../../hdl/top.v') -else: # args.lang == 'vhdl' - prj.add_files('../../hdl/blinking.vhdl', library='examples') - prj.add_files('../../hdl/examples_pkg.vhdl', library='examples') - prj.add_files('../../hdl/top.vhdl') - -prj.add_files(BOARDS[args.board][1]) -prj.set_top('Top') - -if args.action in ['generate', 'all']: - try: - prj.generate() - except RuntimeError: - print('ERROR:generate:Docker not found') - -if args.action in ['transfer', 'all']: - try: - prj.transfer() - except RuntimeError: - print('ERROR:transfer:Docker not found') diff --git a/examples/projects/diamond.py b/examples/projects/diamond.py new file mode 100644 index 00000000..464be7ff --- /dev/null +++ b/examples/projects/diamond.py @@ -0,0 +1,56 @@ +"""Diamond examples.""" + +import argparse + +from pyfpga.diamond import Diamond + + +parser = argparse.ArgumentParser() +parser.add_argument( + '--board', choices=['brevia2'], default='brevia2' +) +parser.add_argument( + '--source', choices=['vlog', 'vhdl', 'slog'], default='vlog' +) +parser.add_argument( + '--action', choices=['make', 'prog', 'all'], default='make' +) +parser.add_argument( + '--notool', action='store_true' +) +args = parser.parse_args() + +prj = Diamond(odir=f'results/diamond/{args.source}/{args.board}') + +if args.board == 'brevia2': + prj.set_part('LFXP2-5E-5TN144C') + prj.add_param('FREQ', '50000000') + prj.add_cons('../sources/cons/brevia2/clk.lpf') + prj.add_cons('../sources/cons/brevia2/led.lpf') +prj.add_param('SECS', '1') + +if args.source == 'vhdl': + prj.add_vhdl('../sources/vhdl/*.vhdl', 'blink_lib') + prj.add_vhdl('../sources/vhdl/top.vhdl') +if args.source == 'vlog': + prj.add_include('../sources/vlog/include1') + prj.add_include('../sources/vlog/include2') + prj.add_vlog('../sources/vlog/*.v') +if args.source == 'slog': + prj.add_include('../sources/slog/include1') + prj.add_include('../sources/slog/include2') + prj.add_slog('../sources/slog/*.sv') +if args.source in ['vlog', 'slog']: + prj.add_define('DEFINE1', '1') + prj.add_define('DEFINE2', '1') + +prj.set_top('Top') + +try: + if args.action in ['make', 'all']: + prj.make() + if args.action in ['prog', 'all']: + prj.prog() +except RuntimeError: + if not args.notool: + raise diff --git a/examples/projects/ise.py b/examples/projects/ise.py new file mode 100644 index 00000000..1955d34d --- /dev/null +++ b/examples/projects/ise.py @@ -0,0 +1,58 @@ +"""ISE examples.""" + +import argparse + +from pyfpga.ise import Ise + + +parser = argparse.ArgumentParser() +parser.add_argument( + '--board', choices=['s6micro', 'nexys3'], default='s6micro' +) +parser.add_argument( + '--source', choices=['vlog', 'vhdl'], default='vlog' +) +parser.add_argument( + '--action', choices=['make', 'prog', 'all'], default='make' +) +parser.add_argument( + '--notool', action='store_true' +) +args = parser.parse_args() + +prj = Ise(odir=f'results/ise/{args.source}/{args.board}') + +if args.board == 's6micro': + prj.set_part('xc6slx9-2-csg324') + prj.add_param('FREQ', '125000000') + prj.add_cons('../sources/cons/s6micro/timing.xcf') + prj.add_cons('../sources/cons/s6micro/clk.ucf') + prj.add_cons('../sources/cons/s6micro/led.ucf') +if args.board == 'nexys3': + prj.set_part('xc6slx16-3-csg32') + prj.add_param('FREQ', '100000000') + prj.add_cons('../sources/cons/nexys3/timing.xcf') + prj.add_cons('../sources/cons/nexys3/clk.ucf') + prj.add_cons('../sources/cons/nexys3/led.ucf') +prj.add_param('SECS', '1') + +if args.source == 'vhdl': + prj.add_vhdl('../sources/vhdl/*.vhdl', 'blink_lib') + prj.add_vhdl('../sources/vhdl/top.vhdl') +if args.source == 'vlog': + prj.add_include('../sources/vlog/include1') + prj.add_include('../sources/vlog/include2') + prj.add_vlog('../sources/vlog/*.v') + prj.add_define('DEFINE1', '1') + prj.add_define('DEFINE2', '1') + +prj.set_top('Top') + +try: + if args.action in ['make', 'all']: + prj.make() + if args.action in ['prog', 'all']: + prj.prog() +except RuntimeError: + if not args.notool: + raise diff --git a/examples/projects/libero.py b/examples/projects/libero.py new file mode 100644 index 00000000..3498a6e3 --- /dev/null +++ b/examples/projects/libero.py @@ -0,0 +1,63 @@ +"""Libero examples.""" + +import argparse + +from pyfpga.libero import Libero + +parser = argparse.ArgumentParser() +parser.add_argument( + '--board', choices=['mpfs-disco-kit', 'maker'], default='mpfs-disco-kit' +) +parser.add_argument( + '--source', choices=['vlog', 'vhdl', 'slog'], default='vlog' +) +parser.add_argument( + '--action', choices=['make', 'prog', 'all'], default='make' +) +parser.add_argument( + '--notool', action='store_true' +) +args = parser.parse_args() + +prj = Libero(odir=f'results/libero/{args.source}/{args.board}') + + +if args.board == 'mpfs-disco-kit': + prj.set_part('MPFS095T-1-FCSG325E') + prj.add_param('FREQ', '50000000') + prj.add_cons('../sources/cons/mpfs-disco-kit/timing.sdc') + prj.add_cons('../sources/cons/mpfs-disco-kit/clk.pdc') + prj.add_cons('../sources/cons/mpfs-disco-kit/led.pdc') +if args.board == 'maker': + prj.set_part('m2s010-1-tq144') + prj.add_param('FREQ', '125000000') + prj.add_cons('../sources/cons/maker/timing.sdc') + prj.add_cons('../sources/cons/maker/clk.pdc') + prj.add_cons('../sources/cons/maker/led.pdc') +prj.add_param('SECS', '1') + +if args.source == 'vhdl': + prj.add_vhdl('../sources/vhdl/*.vhdl', 'blink_lib') + prj.add_vhdl('../sources/vhdl/top.vhdl') +if args.source == 'vlog': + prj.add_include('../sources/vlog/include1') + prj.add_include('../sources/vlog/include2') + prj.add_vlog('../sources/vlog/*.v') +if args.source == 'slog': + prj.add_include('../sources/slog/include1') + prj.add_include('../sources/slog/include2') + prj.add_vlog('../sources/slog/*.sv') +if args.source in ['vlog', 'slog']: + prj.add_define('DEFINE1', '1') + prj.add_define('DEFINE2', '1') + +prj.set_top('Top') + +try: + if args.action in ['make', 'all']: + prj.make() + if args.action in ['prog', 'all']: + prj.prog() +except RuntimeError: + if not args.notool: + raise diff --git a/examples/projects/openflow.py b/examples/projects/openflow.py new file mode 100644 index 00000000..98e2e999 --- /dev/null +++ b/examples/projects/openflow.py @@ -0,0 +1,72 @@ +"""Openflow examples.""" + +import argparse + +from pyfpga.openflow import Openflow + + +parser = argparse.ArgumentParser() +parser.add_argument( + '--board', choices=['icestick', 'edu-ciaa', 'orangecrab', 'ecp5evn'], + default='icestick' +) +parser.add_argument( + '--source', choices=['vlog', 'vhdl', 'slog'], default='vlog' +) +parser.add_argument( + '--action', choices=['make', 'prog', 'all'], default='make' +) +parser.add_argument( + '--notool', action='store_true' +) +args = parser.parse_args() + +prj = Openflow(odir=f'results/openflow/{args.source}/{args.board}') + +if args.board == 'icestick': + prj.set_part('hx1k-tq144') + prj.add_param('FREQ', '100000000') + prj.add_cons('../sources/cons/icestick/clk.pcf') + prj.add_cons('../sources/cons/icestick/led.pcf') +if args.board == 'edu-ciaa': + prj.set_part('hx1k-tq144') + prj.add_param('FREQ', '100000000') + prj.add_cons('../sources/cons/edu-ciaa/clk.pcf') + prj.add_cons('../sources/cons/edu-ciaa/led.pcf') +if args.board == 'orangecrab': + prj.set_part('25k-CSFBGA285') + prj.add_param('FREQ', '100000000') + prj.add_cons('../sources/cons/orangecrab/clk.lpf') + prj.add_cons('../sources/cons/orangecrab/led.lpf') +if args.board == 'ecp5evn': + prj.set_part('um5g-85k-CABGA381') + prj.add_param('FREQ', '100000000') + prj.add_cons('../sources/cons/ecp5evn/clk.lpf') + prj.add_cons('../sources/cons/ecp5evn/led.lpf') +prj.add_param('SECS', '1') + +if args.source == 'vhdl': + prj.add_vhdl('../sources/vhdl/*.vhdl', 'blink_lib') + prj.add_vhdl('../sources/vhdl/top.vhdl') +if args.source == 'vlog': + prj.add_include('../sources/vlog/include1') + prj.add_include('../sources/vlog/include2') + prj.add_vlog('../sources/vlog/*.v') +if args.source == 'slog': + prj.add_include('../sources/slog/include1') + prj.add_include('../sources/slog/include2') + prj.add_slog('../sources/slog/*.sv') +if args.source in ['vlog', 'slog']: + prj.add_define('DEFINE1', '1') + prj.add_define('DEFINE2', '1') + +prj.set_top('Top') + +try: + if args.action in ['make', 'all']: + prj.make() + if args.action in ['prog', 'all']: + prj.prog() +except RuntimeError: + if not args.notool: + raise diff --git a/examples/projects/quartus.py b/examples/projects/quartus.py new file mode 100644 index 00000000..258fa8e6 --- /dev/null +++ b/examples/projects/quartus.py @@ -0,0 +1,57 @@ +"""Quartus examples.""" + +import argparse + +from pyfpga.quartus import Quartus + + +parser = argparse.ArgumentParser() +parser.add_argument( + '--board', choices=['de10nano'], default='de10nano' +) +parser.add_argument( + '--source', choices=['vlog', 'vhdl', 'slog'], default='vlog' +) +parser.add_argument( + '--action', choices=['make', 'prog', 'all'], default='make' +) +parser.add_argument( + '--notool', action='store_true' +) +args = parser.parse_args() + +prj = Quartus(odir=f'results/quartus/{args.source}/{args.board}') + +if args.board == 'de10nano': + prj.set_part('5CSEBA6U23I7') + prj.add_param('FREQ', '125000000') + prj.add_cons('../sources/cons/de10nano/timing.sdc') + prj.add_cons('../sources/cons/de10nano/clk.tcl') + prj.add_cons('../sources/cons/de10nano/led.tcl') +prj.add_param('SECS', '1') + +if args.source == 'vhdl': + prj.add_vhdl('../sources/vhdl/*.vhdl', 'blink_lib') + prj.add_vhdl('../sources/vhdl/top.vhdl') +if args.source == 'vlog': + prj.add_include('../sources/vlog/include1') + prj.add_include('../sources/vlog/include2') + prj.add_vlog('../sources/vlog/*.v') +if args.source == 'slog': + prj.add_include('../sources/slog/include1') + prj.add_include('../sources/slog/include2') + prj.add_slog('../sources/slog/*.sv') +if args.source in ['vlog', 'slog']: + prj.add_define('DEFINE1', '1') + prj.add_define('DEFINE2', '1') + +prj.set_top('Top') + +try: + if args.action in ['make', 'all']: + prj.make() + if args.action in ['prog', 'all']: + prj.prog() +except RuntimeError: + if not args.notool: + raise diff --git a/examples/projects/regress.sh b/examples/projects/regress.sh new file mode 100644 index 00000000..a260b52e --- /dev/null +++ b/examples/projects/regress.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +set -e + +declare -A TOOLS + +TOOLS["diamond"]="brevia2" +TOOLS["ise"]="s6micro nexys3" +TOOLS["libero"]="maker" +TOOLS["openflow"]="icestick edu-ciaa orangecrab ecp5evn" +TOOLS["quartus"]="de10nano" +TOOLS["vivado"]="zybo arty" + +SOURCES=("vlog" "vhdl" "slog") + +SPECIFIED_TOOL="" +NOTOOL=false +while [[ "$#" -gt 0 ]]; do + case $1 in + --tool) + SPECIFIED_TOOL="$2" + shift 2 + ;; + --notool) + NOTOOL=true + shift + ;; + *) + echo "Invalid option: $1" + exit 1 + ;; + esac +done + +for TOOL in "${!TOOLS[@]}"; do + if [[ -n "$SPECIFIED_TOOL" && "$TOOL" != "$SPECIFIED_TOOL" ]]; then + continue + fi + BOARDS=${TOOLS[$TOOL]} + for BOARD in $BOARDS; do + for SOURCE in "${SOURCES[@]}"; do + if [[ "$TOOL" == "ise" && "$SOURCE" == "slog" ]]; then + continue + fi + echo "> $TOOL - $BOARD - $SOURCE" + if [[ "$NOTOOL" == true ]]; then + python3 $TOOL.py --board $BOARD --source $SOURCE --notool + else + python3 $TOOL.py --board $BOARD --source $SOURCE + fi + done + done +done diff --git a/examples/projects/vivado.py b/examples/projects/vivado.py new file mode 100644 index 00000000..33fba3f3 --- /dev/null +++ b/examples/projects/vivado.py @@ -0,0 +1,63 @@ +"""Vivado examples.""" + +import argparse + +from pyfpga.vivado import Vivado + + +parser = argparse.ArgumentParser() +parser.add_argument( + '--board', choices=['zybo', 'arty'], default='zybo' +) +parser.add_argument( + '--source', choices=['vlog', 'vhdl', 'slog'], default='vlog' +) +parser.add_argument( + '--action', choices=['make', 'prog', 'all'], default='make' +) +parser.add_argument( + '--notool', action='store_true' +) +args = parser.parse_args() + +prj = Vivado(odir=f'results/vivado/{args.source}/{args.board}') + +if args.board == 'zybo': + prj.set_part('xc7z010-1-clg400') + prj.add_param('FREQ', '125000000') + prj.add_cons('../sources/cons/ZYBO/timing.xdc') + prj.add_cons('../sources/cons/ZYBO/clk.xdc') + prj.add_cons('../sources/cons/ZYBO/led.xdc') +if args.board == 'arty': + prj.set_part('xc7a35ticsg324-1L') + prj.add_param('FREQ', '100000000') + prj.add_cons('../sources/cons/arty_a7_35t/timing.xdc') + prj.add_cons('../sources/cons/arty_a7_35t/clk.xdc') + prj.add_cons('../sources/cons/arty_a7_35t/led.xdc') +prj.add_param('SECS', '1') + +if args.source == 'vhdl': + prj.add_vhdl('../sources/vhdl/*.vhdl', 'blink_lib') + prj.add_vhdl('../sources/vhdl/top.vhdl') +if args.source == 'vlog': + prj.add_include('../sources/vlog/include1') + prj.add_include('../sources/vlog/include2') + prj.add_vlog('../sources/vlog/*.v') +if args.source == 'slog': + prj.add_include('../sources/slog/include1') + prj.add_include('../sources/slog/include2') + prj.add_slog('../sources/slog/*.sv') +if args.source in ['vlog', 'slog']: + prj.add_define('DEFINE1', '1') + prj.add_define('DEFINE2', '1') + +prj.set_top('Top') + +try: + if args.action in ['make', 'all']: + prj.make() + if args.action in ['prog', 'all']: + prj.prog() +except RuntimeError: + if not args.notool: + raise diff --git a/examples/quartus/Makefile b/examples/quartus/Makefile deleted file mode 100644 index cd77fa54..00000000 --- a/examples/quartus/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/make - -SCRIPTS = $(wildcard *.py) - -all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT);) diff --git a/examples/quartus/de10nano.sdc b/examples/quartus/de10nano.sdc deleted file mode 100644 index db18e27b..00000000 --- a/examples/quartus/de10nano.sdc +++ /dev/null @@ -1,6 +0,0 @@ -# Synopsys Design Constraint -# -# Is a Tcl-based format used by Synopsys tools to specify the design intent -# and timing constraints. - -create_clock -period 20 [get_ports clk_i] diff --git a/examples/quartus/quartus.py b/examples/quartus/quartus.py deleted file mode 100644 index 809036b0..00000000 --- a/examples/quartus/quartus.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Quartus example project.""" - -import argparse -import logging - -from fpga.project import Project - -logging.basicConfig() - -parser = argparse.ArgumentParser() -parser.add_argument( - '--action', choices=['generate', 'transfer', 'all'], default='generate', -) -args = parser.parse_args() - -prj = Project('quartus') -prj.set_part('5CSEBA6U23I7') - -prj.set_outdir('../../build/quartus') - -prj.add_files('../../hdl/blinking.vhdl', library='examples') -prj.add_files('../../hdl/examples_pkg.vhdl', library='examples') -prj.add_files('../../hdl/top.vhdl') -prj.set_top('Top') -prj.add_files('de10nano.sdc') -prj.add_files('de10nano.tcl') - -if args.action in ['generate', 'all']: - try: - prj.generate() - except RuntimeError: - print('ERROR:generate:Quartus not found') - -if args.action in ['transfer', 'all']: - try: - prj.transfer('fpga', 2) - except RuntimeError: - print('ERROR:transfer:Quartus not found') diff --git a/examples/sources/cons/ZYBO/clk.xdc b/examples/sources/cons/ZYBO/clk.xdc new file mode 100644 index 00000000..877602c7 --- /dev/null +++ b/examples/sources/cons/ZYBO/clk.xdc @@ -0,0 +1,2 @@ +set_property PACKAGE_PIN L16 [get_ports clk_i] +set_property IOSTANDARD LVCMOS33 [get_ports clk_i] diff --git a/examples/sources/cons/ZYBO/led.xdc b/examples/sources/cons/ZYBO/led.xdc new file mode 100644 index 00000000..8106c5cc --- /dev/null +++ b/examples/sources/cons/ZYBO/led.xdc @@ -0,0 +1,2 @@ +set_property PACKAGE_PIN M14 [get_ports led_o] +set_property IOSTANDARD LVCMOS33 [get_ports led_o] diff --git a/examples/sources/cons/ZYBO/timing.xdc b/examples/sources/cons/ZYBO/timing.xdc new file mode 100644 index 00000000..be3d63fc --- /dev/null +++ b/examples/sources/cons/ZYBO/timing.xdc @@ -0,0 +1 @@ +create_clock -name clk_i -period 8 [get_ports clk_i] diff --git a/examples/sources/cons/arty_a7_35t/clk.xdc b/examples/sources/cons/arty_a7_35t/clk.xdc new file mode 100644 index 00000000..ee01dcb3 --- /dev/null +++ b/examples/sources/cons/arty_a7_35t/clk.xdc @@ -0,0 +1,2 @@ +set_property PACKAGE_PIN E3 [get_ports clk_i] +set_property IOSTANDARD LVCMOS33 [get_ports clk_i] diff --git a/examples/sources/cons/arty_a7_35t/led.xdc b/examples/sources/cons/arty_a7_35t/led.xdc new file mode 100644 index 00000000..bf6d2304 --- /dev/null +++ b/examples/sources/cons/arty_a7_35t/led.xdc @@ -0,0 +1,2 @@ +set_property PACKAGE_PIN H5 [get_ports led_o] +set_property IOSTANDARD LVCMOS33 [get_ports led_o] diff --git a/examples/sources/cons/arty_a7_35t/timing.xdc b/examples/sources/cons/arty_a7_35t/timing.xdc new file mode 100644 index 00000000..4bfaa72f --- /dev/null +++ b/examples/sources/cons/arty_a7_35t/timing.xdc @@ -0,0 +1 @@ +create_clock -name clk_i -period 10.0 [get_ports clk_i] diff --git a/examples/sources/cons/brevia2/clk.lpf b/examples/sources/cons/brevia2/clk.lpf new file mode 100644 index 00000000..c0d5f789 --- /dev/null +++ b/examples/sources/cons/brevia2/clk.lpf @@ -0,0 +1,3 @@ +BLOCK RESETPATHS ; +BLOCK ASYNCPATHS ; +FREQUENCY NET "clk_i_c" 50.000000 MHz ; diff --git a/examples/sources/cons/brevia2/led.lpf b/examples/sources/cons/brevia2/led.lpf new file mode 100644 index 00000000..9933563f --- /dev/null +++ b/examples/sources/cons/brevia2/led.lpf @@ -0,0 +1,4 @@ +LOCATE COMP "clk_i" SITE "21" ; +IOBUF PORT "clk_i" IO_TYPE=LVCMOS33 ; +LOCATE COMP "led_o" SITE "37" ; +IOBUF PORT "led_o" IO_TYPE=LVCMOS33 ; diff --git a/examples/quartus/de10nano.tcl b/examples/sources/cons/de10nano/clk.tcl similarity index 50% rename from examples/quartus/de10nano.tcl rename to examples/sources/cons/de10nano/clk.tcl index 19494e68..2d72bdd4 100644 --- a/examples/quartus/de10nano.tcl +++ b/examples/sources/cons/de10nano/clk.tcl @@ -1,2 +1 @@ set_location_assignment PIN_V11 -to clk_i -set_location_assignment PIN_W15 -to led_o diff --git a/examples/sources/cons/de10nano/led.tcl b/examples/sources/cons/de10nano/led.tcl new file mode 100644 index 00000000..4159ac36 --- /dev/null +++ b/examples/sources/cons/de10nano/led.tcl @@ -0,0 +1 @@ +set_location_assignment PIN_W15 -to led_o diff --git a/examples/sources/cons/de10nano/timing.sdc b/examples/sources/cons/de10nano/timing.sdc new file mode 100644 index 00000000..00363616 --- /dev/null +++ b/examples/sources/cons/de10nano/timing.sdc @@ -0,0 +1 @@ +create_clock -period 20 [get_ports clk_i] diff --git a/examples/sources/cons/ecp5evn/clk.lpf b/examples/sources/cons/ecp5evn/clk.lpf new file mode 100644 index 00000000..c6c70332 --- /dev/null +++ b/examples/sources/cons/ecp5evn/clk.lpf @@ -0,0 +1,2 @@ +LOCATE COMP "clk_i" SITE "A10"; +IOBUF PORT "clk_i" IO_TYPE=LVCMOS33; diff --git a/examples/sources/cons/ecp5evn/led.lpf b/examples/sources/cons/ecp5evn/led.lpf new file mode 100644 index 00000000..529c0b89 --- /dev/null +++ b/examples/sources/cons/ecp5evn/led.lpf @@ -0,0 +1,2 @@ +LOCATE COMP "led_o" SITE "A13"; +IOBUF PORT "led_o" IO_TYPE=LVCMOS25; diff --git a/examples/openflow/edu-ciaa-fpga.pcf b/examples/sources/cons/edu-ciaa/clk.pcf similarity index 51% rename from examples/openflow/edu-ciaa-fpga.pcf rename to examples/sources/cons/edu-ciaa/clk.pcf index 17fb1134..cfd62a35 100644 --- a/examples/openflow/edu-ciaa-fpga.pcf +++ b/examples/sources/cons/edu-ciaa/clk.pcf @@ -1,2 +1 @@ set_io clk_i 94 -set_io led_o 4 diff --git a/examples/sources/cons/edu-ciaa/led.pcf b/examples/sources/cons/edu-ciaa/led.pcf new file mode 100644 index 00000000..92e61517 --- /dev/null +++ b/examples/sources/cons/edu-ciaa/led.pcf @@ -0,0 +1 @@ +set_io led_o 4 diff --git a/examples/openflow/icestick.pcf b/examples/sources/cons/icestick/clk.pcf similarity index 50% rename from examples/openflow/icestick.pcf rename to examples/sources/cons/icestick/clk.pcf index caa6e413..8b246ac7 100644 --- a/examples/openflow/icestick.pcf +++ b/examples/sources/cons/icestick/clk.pcf @@ -1,2 +1 @@ set_io clk_i 21 -set_io led_o 95 diff --git a/examples/sources/cons/icestick/led.pcf b/examples/sources/cons/icestick/led.pcf new file mode 100644 index 00000000..9a56ae6f --- /dev/null +++ b/examples/sources/cons/icestick/led.pcf @@ -0,0 +1 @@ +set_io led_o 95 diff --git a/examples/sources/cons/maker/clk.pdc b/examples/sources/cons/maker/clk.pdc new file mode 100644 index 00000000..c0070c73 --- /dev/null +++ b/examples/sources/cons/maker/clk.pdc @@ -0,0 +1 @@ +set_io clk_i -DIRECTION INPUT -pinname 23 -fixed yes diff --git a/examples/sources/cons/maker/led.pdc b/examples/sources/cons/maker/led.pdc new file mode 100644 index 00000000..703bd95a --- /dev/null +++ b/examples/sources/cons/maker/led.pdc @@ -0,0 +1 @@ +set_io led_o -DIRECTION OUTPUT -pinname 117 -fixed yes diff --git a/examples/sources/cons/maker/timing.sdc b/examples/sources/cons/maker/timing.sdc new file mode 100644 index 00000000..00363616 --- /dev/null +++ b/examples/sources/cons/maker/timing.sdc @@ -0,0 +1 @@ +create_clock -period 20 [get_ports clk_i] diff --git a/examples/sources/cons/mpfs-disco-kit/clk.pdc b/examples/sources/cons/mpfs-disco-kit/clk.pdc new file mode 100644 index 00000000..61f7332f --- /dev/null +++ b/examples/sources/cons/mpfs-disco-kit/clk.pdc @@ -0,0 +1 @@ +set_io -port_name clk_i -DIRECTION INPUT -pin_name R18 -fixed true diff --git a/examples/sources/cons/mpfs-disco-kit/led.pdc b/examples/sources/cons/mpfs-disco-kit/led.pdc new file mode 100644 index 00000000..678d3bf4 --- /dev/null +++ b/examples/sources/cons/mpfs-disco-kit/led.pdc @@ -0,0 +1 @@ +set_io -port_name led_o -DIRECTION OUTPUT -pin_name T18 -fixed true diff --git a/examples/sources/cons/mpfs-disco-kit/timing.sdc b/examples/sources/cons/mpfs-disco-kit/timing.sdc new file mode 100644 index 00000000..00363616 --- /dev/null +++ b/examples/sources/cons/mpfs-disco-kit/timing.sdc @@ -0,0 +1 @@ +create_clock -period 20 [get_ports clk_i] diff --git a/examples/sources/cons/nexys3/clk.ucf b/examples/sources/cons/nexys3/clk.ucf new file mode 100644 index 00000000..c0230e2d --- /dev/null +++ b/examples/sources/cons/nexys3/clk.ucf @@ -0,0 +1 @@ +NET "clk_i" LOC = "V10"; diff --git a/examples/sources/cons/nexys3/led.ucf b/examples/sources/cons/nexys3/led.ucf new file mode 100644 index 00000000..c210831b --- /dev/null +++ b/examples/sources/cons/nexys3/led.ucf @@ -0,0 +1 @@ +NET "led_o" LOC = "U16"; diff --git a/examples/sources/cons/nexys3/timing.xcf b/examples/sources/cons/nexys3/timing.xcf new file mode 100644 index 00000000..a200fdeb --- /dev/null +++ b/examples/sources/cons/nexys3/timing.xcf @@ -0,0 +1,2 @@ +NET "clk_i" TNM_NET = "clk_i"; +TIMESPEC "TS_clk_i" = PERIOD "clk_i" 10.00 ns HIGH 50%; diff --git a/examples/sources/cons/orangecrab/clk.lpf b/examples/sources/cons/orangecrab/clk.lpf new file mode 100644 index 00000000..ed801503 --- /dev/null +++ b/examples/sources/cons/orangecrab/clk.lpf @@ -0,0 +1,2 @@ +LOCATE COMP "clk_i" SITE "A9"; +IOBUF PORT "clk_i" IO_TYPE=LVCMOS33; diff --git a/examples/sources/cons/orangecrab/led.lpf b/examples/sources/cons/orangecrab/led.lpf new file mode 100644 index 00000000..cad54d2b --- /dev/null +++ b/examples/sources/cons/orangecrab/led.lpf @@ -0,0 +1,2 @@ +LOCATE COMP "led_o" SITE "K4"; +IOBUF PORT "led_o" IO_TYPE=LVCMOS33; diff --git a/examples/sources/cons/s6micro/clk.ucf b/examples/sources/cons/s6micro/clk.ucf new file mode 100644 index 00000000..c0230e2d --- /dev/null +++ b/examples/sources/cons/s6micro/clk.ucf @@ -0,0 +1 @@ +NET "clk_i" LOC = "V10"; diff --git a/examples/sources/cons/s6micro/led.ucf b/examples/sources/cons/s6micro/led.ucf new file mode 100644 index 00000000..c557e8c5 --- /dev/null +++ b/examples/sources/cons/s6micro/led.ucf @@ -0,0 +1 @@ +NET "led_o" LOC = "C2"; diff --git a/examples/sources/cons/s6micro/timing.xcf b/examples/sources/cons/s6micro/timing.xcf new file mode 100644 index 00000000..2f24ee45 --- /dev/null +++ b/examples/sources/cons/s6micro/timing.xcf @@ -0,0 +1,2 @@ +NET "clk_i" TNM_NET = "clk_i"; +TIMESPEC "TS_clk_i" = PERIOD "clk_i" 20.00 ns HIGH 50%; diff --git a/examples/sources/slog/blink.sv b/examples/sources/slog/blink.sv new file mode 100644 index 00000000..4e2679e9 --- /dev/null +++ b/examples/sources/slog/blink.sv @@ -0,0 +1,24 @@ +module Blink #( + parameter int FREQ = 25000000, + parameter int SECS = 1 +)( + input clk_i, + output led_o +); + + localparam int DIV = FREQ*SECS; + logic led = 0; + logic [$clog2(DIV)-1:0] cnt = 0; + + always_ff @(posedge clk_i) begin + if (cnt == DIV-1) begin + cnt <= 0; + led <= ~led; + end else begin + cnt <= cnt + 1; + end + end + + assign led_o = led; + +endmodule diff --git a/examples/sources/slog/include1/header1.svh b/examples/sources/slog/include1/header1.svh new file mode 100644 index 00000000..ed066ea9 --- /dev/null +++ b/examples/sources/slog/include1/header1.svh @@ -0,0 +1 @@ +`define INCLUDE1 diff --git a/examples/sources/slog/include2/header2.svh b/examples/sources/slog/include2/header2.svh new file mode 100644 index 00000000..c45793ec --- /dev/null +++ b/examples/sources/slog/include2/header2.svh @@ -0,0 +1 @@ +`define INCLUDE2 diff --git a/examples/sources/slog/top.sv b/examples/sources/slog/top.sv new file mode 100644 index 00000000..732ae368 --- /dev/null +++ b/examples/sources/slog/top.sv @@ -0,0 +1,56 @@ +`include "header1.svh" +`include "header2.svh" + +module Top #( + parameter int FREQ = 0, + parameter int SECS = 0 +)( + input clk_i, + output led_o +); + + Blink #(.FREQ (FREQ), .SECS (SECS)) dut (.clk_i (clk_i), .led_o (led_o)); + +`ifndef INCLUDE1 + reg led; + always @(posedge clk_i) led <= 1'b0; + always @(posedge clk_i) led <= 1'b1; + assign led_o = led; + initial begin $stop; $error("intentional"); end +`endif + +`ifndef INCLUDE2 + reg led; + always @(posedge clk_i) led <= 1'b0; + always @(posedge clk_i) led <= 1'b1; + assign led_o = led; + initial begin $stop; $error("intentional"); end +`endif + +`ifndef DEFINE1 + reg led; + always @(posedge clk_i) led <= 1'b0; + always @(posedge clk_i) led <= 1'b1; + assign led_o = led; + initial begin $stop; $error("intentional"); end +`endif + +`ifndef DEFINE2 + reg led; + always @(posedge clk_i) led <= 1'b0; + always @(posedge clk_i) led <= 1'b1; + assign led_o = led; + initial begin $stop; $error("intentional"); end +`endif + + generate + if (!FREQ || !SECS) begin + reg led; + always @(posedge clk_i) led <= 1'b0; + always @(posedge clk_i) led <= 1'b1; + assign led_o = led; + initial begin $stop; $error("intentional"); end + end + endgenerate + +endmodule diff --git a/examples/sources/vhdl/blink.vhdl b/examples/sources/vhdl/blink.vhdl new file mode 100644 index 00000000..156f553c --- /dev/null +++ b/examples/sources/vhdl/blink.vhdl @@ -0,0 +1,35 @@ +library IEEE; +use IEEE.std_logic_1164.all; + +entity Blink is + generic ( + FREQ : positive := 25e6; + SECS : positive := 1 + ); + port ( + clk_i : in std_logic; + led_o : out std_logic + ); +end entity Blink; + +architecture RTL of Blink is + constant DIV : positive := FREQ*SECS; + signal led : std_logic := '0'; + signal cnt : natural range 0 to DIV-1 := 0; +begin + + blink_p: process (clk_i) + begin + if rising_edge(clk_i) then + if cnt = DIV-1 then + cnt <= 0; + led <= not led; + else + cnt <= cnt + 1; + end if; + end if; + end process blink_p; + + led_o <= led; + +end architecture RTL; diff --git a/examples/sources/vhdl/blink_pkg.vhdl b/examples/sources/vhdl/blink_pkg.vhdl new file mode 100644 index 00000000..3f6ee86a --- /dev/null +++ b/examples/sources/vhdl/blink_pkg.vhdl @@ -0,0 +1,17 @@ +library IEEE; +use IEEE.std_logic_1164.all; + +package blink_pkg is + + component Blink is + generic ( + FREQ : positive := 25e6; + SECS : positive := 1 + ); + port ( + clk_i : in std_logic; + led_o : out std_logic + ); + end component Blink; + +end package blink_pkg; diff --git a/examples/sources/vhdl/top.vhdl b/examples/sources/vhdl/top.vhdl new file mode 100644 index 00000000..c89ad50e --- /dev/null +++ b/examples/sources/vhdl/top.vhdl @@ -0,0 +1,42 @@ +library IEEE; +use IEEE.std_logic_1164.all; +library blink_lib; +use blink_lib.blink_pkg.all; + +entity Top is + generic ( + FREQ : natural := 0; + SECS : natural := 0 + ); + port ( + clk_i : in std_logic; + led_o : out std_logic + ); +end entity Top; + +architecture ARCH of Top is + signal led : std_logic; +begin + + blink_i: Blink + generic map (FREQ => FREQ, SECS => SECS) + port map (clk_i => clk_i, led_o => led_o); + + gen_error: if (FREQ=0 or SECS=0) generate + begin + process(clk_i) + begin + if rising_edge(clk_i) then + led <= '0'; + end if; + end process; + process(clk_i) + begin + if rising_edge(clk_i) then + led <= '1'; + end if; + end process; + led_o <= led; + end generate gen_error; + +end architecture ARCH; diff --git a/examples/sources/vlog/blink.v b/examples/sources/vlog/blink.v new file mode 100644 index 00000000..120b8c9f --- /dev/null +++ b/examples/sources/vlog/blink.v @@ -0,0 +1,24 @@ +module Blink #( + parameter FREQ = 25000000, + parameter SECS = 1 +)( + input clk_i, + output led_o +); + + localparam DIV = FREQ*SECS; + reg led = 0; + reg [$clog2(DIV)-1:0] cnt = 0; + + always @(posedge clk_i) begin + if (cnt == DIV-1) begin + cnt <= 0; + led <= ~led; + end else begin + cnt <= cnt + 1; + end + end + + assign led_o = led; + +endmodule diff --git a/examples/sources/vlog/include1/header1.vh b/examples/sources/vlog/include1/header1.vh new file mode 100644 index 00000000..ed066ea9 --- /dev/null +++ b/examples/sources/vlog/include1/header1.vh @@ -0,0 +1 @@ +`define INCLUDE1 diff --git a/examples/sources/vlog/include2/header2.vh b/examples/sources/vlog/include2/header2.vh new file mode 100644 index 00000000..c45793ec --- /dev/null +++ b/examples/sources/vlog/include2/header2.vh @@ -0,0 +1 @@ +`define INCLUDE2 diff --git a/examples/sources/vlog/top.v b/examples/sources/vlog/top.v new file mode 100644 index 00000000..9a6d452b --- /dev/null +++ b/examples/sources/vlog/top.v @@ -0,0 +1,56 @@ +`include "header1.vh" +`include "header2.vh" + +module Top #( + parameter FREQ = 0, + parameter SECS = 0 +)( + input clk_i, + output led_o +); + + Blink #(.FREQ (FREQ), .SECS (SECS)) dut (.clk_i (clk_i), .led_o (led_o)); + +`ifndef INCLUDE1 + reg led; + always @(posedge clk_i) led <= 1'b0; + always @(posedge clk_i) led <= 1'b1; + assign led_o = led; + initial begin $stop; $error("intentional"); end +`endif + +`ifndef INCLUDE2 + reg led; + always @(posedge clk_i) led <= 1'b0; + always @(posedge clk_i) led <= 1'b1; + assign led_o = led; + initial begin $stop; $error("intentional"); end +`endif + +`ifndef DEFINE1 + reg led; + always @(posedge clk_i) led <= 1'b0; + always @(posedge clk_i) led <= 1'b1; + assign led_o = led; + initial begin $stop; $error("intentional"); end +`endif + +`ifndef DEFINE2 + reg led; + always @(posedge clk_i) led <= 1'b0; + always @(posedge clk_i) led <= 1'b1; + assign led_o = led; + initial begin $stop; $error("intentional"); end +`endif + + generate + if (!FREQ || !SECS) begin + reg led; + always @(posedge clk_i) led <= 1'b0; + always @(posedge clk_i) led <= 1'b1; + assign led_o = led; + initial begin $stop; $error("intentional"); end + end + endgenerate + +endmodule diff --git a/examples/vivado/Makefile b/examples/vivado/Makefile deleted file mode 100644 index cd77fa54..00000000 --- a/examples/vivado/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/make - -SCRIPTS = $(wildcard *.py) - -all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT);) diff --git a/examples/vivado/README.md b/examples/vivado/README.md deleted file mode 100644 index 42c3fbf0..00000000 --- a/examples/vivado/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# Vivado examples - -The Zybo related things are the FPGA part and the constraints file. -Modify them to make this example compatible with another board. - -* `python3 vivado.py` generates a bitstream based on VHDL files. -* `python3 vivado.py --action transfer` to transfer the generated bitstream to the board. -* `python3 design.py` generates the bitstream based on a Vivado Block design. - -## How to get a compatible Vivado Block Design - -* In the *Flow Navigator* panel, under *IP INTEGRATOR*, click on *Create Block Design*. -* Work on your design. -* *File* -> *Export* -> *Export Block Design* to create a Tcl file to re-generate your design. - -> Tip: open the generated Tcl file and remove the following code (which is generally a problem instead of help): - -```tcl -################################################################ -# Check if script is running in correct Vivado version. -################################################################ -set scripts_vivado_version 2019.2 -set current_vivado_version [version -short] - -if { [string first $scripts_vivado_version $current_vivado_version] == -1 } { - puts "" - catch {common::send_msg_id "BD_TCL-109" "ERROR" "This script was generated using Vivado <$scripts_vivado_version> and is being run in <$current_vivado_version> of Vivado. Please run the script in Vivado <$scripts_vivado_version> then open the design in Vivado <$current_vivado_version>. Upgrade the design by running \"Tools => Report => Report IP Status...\", then run write_bd_tcl to create an updated script."} - - return 1 -} -``` diff --git a/examples/vivado/design.py b/examples/vivado/design.py deleted file mode 100644 index 4ea1cf31..00000000 --- a/examples/vivado/design.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Zybo block design example project.""" - -import logging - -from fpga.project import Project - -logging.basicConfig() - -prj = Project('vivado', 'zybo-design') -prj.set_part('xc7z010-1-clg400') - -prj.set_outdir('../../build/zybo-design') - -prj.add_files('../../hdl/blinking.vhdl') -prj.add_files('zybo.xdc') -prj.add_files('design.tcl', filetype='design') - -export = """ -set PROJECT %s -if { [ catch { - # Vitis - write_hw_platform -fixed -force -include_bit -file ${PROJECT}.xsa -} ] } { - # SDK - write_hwdef -force -file ${PROJECT}.hwdef - write_sysdef -force -hwdef [glob -nocomplain *.hwdef] \\ - -bitfile [glob -nocomplain *.bit] -file ${PROJECT}.hdf -} -""" % ('zybo-design') - -prj.add_hook(export, 'postbit') - -try: - prj.generate() -except Exception as e: - logging.warning('{} ({})'.format(type(e).__name__, e)) diff --git a/examples/vivado/design.tcl b/examples/vivado/design.tcl deleted file mode 100644 index 4b10a0d4..00000000 --- a/examples/vivado/design.tcl +++ /dev/null @@ -1,608 +0,0 @@ -################################################################ -# START -################################################################ - -# CHANGE DESIGN NAME HERE -variable design_name -set design_name design_1 - -# If you do not already have an existing IP Integrator design open, -# you can create a design using the following command: -# create_bd_design $design_name - -# Creating design if needed -set errMsg "" -set nRet 0 - -set cur_design [current_bd_design -quiet] -set list_cells [get_bd_cells -quiet] - -if { ${design_name} eq "" } { - # USE CASES: - # 1) Design_name not set - - set errMsg "Please set the variable to a non-empty value." - set nRet 1 - -} elseif { ${cur_design} ne "" && ${list_cells} eq "" } { - # USE CASES: - # 2): Current design opened AND is empty AND names same. - # 3): Current design opened AND is empty AND names diff; design_name NOT in project. - # 4): Current design opened AND is empty AND names diff; design_name exists in project. - - if { $cur_design ne $design_name } { - common::send_msg_id "BD_TCL-001" "INFO" "Changing value of from <$design_name> to <$cur_design> since current design is empty." - set design_name [get_property NAME $cur_design] - } - common::send_msg_id "BD_TCL-002" "INFO" "Constructing design in IPI design <$cur_design>..." - -} elseif { ${cur_design} ne "" && $list_cells ne "" && $cur_design eq $design_name } { - # USE CASES: - # 5) Current design opened AND has components AND same names. - - set errMsg "Design <$design_name> already exists in your project, please set the variable to another value." - set nRet 1 -} elseif { [get_files -quiet ${design_name}.bd] ne "" } { - # USE CASES: - # 6) Current opened design, has components, but diff names, design_name exists in project. - # 7) No opened design, design_name exists in project. - - set errMsg "Design <$design_name> already exists in your project, please set the variable to another value." - set nRet 2 - -} else { - # USE CASES: - # 8) No opened design, design_name not in project. - # 9) Current opened design, has components, but diff names, design_name not in project. - - common::send_msg_id "BD_TCL-003" "INFO" "Currently there is no design <$design_name> in project, so creating one..." - - create_bd_design $design_name - - common::send_msg_id "BD_TCL-004" "INFO" "Making design <$design_name> as current_bd_design." - current_bd_design $design_name - -} - -common::send_msg_id "BD_TCL-005" "INFO" "Currently the variable is equal to \"$design_name\"." - -if { $nRet != 0 } { - catch {common::send_msg_id "BD_TCL-114" "ERROR" $errMsg} - return $nRet -} - -set bCheckIPsPassed 1 -################################################################## -# CHECK IPs -################################################################## -set bCheckIPs 1 -if { $bCheckIPs == 1 } { - set list_check_ips "\ -xilinx.com:ip:processing_system7:5.5\ -" - - set list_ips_missing "" - common::send_msg_id "BD_TCL-006" "INFO" "Checking if the following IPs exist in the project's IP catalog: $list_check_ips ." - - foreach ip_vlnv $list_check_ips { - set ip_obj [get_ipdefs -all $ip_vlnv] - if { $ip_obj eq "" } { - lappend list_ips_missing $ip_vlnv - } - } - - if { $list_ips_missing ne "" } { - catch {common::send_msg_id "BD_TCL-115" "ERROR" "The following IPs are not found in the IP Catalog:\n $list_ips_missing\n\nResolution: Please add the repository containing the IP(s) to the project." } - set bCheckIPsPassed 0 - } - -} - -################################################################## -# CHECK Modules -################################################################## -set bCheckModules 1 -if { $bCheckModules == 1 } { - set list_check_mods "\ -Blinking\ -" - - set list_mods_missing "" - common::send_msg_id "BD_TCL-006" "INFO" "Checking if the following modules exist in the project's sources: $list_check_mods ." - - foreach mod_vlnv $list_check_mods { - if { [can_resolve_reference $mod_vlnv] == 0 } { - lappend list_mods_missing $mod_vlnv - } - } - - if { $list_mods_missing ne "" } { - catch {common::send_msg_id "BD_TCL-115" "ERROR" "The following module(s) are not found in the project: $list_mods_missing" } - common::send_msg_id "BD_TCL-008" "INFO" "Please add source files for the missing module(s) above." - set bCheckIPsPassed 0 - } -} - -if { $bCheckIPsPassed != 1 } { - common::send_msg_id "BD_TCL-1003" "WARNING" "Will not continue with creation of design due to the error(s) above." - return 3 -} - -################################################################## -# DESIGN PROCs -################################################################## - - - -# Procedure to create entire design; Provide argument to make -# procedure reusable. If parentCell is "", will use root. -proc create_root_design { parentCell } { - - variable design_name - - if { $parentCell eq "" } { - set parentCell [get_bd_cells /] - } - - # Get object for parentCell - set parentObj [get_bd_cells $parentCell] - if { $parentObj == "" } { - catch {common::send_msg_id "BD_TCL-100" "ERROR" "Unable to find parent cell <$parentCell>!"} - return - } - - # Make sure parentObj is hier blk - set parentType [get_property TYPE $parentObj] - if { $parentType ne "hier" } { - catch {common::send_msg_id "BD_TCL-101" "ERROR" "Parent <$parentObj> has TYPE = <$parentType>. Expected to be ."} - return - } - - # Save current instance; Restore later - set oldCurInst [current_bd_instance .] - - # Set parent object as current - current_bd_instance $parentObj - - - # Create interface ports - set DDR [ create_bd_intf_port -mode Master -vlnv xilinx.com:interface:ddrx_rtl:1.0 DDR ] - - set FIXED_IO [ create_bd_intf_port -mode Master -vlnv xilinx.com:display_processing_system7:fixedio_rtl:1.0 FIXED_IO ] - - - # Create ports - set clk_i [ create_bd_port -dir I clk_i ] - set led_o [ create_bd_port -dir O led_o ] - - # Create instance: Blinking_0, and set properties - set block_name Blinking - set block_cell_name Blinking_0 - if { [catch {set Blinking_0 [create_bd_cell -type module -reference $block_name $block_cell_name] } errmsg] } { - catch {common::send_msg_id "BD_TCL-105" "ERROR" "Unable to add referenced block <$block_name>. Please add the files for ${block_name}'s definition into the project."} - return 1 - } elseif { $Blinking_0 eq "" } { - catch {common::send_msg_id "BD_TCL-106" "ERROR" "Unable to referenced block <$block_name>. Please add the files for ${block_name}'s definition into the project."} - return 1 - } - set_property -dict [ list \ - CONFIG.FREQ {125000000} \ - ] $Blinking_0 - - # Create instance: processing_system7_0, and set properties - set processing_system7_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:processing_system7:5.5 processing_system7_0 ] - set_property -dict [ list \ - CONFIG.PCW_ACT_APU_PERIPHERAL_FREQMHZ {650} \ - CONFIG.PCW_ACT_CAN_PERIPHERAL_FREQMHZ {10} \ - CONFIG.PCW_ACT_DCI_PERIPHERAL_FREQMHZ {10} \ - CONFIG.PCW_ACT_ENET0_PERIPHERAL_FREQMHZ {10} \ - CONFIG.PCW_ACT_ENET1_PERIPHERAL_FREQMHZ {10} \ - CONFIG.PCW_ACT_FPGA0_PERIPHERAL_FREQMHZ {10} \ - CONFIG.PCW_ACT_FPGA1_PERIPHERAL_FREQMHZ {10} \ - CONFIG.PCW_ACT_FPGA2_PERIPHERAL_FREQMHZ {10} \ - CONFIG.PCW_ACT_FPGA3_PERIPHERAL_FREQMHZ {10} \ - CONFIG.PCW_ACT_PCAP_PERIPHERAL_FREQMHZ {200} \ - CONFIG.PCW_ACT_QSPI_PERIPHERAL_FREQMHZ {200} \ - CONFIG.PCW_ACT_SDIO_PERIPHERAL_FREQMHZ {50} \ - CONFIG.PCW_ACT_SMC_PERIPHERAL_FREQMHZ {10} \ - CONFIG.PCW_ACT_SPI_PERIPHERAL_FREQMHZ {10} \ - CONFIG.PCW_ACT_TPIU_PERIPHERAL_FREQMHZ {200} \ - CONFIG.PCW_ACT_TTC0_CLK0_PERIPHERAL_FREQMHZ {108} \ - CONFIG.PCW_ACT_TTC0_CLK1_PERIPHERAL_FREQMHZ {108} \ - CONFIG.PCW_ACT_TTC0_CLK2_PERIPHERAL_FREQMHZ {108} \ - CONFIG.PCW_ACT_TTC1_CLK0_PERIPHERAL_FREQMHZ {108} \ - CONFIG.PCW_ACT_TTC1_CLK1_PERIPHERAL_FREQMHZ {108} \ - CONFIG.PCW_ACT_TTC1_CLK2_PERIPHERAL_FREQMHZ {108} \ - CONFIG.PCW_ACT_UART_PERIPHERAL_FREQMHZ {100} \ - CONFIG.PCW_ACT_WDT_PERIPHERAL_FREQMHZ {108} \ - CONFIG.PCW_APU_PERIPHERAL_FREQMHZ {650} \ - CONFIG.PCW_ARMPLL_CTRL_FBDIV {26} \ - CONFIG.PCW_CAN_PERIPHERAL_DIVISOR0 {1} \ - CONFIG.PCW_CAN_PERIPHERAL_DIVISOR1 {1} \ - CONFIG.PCW_CLK0_FREQ {10000000} \ - CONFIG.PCW_CLK1_FREQ {10000000} \ - CONFIG.PCW_CLK2_FREQ {10000000} \ - CONFIG.PCW_CLK3_FREQ {10000000} \ - CONFIG.PCW_CPU_CPU_PLL_FREQMHZ {1300} \ - CONFIG.PCW_CPU_PERIPHERAL_DIVISOR0 {2} \ - CONFIG.PCW_CRYSTAL_PERIPHERAL_FREQMHZ {50.000000} \ - CONFIG.PCW_DCI_PERIPHERAL_DIVISOR0 {15} \ - CONFIG.PCW_DCI_PERIPHERAL_DIVISOR1 {7} \ - CONFIG.PCW_DDRPLL_CTRL_FBDIV {21} \ - CONFIG.PCW_DDR_DDR_PLL_FREQMHZ {1050} \ - CONFIG.PCW_DDR_PERIPHERAL_DIVISOR0 {2} \ - CONFIG.PCW_DDR_RAM_HIGHADDR {0x1FFFFFFF} \ - CONFIG.PCW_ENET0_ENET0_IO {} \ - CONFIG.PCW_ENET0_PERIPHERAL_DIVISOR0 {1} \ - CONFIG.PCW_ENET0_PERIPHERAL_DIVISOR1 {1} \ - CONFIG.PCW_ENET0_PERIPHERAL_ENABLE {0} \ - CONFIG.PCW_ENET0_PERIPHERAL_FREQMHZ {1000 Mbps} \ - CONFIG.PCW_ENET0_RESET_ENABLE {0} \ - CONFIG.PCW_ENET1_PERIPHERAL_DIVISOR0 {1} \ - CONFIG.PCW_ENET1_PERIPHERAL_DIVISOR1 {1} \ - CONFIG.PCW_ENET1_RESET_ENABLE {0} \ - CONFIG.PCW_ENET_RESET_ENABLE {0} \ - CONFIG.PCW_ENET_RESET_SELECT {} \ - CONFIG.PCW_I2C0_RESET_ENABLE {0} \ - CONFIG.PCW_I2C1_RESET_ENABLE {0} \ - CONFIG.PCW_I2C_PERIPHERAL_FREQMHZ {25} \ - CONFIG.PCW_I2C_RESET_ENABLE {0} \ - CONFIG.PCW_IOPLL_CTRL_FBDIV {32} \ - CONFIG.PCW_IO_IO_PLL_FREQMHZ {1600} \ - CONFIG.PCW_MIO_0_DIRECTION {} \ - CONFIG.PCW_MIO_0_PULLUP {} \ - CONFIG.PCW_MIO_10_DIRECTION {} \ - CONFIG.PCW_MIO_10_PULLUP {} \ - CONFIG.PCW_MIO_11_DIRECTION {} \ - CONFIG.PCW_MIO_11_PULLUP {} \ - CONFIG.PCW_MIO_12_DIRECTION {} \ - CONFIG.PCW_MIO_12_PULLUP {} \ - CONFIG.PCW_MIO_13_DIRECTION {} \ - CONFIG.PCW_MIO_13_PULLUP {} \ - CONFIG.PCW_MIO_14_DIRECTION {} \ - CONFIG.PCW_MIO_14_PULLUP {} \ - CONFIG.PCW_MIO_15_DIRECTION {} \ - CONFIG.PCW_MIO_15_PULLUP {} \ - CONFIG.PCW_MIO_16_DIRECTION {} \ - CONFIG.PCW_MIO_16_PULLUP {} \ - CONFIG.PCW_MIO_17_DIRECTION {} \ - CONFIG.PCW_MIO_17_PULLUP {} \ - CONFIG.PCW_MIO_18_DIRECTION {} \ - CONFIG.PCW_MIO_18_PULLUP {} \ - CONFIG.PCW_MIO_19_DIRECTION {} \ - CONFIG.PCW_MIO_19_PULLUP {} \ - CONFIG.PCW_MIO_1_DIRECTION {out} \ - CONFIG.PCW_MIO_1_IOTYPE {LVCMOS 3.3V} \ - CONFIG.PCW_MIO_1_PULLUP {disabled} \ - CONFIG.PCW_MIO_1_SLEW {fast} \ - CONFIG.PCW_MIO_20_DIRECTION {} \ - CONFIG.PCW_MIO_20_PULLUP {} \ - CONFIG.PCW_MIO_21_DIRECTION {} \ - CONFIG.PCW_MIO_21_PULLUP {} \ - CONFIG.PCW_MIO_22_DIRECTION {} \ - CONFIG.PCW_MIO_22_PULLUP {} \ - CONFIG.PCW_MIO_23_DIRECTION {} \ - CONFIG.PCW_MIO_23_PULLUP {} \ - CONFIG.PCW_MIO_24_DIRECTION {} \ - CONFIG.PCW_MIO_24_PULLUP {} \ - CONFIG.PCW_MIO_25_DIRECTION {} \ - CONFIG.PCW_MIO_25_PULLUP {} \ - CONFIG.PCW_MIO_26_DIRECTION {} \ - CONFIG.PCW_MIO_26_PULLUP {} \ - CONFIG.PCW_MIO_27_DIRECTION {} \ - CONFIG.PCW_MIO_27_PULLUP {} \ - CONFIG.PCW_MIO_28_DIRECTION {} \ - CONFIG.PCW_MIO_28_PULLUP {} \ - CONFIG.PCW_MIO_29_DIRECTION {} \ - CONFIG.PCW_MIO_29_PULLUP {} \ - CONFIG.PCW_MIO_2_DIRECTION {inout} \ - CONFIG.PCW_MIO_2_IOTYPE {LVCMOS 3.3V} \ - CONFIG.PCW_MIO_2_PULLUP {disabled} \ - CONFIG.PCW_MIO_2_SLEW {fast} \ - CONFIG.PCW_MIO_30_DIRECTION {} \ - CONFIG.PCW_MIO_30_PULLUP {} \ - CONFIG.PCW_MIO_31_DIRECTION {} \ - CONFIG.PCW_MIO_31_PULLUP {} \ - CONFIG.PCW_MIO_32_DIRECTION {} \ - CONFIG.PCW_MIO_32_PULLUP {} \ - CONFIG.PCW_MIO_33_DIRECTION {} \ - CONFIG.PCW_MIO_33_PULLUP {} \ - CONFIG.PCW_MIO_34_DIRECTION {} \ - CONFIG.PCW_MIO_34_PULLUP {} \ - CONFIG.PCW_MIO_35_DIRECTION {} \ - CONFIG.PCW_MIO_35_PULLUP {} \ - CONFIG.PCW_MIO_36_DIRECTION {} \ - CONFIG.PCW_MIO_36_PULLUP {} \ - CONFIG.PCW_MIO_37_DIRECTION {} \ - CONFIG.PCW_MIO_37_PULLUP {} \ - CONFIG.PCW_MIO_38_DIRECTION {} \ - CONFIG.PCW_MIO_38_PULLUP {} \ - CONFIG.PCW_MIO_39_DIRECTION {} \ - CONFIG.PCW_MIO_39_PULLUP {} \ - CONFIG.PCW_MIO_3_DIRECTION {inout} \ - CONFIG.PCW_MIO_3_IOTYPE {LVCMOS 3.3V} \ - CONFIG.PCW_MIO_3_PULLUP {disabled} \ - CONFIG.PCW_MIO_3_SLEW {fast} \ - CONFIG.PCW_MIO_40_DIRECTION {inout} \ - CONFIG.PCW_MIO_40_IOTYPE {LVCMOS 1.8V} \ - CONFIG.PCW_MIO_40_PULLUP {disabled} \ - CONFIG.PCW_MIO_40_SLEW {fast} \ - CONFIG.PCW_MIO_41_DIRECTION {inout} \ - CONFIG.PCW_MIO_41_IOTYPE {LVCMOS 1.8V} \ - CONFIG.PCW_MIO_41_PULLUP {disabled} \ - CONFIG.PCW_MIO_41_SLEW {fast} \ - CONFIG.PCW_MIO_42_DIRECTION {inout} \ - CONFIG.PCW_MIO_42_IOTYPE {LVCMOS 1.8V} \ - CONFIG.PCW_MIO_42_PULLUP {disabled} \ - CONFIG.PCW_MIO_42_SLEW {fast} \ - CONFIG.PCW_MIO_43_DIRECTION {inout} \ - CONFIG.PCW_MIO_43_IOTYPE {LVCMOS 1.8V} \ - CONFIG.PCW_MIO_43_PULLUP {disabled} \ - CONFIG.PCW_MIO_43_SLEW {fast} \ - CONFIG.PCW_MIO_44_DIRECTION {inout} \ - CONFIG.PCW_MIO_44_IOTYPE {LVCMOS 1.8V} \ - CONFIG.PCW_MIO_44_PULLUP {disabled} \ - CONFIG.PCW_MIO_44_SLEW {fast} \ - CONFIG.PCW_MIO_45_DIRECTION {inout} \ - CONFIG.PCW_MIO_45_IOTYPE {LVCMOS 1.8V} \ - CONFIG.PCW_MIO_45_PULLUP {disabled} \ - CONFIG.PCW_MIO_45_SLEW {fast} \ - CONFIG.PCW_MIO_46_DIRECTION {} \ - CONFIG.PCW_MIO_46_PULLUP {} \ - CONFIG.PCW_MIO_47_DIRECTION {in} \ - CONFIG.PCW_MIO_47_IOTYPE {LVCMOS 1.8V} \ - CONFIG.PCW_MIO_47_PULLUP {disabled} \ - CONFIG.PCW_MIO_47_SLEW {slow} \ - CONFIG.PCW_MIO_48_DIRECTION {out} \ - CONFIG.PCW_MIO_48_IOTYPE {LVCMOS 1.8V} \ - CONFIG.PCW_MIO_48_PULLUP {disabled} \ - CONFIG.PCW_MIO_48_SLEW {slow} \ - CONFIG.PCW_MIO_49_DIRECTION {in} \ - CONFIG.PCW_MIO_49_IOTYPE {LVCMOS 1.8V} \ - CONFIG.PCW_MIO_49_PULLUP {disabled} \ - CONFIG.PCW_MIO_49_SLEW {slow} \ - CONFIG.PCW_MIO_4_DIRECTION {inout} \ - CONFIG.PCW_MIO_4_IOTYPE {LVCMOS 3.3V} \ - CONFIG.PCW_MIO_4_PULLUP {disabled} \ - CONFIG.PCW_MIO_4_SLEW {fast} \ - CONFIG.PCW_MIO_50_DIRECTION {} \ - CONFIG.PCW_MIO_50_PULLUP {} \ - CONFIG.PCW_MIO_51_DIRECTION {} \ - CONFIG.PCW_MIO_51_PULLUP {} \ - CONFIG.PCW_MIO_52_DIRECTION {} \ - CONFIG.PCW_MIO_52_PULLUP {} \ - CONFIG.PCW_MIO_53_DIRECTION {} \ - CONFIG.PCW_MIO_53_PULLUP {} \ - CONFIG.PCW_MIO_5_DIRECTION {inout} \ - CONFIG.PCW_MIO_5_IOTYPE {LVCMOS 3.3V} \ - CONFIG.PCW_MIO_5_PULLUP {disabled} \ - CONFIG.PCW_MIO_5_SLEW {fast} \ - CONFIG.PCW_MIO_6_DIRECTION {out} \ - CONFIG.PCW_MIO_6_IOTYPE {LVCMOS 3.3V} \ - CONFIG.PCW_MIO_6_PULLUP {disabled} \ - CONFIG.PCW_MIO_6_SLEW {fast} \ - CONFIG.PCW_MIO_7_DIRECTION {} \ - CONFIG.PCW_MIO_7_PULLUP {} \ - CONFIG.PCW_MIO_8_DIRECTION {out} \ - CONFIG.PCW_MIO_8_IOTYPE {LVCMOS 3.3V} \ - CONFIG.PCW_MIO_8_PULLUP {disabled} \ - CONFIG.PCW_MIO_8_SLEW {fast} \ - CONFIG.PCW_MIO_9_DIRECTION {} \ - CONFIG.PCW_MIO_9_PULLUP {} \ - CONFIG.PCW_MIO_TREE_PERIPHERALS {unassigned#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#unassigned#Quad SPI Flash#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#SD 0#SD 0#SD 0#SD 0#SD 0#SD 0#unassigned#SD 0#UART 1#UART 1#unassigned#unassigned#unassigned#unassigned} \ - CONFIG.PCW_MIO_TREE_SIGNALS {unassigned#qspi0_ss_b#qspi0_io[0]#qspi0_io[1]#qspi0_io[2]#qspi0_io[3]/HOLD_B#qspi0_sclk#unassigned#qspi_fbclk#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#clk#cmd#data[0]#data[1]#data[2]#data[3]#unassigned#cd#tx#rx#unassigned#unassigned#unassigned#unassigned} \ - CONFIG.PCW_NAND_GRP_D8_ENABLE {0} \ - CONFIG.PCW_NAND_PERIPHERAL_ENABLE {0} \ - CONFIG.PCW_NOR_GRP_A25_ENABLE {0} \ - CONFIG.PCW_NOR_GRP_CS0_ENABLE {0} \ - CONFIG.PCW_NOR_GRP_CS1_ENABLE {0} \ - CONFIG.PCW_NOR_GRP_SRAM_CS0_ENABLE {0} \ - CONFIG.PCW_NOR_GRP_SRAM_CS1_ENABLE {0} \ - CONFIG.PCW_NOR_GRP_SRAM_INT_ENABLE {0} \ - CONFIG.PCW_NOR_PERIPHERAL_ENABLE {0} \ - CONFIG.PCW_PCAP_PERIPHERAL_DIVISOR0 {8} \ - CONFIG.PCW_PRESET_BANK1_VOLTAGE {LVCMOS 1.8V} \ - CONFIG.PCW_QSPI_GRP_FBCLK_ENABLE {1} \ - CONFIG.PCW_QSPI_GRP_FBCLK_IO {MIO 8} \ - CONFIG.PCW_QSPI_GRP_IO1_ENABLE {0} \ - CONFIG.PCW_QSPI_GRP_SINGLE_SS_ENABLE {1} \ - CONFIG.PCW_QSPI_GRP_SINGLE_SS_IO {MIO 1 .. 6} \ - CONFIG.PCW_QSPI_GRP_SS1_ENABLE {0} \ - CONFIG.PCW_QSPI_PERIPHERAL_DIVISOR0 {8} \ - CONFIG.PCW_QSPI_PERIPHERAL_ENABLE {1} \ - CONFIG.PCW_QSPI_PERIPHERAL_FREQMHZ {200} \ - CONFIG.PCW_QSPI_QSPI_IO {MIO 1 .. 6} \ - CONFIG.PCW_SD0_GRP_CD_ENABLE {1} \ - CONFIG.PCW_SD0_GRP_CD_IO {MIO 47} \ - CONFIG.PCW_SD0_GRP_POW_ENABLE {0} \ - CONFIG.PCW_SD0_GRP_WP_ENABLE {1} \ - CONFIG.PCW_SD0_GRP_WP_IO {EMIO} \ - CONFIG.PCW_SD0_PERIPHERAL_ENABLE {1} \ - CONFIG.PCW_SD0_SD0_IO {MIO 40 .. 45} \ - CONFIG.PCW_SDIO_PERIPHERAL_DIVISOR0 {32} \ - CONFIG.PCW_SDIO_PERIPHERAL_FREQMHZ {50} \ - CONFIG.PCW_SDIO_PERIPHERAL_VALID {1} \ - CONFIG.PCW_SINGLE_QSPI_DATA_MODE {x4} \ - CONFIG.PCW_SMC_PERIPHERAL_DIVISOR0 {1} \ - CONFIG.PCW_SPI_PERIPHERAL_DIVISOR0 {1} \ - CONFIG.PCW_TPIU_PERIPHERAL_DIVISOR0 {1} \ - CONFIG.PCW_TTC0_CLK0_PERIPHERAL_FREQMHZ {133.333333} \ - CONFIG.PCW_TTC0_CLK1_PERIPHERAL_FREQMHZ {133.333333} \ - CONFIG.PCW_TTC0_CLK2_PERIPHERAL_FREQMHZ {133.333333} \ - CONFIG.PCW_TTC0_PERIPHERAL_ENABLE {0} \ - CONFIG.PCW_TTC0_TTC0_IO {} \ - CONFIG.PCW_USB0_USB0_IO {} \ - CONFIG.PCW_USE_M_AXI_GP0 {0} \ - ] $processing_system7_0 - - # Create interface connections - connect_bd_intf_net -intf_net processing_system7_0_DDR [get_bd_intf_ports DDR] [get_bd_intf_pins processing_system7_0/DDR] - connect_bd_intf_net -intf_net processing_system7_0_FIXED_IO [get_bd_intf_ports FIXED_IO] [get_bd_intf_pins processing_system7_0/FIXED_IO] - - # Create port connections - connect_bd_net -net Blinking_0_led_o [get_bd_ports led_o] [get_bd_pins Blinking_0/led_o] - connect_bd_net -net clk_i_1 [get_bd_ports clk_i] [get_bd_pins Blinking_0/clk_i] - - # Create address segments - - - # Restore current instance - current_bd_instance $oldCurInst - - validate_bd_design - save_bd_design -} -# End of create_root_design() - - -################################################################## -# MAIN FLOW -################################################################## - -create_root_design "" diff --git a/examples/vivado/vivado.py b/examples/vivado/vivado.py deleted file mode 100644 index 5b00bd1a..00000000 --- a/examples/vivado/vivado.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Vivado example project.""" - -import argparse -import logging - -from fpga.project import Project - -logging.basicConfig() - -parser = argparse.ArgumentParser() -parser.add_argument( - '--action', choices=['generate', 'transfer', 'all'], default='generate', -) -args = parser.parse_args() - -prj = Project('vivado') -prj.set_part('xc7z010-1-clg400') - -prj.set_outdir('../../build/vivado') - -prj.set_param('FREQ', '125000000') -prj.add_files('../../hdl/blinking.vhdl') -prj.add_files('zybo.xdc') -prj.set_top('Blinking') - -if args.action in ['generate', 'all']: - try: - prj.generate() - except RuntimeError: - print('ERROR:generate:Vivado not found') - -if args.action in ['transfer', 'all']: - try: - prj.transfer('fpga') - except RuntimeError: - print('ERROR:transfer:Vivado not found') diff --git a/examples/vivado/zybo.xdc b/examples/vivado/zybo.xdc deleted file mode 100644 index 9b6fe3d3..00000000 --- a/examples/vivado/zybo.xdc +++ /dev/null @@ -1,10 +0,0 @@ -# Xilinx Design Constraints -# -# Are based on the standard Synopsys Design Constraints (SDC) format. - -create_clock -name clk_i -period 8 [get_ports clk_i] - -set_property PACKAGE_PIN L16 [get_ports clk_i] -set_property IOSTANDARD LVCMOS33 [get_ports clk_i] -set_property PACKAGE_PIN M14 [get_ports led_o] -set_property IOSTANDARD LVCMOS33 [get_ports led_o] diff --git a/examples/yosys/Makefile b/examples/yosys/Makefile deleted file mode 100644 index 9546d682..00000000 --- a/examples/yosys/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/make - -SCRIPTS = $(wildcard *.py) - -all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT); python3 $(SCRIPT) --lang vhdl; ) diff --git a/examples/yosys/README.md b/examples/yosys/README.md deleted file mode 100644 index 44649f92..00000000 --- a/examples/yosys/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Yosys examples - -## Yosys synthesis - -* Verilog project: `python3 yosys.py` -* VHDL project: `python3 yosys.py --lang vhdl` - -## Yosys using ISE as backend - -Bitstream generation: -* Verilog project: `python3 ise.py` -* VHDL project: `python3 ise.py --lang vhdl` - -Spartan-6 FPGA LX9 MicroBoard programming: -* Verilog project: `python3 ise.py --action transfer` -* VHDL project: `python3 ise.py --lang vhdl --action transfer` - -## Yosys using Vivado as backend - -Bitstream generation: -* Verilog project: `python3 vivado.py` -* VHDL project: `python3 vivado.py --lang vhdl` - -Zybo programming: -* Verilog project: `python3 vivado.py --action transfer` -* VHDL project: `python3 vivado.py --lang vhdl --action transfer` diff --git a/examples/yosys/yosys.py b/examples/yosys/yosys.py deleted file mode 100644 index 29316969..00000000 --- a/examples/yosys/yosys.py +++ /dev/null @@ -1,34 +0,0 @@ -"""Yosys example project.""" - -import argparse -import logging - -from fpga.project import Project - -logging.basicConfig() - -parser = argparse.ArgumentParser() -parser.add_argument( - '--lang', choices=['verilog', 'vhdl'], default='verilog', -) -args = parser.parse_args() - -prj = Project('yosys') -prj.set_outdir('../../build/yosys-{}'.format(args.lang)) - -if args.lang == 'verilog': - prj.add_path('../../hdl/headers1') - prj.add_path('../../hdl/headers2') - prj.add_files('../../hdl/blinking.v') - prj.add_files('../../hdl/top.v') -else: # args.lang == 'vhdl' - prj.add_files('../../hdl/blinking.vhdl', library='examples') - prj.add_files('../../hdl/examples_pkg.vhdl', library='examples') - prj.add_files('../../hdl/top.vhdl') - -prj.set_top('Top') - -try: - prj.generate() -except RuntimeError: - print('ERROR:generate:Docker not found') diff --git a/fpga/__init__.py b/fpga/__init__.py deleted file mode 100644 index ffaf5fbc..00000000 --- a/fpga/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""PyFPGA""" - -__version__ = '0.2.0' - -from fpga.project import Project diff --git a/fpga/helpers/bitprog.py b/fpga/helpers/bitprog.py deleted file mode 100644 index a15e18a3..00000000 --- a/fpga/helpers/bitprog.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2020 INTI -# Copyright (C) 2020 Rodrigo A. Melo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -""" -A CLI helper utility to transfer a bitstream to a supported device. -""" - -import argparse -import logging -import sys - -from fpga import __version__ as version -from fpga.project import Project, TOOLS -from fpga.tool import MEMWIDTHS - -logging.basicConfig() -logging.getLogger('fpga.project').level = logging.INFO - -DEVS = ['fpga', 'spi', 'bpi'] -POSITIONS = range(1, 10) -ACTIONS = ['program', 'detect', 'unlock'] - -EPILOGUE = """ -Supported values of arguments with choices: -* TOOL = {} -* DEVTYPE = {} -* POSITIONS = {} -* MEMWIDTH = {} -* ACTION = {} -""".format( - " | ".join(TOOLS), - " | ".join(DEVS), - " | ".join(str(x) for x in POSITIONS), - " | ".join(str(x) for x in MEMWIDTHS), - " | ".join(ACTIONS) -) - - -def main(): - """Solves the main functionality of this helper.""" - - # Parsing the command-line. - - parser = argparse.ArgumentParser( - description=__doc__, - epilog=EPILOGUE, - formatter_class=argparse.RawDescriptionHelpFormatter - ) - - parser.add_argument( - '-v', '--version', - action='version', - version='v{}'.format(version) - ) - - parser.add_argument( - 'bit', - metavar='BITFILE', - nargs='?', - help='a bitstream file' - ) - - parser.add_argument( - '-t', '--tool', - metavar='TOOL', - default='vivado', - choices=TOOLS, - help='backend tool to be used [vivado]' - ) - - parser.add_argument( - '-o', '--outdir', - metavar='PATH', - default='temp', - help='where to generate files [temp]' - ) - - parser.add_argument( - '-d', '--device', - metavar='DEVTYPE', - choices=DEVS, - default=DEVS[0], - help='the target device type [{}]'.format(DEVS[0]) - ) - - parser.add_argument( - '-p', '--position', - metavar='POSITION', - choices=POSITIONS, - type=int, - default=1, - help='the device position into the JTAG chain [1]' - ) - - parser.add_argument( - '-m', '--memname', - metavar='MEMNAME', - default='', - help='memory name if spi or bpi selected' - ) - - parser.add_argument( - '-w', '--width', - metavar='MEMWIDTH', - choices=MEMWIDTHS, - type=int, - default=1, - help='memory width if spi or bpi selected [1]' - ) - - parser.add_argument( - '--run', - metavar='ACTION', - choices=ACTIONS, - default=ACTIONS[0], - help='action to perform [{}]'.format(ACTIONS[0]) - ) - - args = parser.parse_args() - - # Solving with PyFPGA - - prj = Project(args.tool, relative_to_script=False) - prj.set_outdir(args.outdir) - - if args.run == 'program': - devtype = args.device - prj.set_bitstream(args.bit) - elif args.run == 'detect': - devtype = 'detect' - else: # args.run == 'unlock' - devtype = 'unlock' - - try: - prj.transfer(devtype, args.position, args.memname, args.width) - except RuntimeError: - logging.error('{} not found'.format(args.tool)) - except Exception as e: - sys.exit('{} ({})'.format(type(e).__name__, e)) - - -if __name__ == "__main__": - main() diff --git a/fpga/helpers/hdl2bit.py b/fpga/helpers/hdl2bit.py deleted file mode 100644 index 68378ca9..00000000 --- a/fpga/helpers/hdl2bit.py +++ /dev/null @@ -1,162 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2020 INTI -# Copyright (C) 2020 Rodrigo A. Melo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -""" -A CLI helper utility to go from FPGA design files to a bitstream. -""" - -import argparse -import logging -import sys - -from fpga import __version__ as version -from fpga.project import Project, TOOLS -from fpga.tool import TASKS - -logging.basicConfig() -logging.getLogger('fpga.project').level = logging.INFO - -EPILOGUE = """ -Supported values of arguments with choices: -* TOOL = {} -* TASK = {} - -Notes: -* PATH and FILE must be relative to the execution directory. -* The default PART name and how to specify it depends on the selected TOOL. -* More than one '--file', '--include' or '--param' arguments can be specified. -""".format( - " | ".join(TOOLS), - " | ".join(TASKS[1:len(TASKS)]) -) - - -def main(): - """Solves the main functionality of this helper.""" - - # Parsing the command-line. - - parser = argparse.ArgumentParser( - description=__doc__, - epilog=EPILOGUE, - formatter_class=argparse.RawDescriptionHelpFormatter - ) - - parser.add_argument( - '-v', '--version', - action='version', - version='v{}'.format(version) - ) - - parser.add_argument( - 'top', - metavar='TOPFILE', - help='a top-level file' - ) - - parser.add_argument( - '-t', '--tool', - metavar='TOOL', - default='vivado', - choices=TOOLS, - help='backend tool to be used [vivado]' - ) - - parser.add_argument( - '-o', '--outdir', - metavar='PATH', - default='temp', - help='where to generate files [temp]' - ) - - parser.add_argument( - '-p', '--part', - metavar='PART', - help='the target device' - ) - - parser.add_argument( - '-f', '--file', - metavar='FILE[,PACKAGE]', - action='append', - help='add a design file (specifying an optional VHDL package)' - ) - - parser.add_argument( - '-i', '--include', - metavar='PATH', - action='append', - help='specify where to search Verilog included files' - ) - - parser.add_argument( - '--param', - metavar=('GENERIC/PARAMETER', 'VALUE'), - action='append', - nargs=2, - help='set the value of a generic/parameter of the top-level' - ) - - parser.add_argument( - '--run', - metavar='TASK', - choices=TASKS[1:len(TASKS)], - default='bit', - help='task to perform [{}]'.format('bit') - ) - - args = parser.parse_args() - - # Solving with PyFPGA - - prj = Project(args.tool, relative_to_script=False) - prj.set_outdir(args.outdir) - - if args.part is not None: - prj.set_part(args.part) - - if args.include is not None: - for include in args.include: - prj.add_path(include) - - if args.file is not None: - for file in args.file: - file = file.split(',') - if len(file) > 1: - prj.add_files(file[0], library=file[1]) - else: - prj.add_files(file[0]) - - if args.param is not None: - for param in args.param: - prj.set_param(param[0], param[1]) - - prj.add_files(args.top) - prj.set_top(args.top) - - try: - prj.generate(args.run) - except RuntimeError: - logging.error('{} not found'.format(args.tool)) - except Exception as e: - sys.exit('{} ({})'.format(type(e).__name__, e)) - - -if __name__ == "__main__": - main() diff --git a/fpga/helpers/prj2bit.py b/fpga/helpers/prj2bit.py deleted file mode 100644 index 1cd8a74e..00000000 --- a/fpga/helpers/prj2bit.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2020 INTI -# Copyright (C) 2020 Rodrigo A. Melo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -""" -A CLI helper utility to deal with a vendor FPGA Project file. -""" - -import argparse -import logging -import os -import sys - -from fpga import __version__ as version -from fpga.project import Project -from fpga.tool import TASKS - -logging.basicConfig() -logging.getLogger('fpga.project').level = logging.INFO - - -def main(): - """Solves the main functionality of this helper.""" - - # Parsing the command-line. - - parser = argparse.ArgumentParser( - description=__doc__ - ) - - parser.add_argument( - '-v', '--version', - action='version', - version='v{}'.format(version) - ) - - parser.add_argument( - 'project', - metavar='PRJFILE', - help='a vendor project file' - ) - - parser.add_argument( - '--run', - metavar='TASK', - choices=TASKS[1:len(TASKS)], - default='bit', - help='task to perform [{}] ({})'.format( - 'bit', " | ".join(TASKS[1:len(TASKS)]) - ) - ) - - parser.add_argument( - '--clean', - action='store_true', - help='clean the generated project files' - ) - - args = parser.parse_args() - - # Detecting a Project file - - tool_per_ext = { - '.xise': 'ise', - '.prjx': 'libero', - '.qpf': 'quartus', - '.xpr': 'vivado' - } - - if not os.path.exists(args.project): - sys.exit('Project file not found') - - outdir = os.path.dirname(args.project) - project, extension = os.path.splitext(args.project) - project = os.path.basename(project) - - tool = '' - if extension in tool_per_ext: - tool = tool_per_ext[extension] - print('{} Project file found.'.format(tool)) - else: - sys.exit('Unknown Project file extension') - - # Solving with PyFPGA - - prj = Project(tool, project=project, relative_to_script=False) - prj.set_outdir(outdir) - - prj.set_top(project) - - try: - if args.clean: - prj.clean() - else: - prj.generate(args.run, 'syn') - except RuntimeError: - logging.error('{} not found'.format(tool)) - except Exception as e: - sys.exit('{} ({})'.format(type(e).__name__, e)) - - -if __name__ == "__main__": - main() diff --git a/fpga/project.py b/fpga/project.py deleted file mode 100644 index d9bc2f81..00000000 --- a/fpga/project.py +++ /dev/null @@ -1,366 +0,0 @@ -# -# Copyright (C) 2019-2021 Rodrigo A. Melo -# Copyright (C) 2019-2020 INTI -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -"""fpga.project - -This module implements the main class of PyFPGA, which provides -functionalities to create a project, generate a bitstream and transfer it to a -Device. -""" - -import contextlib -import glob -import inspect -import logging -import os -import re -import time - - -TOOLS = [ - 'ghdl', 'ise', 'libero', 'openflow', 'quartus', 'vivado', 'yosys', - 'yosys-ise', 'yosys-vivado' -] - - -_log = logging.getLogger(__name__) -_log.level = logging.INFO -_log.addHandler(logging.NullHandler()) - - -class Project: - """Class to manage an FPGA project. - - :param tool: FPGA tool to be used - :param project: project name (the tool name is used if none specified) - :param init: a dict to initialize some parameters - :param relative_to_script: specifies if the files/directories are relative - to the script or the execution directory - :raises NotImplementedError: when tool is unsupported - - .. note:: Valid values for **tool** are ``ghdl``, ``ise``, ``libero``, - ``openflow``, ``quartus``, ``vivado``, ``yosys``, ``yosys-ise`` and - ``yosys-vivado`` - """ - - def __init__( - self, tool='vivado', project=None, init=None, - relative_to_script=True): - """Class constructor.""" - if tool == 'ghdl': - from fpga.tool.openflow import Openflow - self.tool = Openflow(project, frontend='ghdl', backend='vhdl') - elif tool in ['ise', 'yosys-ise']: - from fpga.tool.ise import Ise - self.tool = Ise(project, 'yosys' if 'yosys' in tool else '') - elif tool == 'libero': - from fpga.tool.libero import Libero - self.tool = Libero(project) - elif tool == 'openflow': - from fpga.tool.openflow import Openflow - self.tool = Openflow(project) - elif tool == 'quartus': - from fpga.tool.quartus import Quartus - self.tool = Quartus(project) - elif tool in ['vivado', 'yosys-vivado']: - from fpga.tool.vivado import Vivado - self.tool = Vivado(project, 'yosys' if 'yosys' in tool else '') - elif tool == 'yosys': - from fpga.tool.openflow import Openflow - self.tool = Openflow(project, frontend='yosys', backend='verilog') - else: - raise NotImplementedError(tool) - self._rundir = os.getcwd() - _log.debug('RUNDIR = %s', self._rundir) - if relative_to_script: - self._reldir = os.path.dirname(inspect.stack()[-1].filename) - else: - self._reldir = '' - _log.debug('RELDIR = %s', self._reldir) - self._absdir = os.path.join(self._rundir, self._reldir) - _log.debug('ABSDIR = %s', self._absdir) - self.set_outdir('build') - self._initialize(init) - - def _initialize(self, init): - """Set some of the most used internal parameters.""" - if init is None: - return - if 'outdir' in init: - _log.debug('OUTDIR = %s', init['outdir']) - self.set_outdir(init['outdir']) - if 'part' in init: - _log.debug('PART = %s', init['part']) - self.set_part(init['part']) - if 'paths' in init: - for path in init['paths']: - _log.debug('PATH = %s', path) - self.add_path(path) - for filetype in ['vhdl', 'verilog', 'constraint']: - if filetype in init: - for file in init[filetype]: - if isinstance(file, list): - filename = file[0] - library = file[1] - else: - filename = file - library = None - _log.debug( - 'FILE = %s %s %s', filename, filetype, library - ) - self.add_files(filename, filetype, library) - if 'params' in init: - for parname, parvalue in init['params'].items(): - _log.debug('PARAM = %s %s', parname, parvalue) - self.set_param(parname, parvalue) - if 'top' in init: - _log.debug('TOP = %s', init['top']) - self.set_top(init['top']) - - def set_outdir(self, outdir): - """Sets the OUTput DIRectory (where to put the resulting files). - - :param outdir: path to the output directory - """ - self.outdir = os.path.normpath(os.path.join(self._absdir, outdir)) - _log.debug('OUTDIR = %s', self.outdir) - - def get_configs(self): - """Gets the Project Configurations. - - :returns: a dict which includes ``tool`` and ``project`` names, the - ``extension`` of a project file (according to the selected tool) and - the ``part`` to be used - """ - return self.tool.get_configs() - - def set_part(self, part): - """Set the target FPGA part. - - :param part: the FPGA part as specified by the tool - """ - self.tool.set_part(part) - - def set_param(self, name, value): - """Set a Generic/Parameter Value. - - :param name: parameter/generic name - :param value: value to be assigned - """ - self.tool.set_param(name, value) - - def add_files(self, pathname, filetype=None, library=None, options=None): - """Adds files to the project. - - :param pathname: a relative path to a file, which can contain - shell-style wildcards (glob compliant) - :param filetype: specifies the file type - :param library: an optional VHDL library name - :param options: to be provided to the underlying tool - :raises FileNotFoundError: when a file specified as pathname is not - found - :raises ValueError: when *filetype* is unsupported - - .. note:: Valid values for *filetype* are ``vhdl``, ``verilog``, - ``system_verilog``, ``constraint`` (default) and ``block_design`` - (only **Vivado** is currently supported). If None provided, this - value is automatically discovered based on the extension ( - ``.vhd`` or ``.vhdl``, ``.v`` and ``.sv``). - """ - pathname = os.path.join(self._absdir, pathname) - pathname = os.path.normpath(pathname) - _log.debug('PATHNAME = %s', pathname) - files = glob.glob(pathname) - if len(files) == 0: - raise FileNotFoundError(pathname) - for file in files: - if not os.path.exists(file): - raise FileNotFoundError(file) - if filetype is None: - ext = os.path.splitext(file)[1] - if ext in ['.vhd', '.vhdl']: - filetype = 'vhdl' - elif ext in ['.v', '.sv']: - filetype = 'verilog' - else: - filetype = 'constraint' - _log.debug('add_files: %s filetype detected', filetype) - file = os.path.relpath(file, self.outdir) - self.tool.add_file(file, filetype, library, options) - - def get_files(self): - """Get the files of the project. - - :returns: a list with the files of the project - """ - return self.tool.get_files() - - def add_path(self, path): - """Add a search path. - - Useful to specify where to search Verilog Included Files or IP - repositories. - - :param path: a relative path to a directory - :raises NotADirectoryError: when path is not a directory - """ - path = os.path.join(self._absdir, path) - path = os.path.normpath(path) - if os.path.isdir(path): - path = os.path.relpath(path, self.outdir) - self.tool.add_path(path) - else: - raise NotADirectoryError(path) - - def set_top(self, toplevel): - """Set the top level of the project. - - :param toplevel: name or file path of the top level entity/module - :raises FileNotFoundError: when toplevel is a not found file - """ - if os.path.splitext(toplevel)[1]: - toplevel = os.path.join(self._absdir, toplevel) - toplevel = os.path.normpath(toplevel) - if os.path.exists(toplevel): - with open(toplevel, 'r', encoding='utf-8') as file: - hdl = file.read() - # Removing comments, newlines and carriage-returns - hdl = re.sub(r'--.*[$\n]|\/\/.*[$\n]', '', hdl) - hdl = hdl.replace('\n', '').replace('\r', '') - hdl = re.sub(r'\/\*.*\*\/', '', hdl) - # Finding modules/entities - top = re.findall(r'module\s+(\w+)\s*[#(;]', hdl) - top.extend(re.findall(r'entity\s+(\w+)\s+is', hdl)) - if len(top) > 0: - self.tool.set_top(top[-1]) - if len(top) > 1: - _log.warning( - 'set_top: more than one Top found (last selected)' - ) - else: - self.tool.set_top('UNDEFINED') - else: - raise FileNotFoundError(toplevel) - else: - self.tool.set_top(toplevel) - - def add_hook(self, hook, phase='project'): - """Adds a hook in the specified phase. - - A hook is a place that allows you to insert customized programming. - - :param hook: is a string representing a tool specific command - :param phase: the phase where to insert a hook - :raises ValueError: when phase is unsupported - - .. note:: Valid values for *phase* are - ``prefile`` (to add options needed to find files), - ``project`` (to add project related options), - ``preflow`` (to change options previous to run the flow), - ``postsyn`` (to perform an action between *syn* and *imp*), - ``postimp`` (to perform an action between *imp* and *bit*) and - ``postbit`` (to perform an action after *bit*) - - .. warning:: Using a hook, you will be probably broken the vendor - independence - """ - self.tool.add_hook(hook, phase) - - def generate(self, to_task='bit', from_task='prj', capture=False): - """Run the FPGA tool. - - :param to_task: last task - :param from_task: first task - :param capture: capture STDOUT and STDERR - :returns: STDOUT and STDERR messages - :raises ValueError: when from_task is later than to_task - :raises RuntimeError: when the tool to be used is not found - :raises ValueError: when to_task or from_task are is unsupported - - .. note:: Valid values for **tasks** are - ``prj`` (to creates the project file), - ``syn`` (to performs the synthesis), - ``imp`` (to runs implementation) and - ``bit`` (to generates the bitstream) - """ - _log.info( - 'generating "%s" project using "%s" tool into "%s" directory', - self.tool.project, self.tool.get_configs()['tool'], self.outdir - ) - with self._run_in_dir(): - if capture: - _log.info('the execution messages are being captured') - return self.tool.generate(to_task, from_task, capture) - - def set_bitstream(self, path): - """Set the bitstream file to transfer. - - :param path: path to the bitstream file - """ - path = os.path.join(self._absdir, path) - if not os.path.exists(path): - raise FileNotFoundError(path) - self.tool.set_bitstream(path) - - def transfer( - self, devtype='fpga', position=1, part='', width=1, - capture=False): - """Transfers the generated bitstream to a device. - - :param devtype: *fpga* or other valid option - (depending on the used tool, it could be *spi*, *bpi*, etc) - :param position: position of the device in the JTAG chain - :param part: name of the memory (when device is not *fpga*) - :param width: bits width of the memory (when device is not *fpga*) - :param capture: capture STDOUT and STDERR - :returns: STDOUT and STDERR messages - :raises RuntimeError: when the tool to be used is not found - :raises FileNotFoundError: when the bitstream is not found - :raises ValueError: when devtype, position or width are unsupported - """ - _log.info( - 'transfering "%s" project using "%s" tool from "%s" directory', - self.tool.project, self.tool.get_configs()['tool'], self.outdir - ) - with self._run_in_dir(): - if capture: - _log.info('the execution messages are being captured') - return self.tool.transfer(devtype, position, part, width, capture) - - def clean(self): - """Clean the generated project files.""" - _log.info( - 'cleaning the generated project files into "%s"', self.outdir - ) - with self._run_in_dir(): - self.tool.clean() - - @contextlib.contextmanager - def _run_in_dir(self): - """Runs the tool in another directory.""" - start = time.time() - try: - if not os.path.exists(self.outdir): - _log.debug('the output directory did not exist (created)') - os.makedirs(self.outdir) - os.chdir(self.outdir) - yield - finally: - end = time.time() - os.chdir(self._rundir) - _log.info('executed in %.3f seconds', end-start) diff --git a/fpga/tool/__init__.py b/fpga/tool/__init__.py deleted file mode 100644 index 1f82fc3e..00000000 --- a/fpga/tool/__init__.py +++ /dev/null @@ -1,257 +0,0 @@ -# -# Copyright (C) 2019-2020 INTI -# Copyright (C) 2019-2021 Rodrigo A. Melo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -"""fpga.tool - -Defines the interface to be inherited to support a tool. -""" - -from glob import glob -import os -import subprocess -from shutil import rmtree, which -from yaml import safe_load - - -FILETYPES = ['verilog', 'vhdl', 'constraint', 'design'] -MEMWIDTHS = [1, 2, 4, 8, 16, 32] -PHASES = ['prefile', 'project', 'preflow', 'postsyn', 'postimp', 'postbit'] -TASKS = ['prj', 'syn', 'imp', 'bit'] - - -def check_value(value, values): - """Check if VALUE is included in VALUES.""" - if value not in values: - joined_values = ", ".join(values) - raise ValueError(f'{value} is not a valid value [{joined_values}]') - - -def run(command, capture): - """Run a command.""" - output = subprocess.PIPE if capture else None - check = not capture - result = subprocess.run( - command, shell=True, check=check, universal_newlines=True, - stdout=output, stderr=subprocess.STDOUT - ) - return result.stdout - - -def tcl_path(path): - """Returns a Tcl suitable path.""" - return path.replace(os.path.sep, "/") - - -class Tool: - """Tool interface. - - It is the basic interface for tool implementations. - """ - - # Following variables are set in each inheritance (if employed) - _TOOL = None # tool name - _EXTENSION = None # project file extension - _PART = None # default device part name - _GEN_PROGRAM = None # program used when generate is executed - _GEN_COMMAND = None # command to run when generate is executed - _TRF_PROGRAM = None # program used when transfer is executed - _TRF_COMMAND = None # command to run when transfer is executed - _BIT_EXT = [] # Supported BITstream EXTensions - _DEVTYPES = [] # Supported DEVice TYPES - _CLEAN = [] # Files to be CLEAN - - def __init__(self, project): - """Initializes the attributes of the class.""" - self.bitstream = None - self.cmds = { - 'prefile': [], - 'project': [], - 'preflow': [], - 'postsyn': [], - 'postimp': [], - 'postbit': [] - } - self.files = { - 'vhdl': [], - 'verilog': [], - 'constraint': [], - 'design': [] - } - self.params = [] - self.part = { - 'name': 'UNSET', - 'family': 'UNSET', - 'device': 'UNSET', - 'package': 'UNSET', - 'speed': 'UNSET' - } - self.paths = [] - self.presynth = False - self.project = self._TOOL if project is None else project - self.set_part(self._PART) - self.set_top('UNDEFINED') - self._configure() - - def _configure(self): - """Configures the underlying tools.""" - filename = '.pyfpga.yml' - self.configs = {} - if os.path.exists(filename): - with open(filename, 'r', encoding='utf-8') as file: - data = safe_load(file) - if self._TOOL in data: - self.configs = data[self._TOOL] - - def get_configs(self): - """Get Configurations.""" - return { - 'tool': self._TOOL, - 'project': self.project, - 'extension': self._EXTENSION, - 'part': self.part['name'] - } - - def set_part(self, part): - """Set the target PART.""" - self.part['name'] = part - - def set_param(self, name, value): - """Set a Generic/Parameter Value.""" - self.params.append([name, value]) - - def add_file(self, file, filetype, library, options): - """Add a file to the project of the specified **type**.""" - check_value(filetype, FILETYPES) - self.files[filetype].append([file, library, options]) - - def get_files(self): - """Get the files of the project.""" - return self.files - - def add_path(self, path): - """Add a search path.""" - self.paths.append(path) - - def set_top(self, top): - """Set the TOP LEVEL of the project.""" - self.top = top - - def add_hook(self, hook, phase): - """Add the specified *hook* in the desired *phase*.""" - check_value(phase, PHASES) - self.cmds[phase].append(hook) - - def _create_gen_script(self, tasks): - """Create the script for generate execution.""" - # Paths and files - files = [] - if self.presynth: - files.append(f' fpga_file {self.project}.edif') - else: - for path in self.paths: - files.append(f' fpga_include {tcl_path(path)}') - for file in self.files['verilog']: - files.append(f' fpga_file {tcl_path(file[0])}') - for file in self.files['vhdl']: - if file[1] is None: - files.append(f' fpga_file {tcl_path(file[0])}') - else: - files.append( - f' fpga_file {tcl_path(file[0])} {file[1]}' - ) - for file in self.files['design']: - files.append(f' fpga_design {tcl_path(file[0])}') - for file in self.files['constraint']: - files.append(f' fpga_file {tcl_path(file[0])}') - # Parameters - params = [] - for param in self.params: - params.append(f'{{ {param[0]} {param[1]} }}') - # Script creation - template = os.path.join(os.path.dirname(__file__), 'template.tcl') - with open(template, 'r', encoding='utf-8') as file: - tcl = file.read() - tcl = tcl.replace('#TOOL#', self._TOOL) - tcl = tcl.replace('#PRESYNTH#', "True" if self.presynth else "False") - tcl = tcl.replace('#PROJECT#', self.project) - tcl = tcl.replace('#PART#', self.part['name']) - tcl = tcl.replace('#FAMILY#', self.part['family']) - tcl = tcl.replace('#DEVICE#', self.part['device']) - tcl = tcl.replace('#PACKAGE#', self.part['package']) - tcl = tcl.replace('#SPEED#', self.part['speed']) - tcl = tcl.replace('#PARAMS#', ' '.join(params)) - tcl = tcl.replace('#FILES#', '\n'.join(files)) - tcl = tcl.replace('#TOP#', self.top) - tcl = tcl.replace('#TASKS#', tasks) - tcl = tcl.replace('#PREFILE_CMDS#', '\n'.join(self.cmds['prefile'])) - tcl = tcl.replace('#PROJECT_CMDS#', '\n'.join(self.cmds['project'])) - tcl = tcl.replace('#PREFLOW_CMDS#', '\n'.join(self.cmds['preflow'])) - tcl = tcl.replace('#POSTSYN_CMDS#', '\n'.join(self.cmds['postsyn'])) - tcl = tcl.replace('#POSTIMP_CMDS#', '\n'.join(self.cmds['postimp'])) - tcl = tcl.replace('#POSTBIT_CMDS#', '\n'.join(self.cmds['postbit'])) - with open(f'{self._TOOL}.tcl', 'w', encoding='utf-8') as file: - file.write(tcl) - - def generate(self, to_task, from_task, capture): - """Run the FPGA tool.""" - check_value(to_task, TASKS) - check_value(from_task, TASKS) - to_index = TASKS.index(to_task) - from_index = TASKS.index(from_task) - if from_index > to_index: - raise ValueError( - f'initial task "{from_task}" cannot be later than the ' + - f'last task "{to_task}"' - ) - tasks = " ".join(TASKS[from_index:to_index+1]) - self._create_gen_script(tasks) - if not which(self._GEN_PROGRAM): - raise RuntimeError(f'program "{self._GEN_PROGRAM}" not found') - return run(self._GEN_COMMAND, capture) - - def set_bitstream(self, path): - """Set the bitstream file to transfer.""" - self.bitstream = path - - def transfer(self, devtype, position, part, width, capture): - """Transfer a bitstream.""" - if not which(self._TRF_PROGRAM): - raise RuntimeError(f'program "{self._TRF_PROGRAM}" not found') - check_value(devtype, self._DEVTYPES) - check_value(position, range(10)) - isinstance(part, str) - check_value(width, MEMWIDTHS) - isinstance(capture, bool) - # Bitstream autodiscovery - if not self.bitstream and devtype not in ['detect', 'unlock']: - bitstream = [] - for ext in self._BIT_EXT: - bitstream.extend(glob(f'**/*.{ext}', recursive=True)) - if len(bitstream) == 0: - raise FileNotFoundError('bitStream not found') - self.bitstream = bitstream[0] - - def clean(self): - """Clean the generated project files.""" - for path in self._CLEAN: - elements = glob(path) - for element in elements: - if os.path.isfile(element): - os.remove(element) - else: - rmtree(element) diff --git a/fpga/tool/ise.py b/fpga/tool/ise.py deleted file mode 100644 index c6f5e83d..00000000 --- a/fpga/tool/ise.py +++ /dev/null @@ -1,197 +0,0 @@ -# -# Copyright (C) 2019-2020 INTI -# Copyright (C) 2019-2021 Rodrigo A. Melo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -"""fpga.tool.ise - -Implements the support of ISE (Xilinx). -""" - -import re - -from fpga.tool import Tool, run - -_TEMPLATES = { - 'fpga': """setMode -bs -setCable -port auto -Identify -inferir -assignFile -p #POSITION# -file #BITSTREAM# -Program -p #POSITION# - -quit -""", - 'spi': """setMode -pff -addConfigDevice -name #NAME# -path . -setSubmode -pffspi -addDesign -version 0 -name 0 -addDeviceChain -index 0 -addDevice -p 1 -file #BITSTREAM# -generate -generic - -setMode -bs -setCable -port auto -Identify -attachflash -position #POSITION# -spi #NAME# -assignfiletoattachedflash -position #POSITION# -file ./#NAME#.mcs -Program -p #POSITION# -dataWidth #WIDTH# -spionly -e -v -loadfpga - -quit -""", - 'bpi': """setMode -pff -addConfigDevice -name #NAME# -path . -setSubmode -pffbpi -addDesign -version 0 -name 0 -addDeviceChain -index 0 -setAttribute -configdevice -attr flashDataWidth -value #WIDTH# -addDevice -p 1 -file #BITSTREAM# -generate -generic - -setMode -bs -setCable -port auto -Identify -attachflash -position #POSITION# -bpi #NAME# -assignfiletoattachedflash -position #POSITION# -file ./#NAME#.mcs -Program -p #POSITION# -dataWidth #WIDTH# \ --rs1 NONE -rs0 NONE -bpionly -e -v -loadfpga - -quit -""", - 'detect': """setMode -bs -setCable -port auto -Identify -inferir -quit -""", - 'unlock': """cleancablelock -quit -""" -} - - -class Ise(Tool): - """Implementation of the class to support ISE.""" - - _TOOL = 'ise' - _EXTENSION = 'xise' - _PART = 'xc7k160t-3-fbg484' - _GEN_PROGRAM = 'xtclsh' - _GEN_COMMAND = 'xtclsh ise.tcl' - _TRF_PROGRAM = 'impact' - _TRF_COMMAND = 'impact -batch ise-prog.impact' - _BIT_EXT = ['bit'] - _DEVTYPES = ['fpga', 'spi', 'bpi', 'detect', 'unlock'] - _CLEAN = [ - # directories - 'iseconfig', '_ngo', 'xlnx_auto_0_xdb', '_xmsgs', 'xst', - # files - '*.bgn', '*.bld', '*.bit', - '*.cmd_log', '*.csv', - '*.drc', - '*.gise', - '*.html', - '*.log', '*.lso', - '*.map', '*.mrp', - '*.ncd', '*.ngc', '*.ngd', '*.ngm', '*.ngr', - '*.pad', '*.par', '*.pcf', '*.prj', '*.ptwx', - '*.stx', '*.syr', - '*.twr', '*.twx', - '*.unroutes', '*.ut', - '*.txt', - '*.xml', '*.xpi', '*.xrpt', '*.xst', '*.xwbt', - '_impact*', - # pyfpga - '*.impact', 'ise.tcl' - ] - - def __init__(self, project, frontend=None): - super().__init__(project) - if frontend == 'yosys': - from fpga.tool.openflow import Openflow - self.tool = Openflow( - self.project, - frontend='yosys', - backend='ise' - ) - self.presynth = True - - def set_part(self, part): - try: - device, speed, package = re.findall(r'(\w+)-(\w+)-(\w+)', part)[0] - if len(speed) > len(package): - speed, package = package, speed - part = f'{device}-{speed}-{package}' - except IndexError: - raise ValueError( - 'Part must be DEVICE-SPEED-PACKAGE or DEVICE-PACKAGE-SPEED' - ) - self.part['name'] = part - self.part['family'] = get_family(part) - self.part['device'] = device - self.part['package'] = package - self.part['speed'] = '-' + speed - - def generate(self, to_task, from_task, capture): - if self.presynth and from_task in ['prj', 'syn']: - self.tool.set_part(self.part['name']) - self.tool.set_top(self.top) - self.tool.paths = self.paths - self.tool.files['vhdl'] = self.files['vhdl'] - self.tool.files['verilog'] = self.files['verilog'] - self.tool.params = self.params - output1 = self.tool.generate('syn', 'prj', capture) - output2 = super().generate(to_task, from_task, capture) - return str(output1) + str(output2) - return super().generate(to_task, from_task, capture) - - def transfer(self, devtype, position, part, width, capture): - super().transfer(devtype, position, part, width, capture) - temp = _TEMPLATES[devtype] - if devtype not in ['detect', 'unlock']: - temp = temp.replace('#BITSTREAM#', self.bitstream) - temp = temp.replace('#POSITION#', str(position)) - temp = temp.replace('#NAME#', part) - temp = temp.replace('#WIDTH#', str(width)) - with open('ise-prog.impact', 'w', encoding='utf-8') as file: - file.write(temp) - return run(self._TRF_COMMAND, capture) - - -def get_family(part): - """Get the Family name from the specified part name.""" - part = part.lower() - families = { - r'xc7a\d+l': 'artix7l', - r'xc7a': 'artix7', - r'xc7k\d+l': 'kintex7l', - r'xc7k': 'kintex7', - r'xc3sd\d+a': 'spartan3adsp', - r'xc3s\d+a': 'spartan3a', - r'xc3s\d+e': 'spartan3e', - r'xc3s': 'spartan3', - r'xc6s\d+l': 'spartan6l', - r'xc6s': 'spartan6', - r'xc4v': 'virtex4', - r'xc5v': 'virtex5', - r'xc6v\d+l': 'virtex6l', - r'xc6v': 'virtex6', - r'xc7v\d+l': 'virtex7l', - r'xc7v': 'virtex7', - r'xc7z': 'zynq' - } - for key, value in families.items(): - if re.match(key, part): - return value - return 'UNKNOWN' diff --git a/fpga/tool/libero.py b/fpga/tool/libero.py deleted file mode 100644 index ee463993..00000000 --- a/fpga/tool/libero.py +++ /dev/null @@ -1,103 +0,0 @@ -# -# Copyright (C) 2019-2020 INTI -# Copyright (C) 2019-2021 Rodrigo A. Melo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -"""fpga.tool.libero - -Implements the support of Libero (Microchip/Microsemi). -""" - -import re - -from fpga.tool import Tool - -_TEMPLATES = { - 'fpga': """\ -""", - 'detect': """\ -""" -} - -# open_project -file {$TEMPDIR/libero.prjx} -# run_tool -name {CONFIGURE_CHAIN} -script {$TEMPDIR/flashpro.tcl} -# run_tool -name {PROGRAMDEVICE} - -# set flashpro_programmer "configure_flashpro5_prg -vpump {ON} \ -# -clk_mode {free_running_clk} -programming_method {spi_slave} \ -# -force_freq {OFF} -freq {4000000}" - - -class Libero(Tool): - """Implementation of the class to support Libero.""" - - _TOOL = 'libero' - _EXTENSION = 'prjx' - _PART = 'mpf100t-1-fcg484' - _GEN_PROGRAM = 'libero' - _GEN_COMMAND = 'libero SCRIPT:libero.tcl' - _DEVTYPES = ['fpga'] - _CLEAN = [ - # directories - 'libero', - # pyfpga - 'libero.tcl' - ] - - def set_part(self, part): - try: - device, speed, package = re.findall(r'(\w+)-(\w+)-*(\w*)', part)[0] - if len(speed) > len(package): - speed, package = package, speed - if speed == '': - speed = 'STD' - part = f'{device}-{speed}-{package}' - except IndexError: - raise ValueError( - 'Part must be DEVICE-SPEED-PACKAGE or DEVICE-PACKAGE' - ) - self.part['name'] = part - self.part['family'] = get_family(part) - self.part['device'] = device - self.part['package'] = package - self.part['speed'] = 'STD' if speed == 'STD' else '-' + speed - - def transfer(self, devtype, position, part, width, capture): - super().transfer(devtype, position, part, width, capture) - raise NotImplementedError('transfer(libero)') - - -def get_family(part): - """Get the Family name from the specified part name.""" - part = part.lower() - families = { - r'm2s': 'SmartFusion2', - r'm2gl': 'Igloo2', - r'rt4g': 'RTG4', - r'mpf': 'PolarFire', - r'a2f': 'SmartFusion', - r'afs': 'Fusion', - r'aglp': 'IGLOO+', - r'agle': 'IGLOOE', - r'agl': 'IGLOO', - r'a3p\d+l': 'ProAsic3L', - r'a3pe': 'ProAsic3E', - r'a3p': 'ProAsic3' - } - for key, value in families.items(): - if re.match(key, part): - return value - return 'UNKNOWN' diff --git a/fpga/tool/openflow.py b/fpga/tool/openflow.py deleted file mode 100644 index 0eb6b905..00000000 --- a/fpga/tool/openflow.py +++ /dev/null @@ -1,218 +0,0 @@ -# -# Copyright (C) 2020 INTI -# Copyright (C) 2020-2021 Rodrigo A. Melo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -"""fpga.tool.openflow - -Implements the support of the open-source tools. -""" - -import os -from fpga.tool import Tool, run - - -class Openflow(Tool): - """Implementation of the class to support the open-source tools.""" - - _TOOL = 'openflow' - _PART = 'hx8k-ct256' - _GEN_PROGRAM = 'docker' - _GEN_COMMAND = 'bash openflow.sh' - _TRF_PROGRAM = 'docker' - _TRF_COMMAND = 'bash openprog.sh' - _BIT_EXT = ['bit'] - _DEVTYPES = ['fpga'] - _CLEAN = [ - # files - '*.asc', '*.bit', '*.cf', '*.config', '*.edif', '*.json', '*.rpt', - '*.svf', - # pyfpga - '*.sh' - ] - - def __init__(self, project, frontend='yosys', backend='nextpnr'): - # The valid frontends are be ghdl and yosys - # The valid backends are: - # * For ghdl -> vhdl - # * For yosys -> ise, nextpnr, verilog, verilog-nosynth and vivado - super().__init__(project) - self.backend = backend - self.frontend = frontend - - def _configure(self): - super()._configure() - # OCI ENGINE - engine = self.configs.get('oci', {}).get('engine', {}) - command = engine.get('command', 'docker') + ' run --rm' - volumes = '-v ' + ('-v ').join(engine.get('volumes', ['$HOME:$HOME'])) - work = '-w ' + engine.get('work', '$PWD') - self.oci_engine = f'{command} {volumes} {work}' - # Containers - defaults = { - 'ghdl': 'ghdl/synth:beta', - 'yosys': 'ghdl/synth:beta', - 'nextpnr-ice40': 'ghdl/synth:nextpnr-ice40', - 'icetime': 'ghdl/synth:icestorm', - 'icepack': 'ghdl/synth:icestorm', - 'iceprog': '--device /dev/bus/usb ghdl/synth:prog', - 'nextpnr-ecp5': 'ghdl/synth:nextpnr-ecp5', - 'ecppack': 'ghdl/synth:trellis', - 'openocd': '--device /dev/bus/usb ghdl/synth:prog' - } - self.tools = {} - self.conts = {} - tools = self.configs.get('tools', {}) - containers = self.configs.get('oci', {}).get('containers', {}) - for tool, container in defaults.items(): - self.tools[tool] = tools.get(tool, tool) - self.conts[tool] = containers.get(tool, container) - - def set_part(self, part): - self.part['name'] = part - self.part['family'] = get_family(part) - if self.part['family'] in ['ice40', 'ecp5']: - aux = part.split('-') - if len(aux) == 2: - self.part['device'] = aux[0] - self.part['package'] = aux[1] - elif len(aux) == 3: - self.part['device'] = f'{aux[0]}-{aux[1]}' - self.part['package'] = aux[2] - else: - raise ValueError('Part must be DEVICE-PACKAGE') - if self.part['device'].endswith('4k'): - # See http://www.clifford.at/icestorm/ - self.part['device'] = self.part['device'].replace('4', '8') - self.part['package'] += ":4k" - - def _create_gen_script(self, tasks): - # Verilog includes - paths = [] - for path in self.paths: - paths.append(f'verilog_defaults -add -I{path}') - # Files - constraints = [] - verilogs = [] - vhdls = [] - for file in self.files['vhdl']: - lib = '' - if file[1] is not None: - lib = f'--work={file[1]}' - vhdls.append(f'{self.tools["ghdl"]} -a $FLAGS {lib} {file[0]}') - for file in self.files['verilog']: - if file[0].endswith('.sv'): - verilogs.append(f'read_verilog -sv -defer {file[0]}') - else: - verilogs.append(f'read_verilog -defer {file[0]}') - for file in self.files['constraint']: - constraints.append(file[0]) - if len(vhdls) > 0: - verilogs = [f'ghdl $FLAGS {self.top}'] - # Parameters - params = [] - for param in self.params: - params.append(f'chparam -set {param[0]} {param[1]} {self.top}') - # Script creation - template = os.path.join(os.path.dirname(__file__), 'template.sh') - with open(template, 'r', encoding='utf-8') as file: - text = file.read() - text = text.format( - backend=self.backend, - constraints='\\\n'+'\n'.join(constraints), - device=self.part['device'], - includes='\\\n'+'\n'.join(paths), - family=self.part['family'], - frontend=self.frontend, - package=self.part['package'], - params='\\\n'+'\n'.join(params), - project=self.project, - tasks=tasks, - top=self.top, - verilogs='\\\n'+'\n'.join(verilogs), - vhdls='\\\n'+'\n'.join(vhdls), - # - oci_engine=self.oci_engine, - cont_ghdl=self.conts['ghdl'], - cont_yosys=self.conts['yosys'], - cont_nextpnr_ice40=self.conts['nextpnr-ice40'], - cont_icetime=self.conts['icetime'], - cont_icepack=self.conts['icepack'], - cont_nextpnr_ecp5=self.conts['nextpnr-ecp5'], - cont_ecppack=self.conts['ecppack'], - tool_ghdl=self.tools['ghdl'], - tool_yosys=self.tools['yosys'], - tool_nextpnr_ice40=self.tools['nextpnr-ice40'], - tool_icetime=self.tools['icetime'], - tool_icepack=self.tools['icepack'], - tool_nextpnr_ecp5=self.tools['nextpnr-ecp5'], - tool_ecppack=self.tools['ecppack'] - ) - with open(f'{self._TOOL}.sh', 'w', encoding='utf-8') as file: - file.write(text) - - def generate(self, to_task, from_task, capture): - if self.frontend == 'ghdl' or 'verilog' in self.backend: - to_task = 'syn' - from_task = 'syn' - return super().generate(to_task, from_task, capture) - - def transfer(self, devtype, position, part, width, capture): - super().transfer(devtype, position, part, width, capture) - template = os.path.join(os.path.dirname(__file__), 'openprog.sh') - with open(template, 'r', encoding='utf-8') as file: - text = file.read() - text = text.format( - family=self.part['family'], - project=self.project, - # - oci_engine=self.oci_engine, - cont_iceprog=self.conts['iceprog'], - cont_openocd=self.conts['openocd'], - tool_iceprog=self.tools['iceprog'], - tool_openocd=self.tools['openocd'] - ) - with open('openprog.sh', 'w', encoding='utf-8') as file: - file.write(text) - return run(self._TRF_COMMAND, capture) - - -def get_family(part): - """Get the Family name from the specified part name.""" - part = part.lower() - families = [ - # From /techlibs/xilinx/synth_xilinx.cc - 'xcup', 'xcu', 'xc7', 'xc6s', 'xc6v', 'xc5v', 'xc4v', 'xc3sda', - 'xc3sa', 'xc3se', 'xc3s', 'xc2vp', 'xc2v', 'xcve', 'xcv' - ] - for family in families: - if part.startswith(family): - return family - families = [ - # From /ice40/main.cc - 'lp384', 'lp1k', 'lp4k', 'lp8k', 'hx1k', 'hx4k', 'hx8k', - 'up3k', 'up5k', 'u1k', 'u2k', 'u4k' - ] - if part.startswith(tuple(families)): - return 'ice40' - families = [ - # From /ecp5/main.cc - '12k', '25k', '45k', '85k', 'um-25k', 'um-45k', 'um-85k', - 'um5g-25k', 'um5g-45k', 'um5g-85k' - ] - if part.startswith(tuple(families)): - return 'ecp5' - return 'UNKNOWN' diff --git a/fpga/tool/openprog.sh b/fpga/tool/openprog.sh deleted file mode 100644 index 2746d050..00000000 --- a/fpga/tool/openprog.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2020 Rodrigo A. Melo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -set -e - -############################################################################### -# Things to tuneup -############################################################################### - -FAMILY={family} -PROJECT={project} - -# -# Tools configuration -# - -OCI_ENGINE="{oci_engine}" - -CONT_ICEPROG="{cont_iceprog}" -CONT_OPENOCD="{cont_openocd}" - -TOOL_ICEPROG="{tool_iceprog}" -TOOL_OPENOCD="{tool_openocd}" - -############################################################################### -# Programming -############################################################################### - -if [[ $FAMILY == "ice40" ]]; then - $OCI_ENGINE $CONT_ICEPROG $TOOL_ICEPROG $PROJECT.bit -elif [[ $FAMILY == "ecp5" ]]; then - $OCI_ENGINE $CONT_OPENOCD $TOOL_OPENOCD \ - -f /usr/share/trellis/misc/openocd/ecp5-evn.cfg \ - -c "transport select jtag; init; svf $PROJECT.svf; exit" -else - echo "ERROR: unsuported tool" && exit 1 -fi diff --git a/fpga/tool/quartus.py b/fpga/tool/quartus.py deleted file mode 100644 index f7644618..00000000 --- a/fpga/tool/quartus.py +++ /dev/null @@ -1,65 +0,0 @@ -# -# Copyright (C) 2019-2020 INTI -# Copyright (C) 2019-2020 Rodrigo A. Melo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -"""fpga.tool.quartus - -Implements the support of Quartus (Intel/Altera). -""" - -import re -import subprocess - -from fpga.tool import Tool, run - - -class Quartus(Tool): - """Implementation of the class to support Quartus.""" - - _TOOL = 'quartus' - _EXTENSION = 'qpf' - _PART = '10cl120zf780i8g' - _GEN_PROGRAM = 'quartus_sh' - _GEN_COMMAND = 'quartus_sh --script quartus.tcl' - _TRF_PROGRAM = 'quartus_pgm' - _TRF_COMMAND = 'quartus_pgm -c %s --mode jtag -o "p;%s@%s"' - _BIT_EXT = ['sof', 'pof'] - _DEVTYPES = ['fpga', 'detect'] - _CLEAN = [ - # directories - 'db', 'incremental_db', 'output_files', - # files - '*.done', '*.jdi', '*.log', '*.pin', '*.pof', '*.qws', '*.rpt', - '*.smsg', '*.sld', '*.sof', '*.sop', '*.summary', '*.txt', - # pyfpga - 'quartus.tcl' - ] - - def transfer(self, devtype, position, part, width, capture): - super().transfer(devtype, position, part, width, capture) - result = subprocess.run( - 'jtagconfig', shell=True, check=True, - stdout=subprocess.PIPE, universal_newlines=True - ) - result = result.stdout - if devtype == 'detect': - print(result) - else: - cable = re.match(r"1\) (.*) \[", result).groups()[0] - cmd = self._TRF_COMMAND % (cable, self.bitstream, position) - result = run(cmd, capture) - return result diff --git a/fpga/tool/template.sh b/fpga/tool/template.sh deleted file mode 100644 index c10dbdd6..00000000 --- a/fpga/tool/template.sh +++ /dev/null @@ -1,181 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2020 Rodrigo A. Melo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# This file implements an open-source flow based on ghdl, ghdl-yosys-plugin, -# yosys, nextpnr, icestorm and prjtrellis. -# - -set -e - -############################################################################### -# Things to tuneup -############################################################################### - -FRONTEND={frontend} -BACKEND={backend} -PROJECT={project} -FAMILY={family} -DEVICE={device} -PACKAGE={package} -TOP={top} - -PARAMS="{params}" -FLAGS="--std=08 -fsynopsys -fexplicit -frelaxed" -VHDLS="{vhdls}" -INCLUDES="{includes}" -VERILOGS="{verilogs}" -CONSTRAINTS="{constraints}" - -# taks = prj syn imp bit -TASKS="{tasks}" - -# -# Tools configuration -# - -OCI_ENGINE="{oci_engine}" - -CONT_GHDL="{cont_ghdl}" -CONT_YOSYS="{cont_yosys}" -CONT_NEXTPNR_ICE40="{cont_nextpnr_ice40}" -CONT_ICETIME="{cont_icetime}" -CONT_ICEPACK="{cont_icepack}" -CONT_NEXTPNR_ECP5="{cont_nextpnr_ecp5}" -CONT_ECPPACK="{cont_ecppack}" - -TOOL_GHDL="{tool_ghdl}" -TOOL_YOSYS="{tool_yosys}" -TOOL_NEXTPNR_ICE40="{tool_nextpnr_ice40}" -TOOL_ICETIME="{tool_icetime}" -TOOL_ICEPACK="{tool_icepack}" -TOOL_NEXTPNR_ECP5="{tool_nextpnr_ecp5}" -TOOL_ECPPACK="{tool_ecppack}" - -############################################################################### -# Support -############################################################################### - -MODULE= -[ -n "$VHDLS" ] && MODULE="-m ghdl" - -function print () {{ - tput setaf 6; echo ">>> PyFPGA ($1): $2"; tput sgr0; -}} - -############################################################################### -# Synthesis -############################################################################### - -if [[ $TASKS == *"syn"* ]]; then - -print "$FRONTEND" "running 'synthesis'" - -### GHDL - -if [[ $FRONTEND == "ghdl" ]]; then - -$OCI_ENGINE $CONT_GHDL /bin/bash -c " -$VHDLS -$TOOL_GHDL --synth $FLAGS $TOP -" > $PROJECT.vhdl - -fi - -### Yosys (with ghdl-yosys-plugin) - -if [[ $FRONTEND == "yosys" ]]; then - -SYNTH= -WRITE= -if [[ $BACKEND == "vivado" ]]; then - SYNTH="synth_xilinx -top $TOP -family $FAMILY" - WRITE="write_edif -pvector bra $PROJECT.edif" -elif [[ $BACKEND == "ise" ]]; then - SYNTH="synth_xilinx -top $TOP -family $FAMILY -ise" - WRITE="write_edif -pvector bra $PROJECT.edif" -elif [[ $BACKEND == "nextpnr" ]]; then - SYNTH="synth_$FAMILY -top $TOP -json $PROJECT.json" -elif [[ $BACKEND == "verilog-nosynth" ]]; then - WRITE="write_verilog $PROJECT.v" -else - SYNTH="synth -top $TOP" - WRITE="write_verilog $PROJECT.v" -fi - -$OCI_ENGINE $CONT_YOSYS /bin/bash -c " -$VHDLS -$TOOL_YOSYS -Q $MODULE -p ' -$INCLUDES; -$VERILOGS; -$PARAMS; -$SYNTH; -$WRITE -'" - -fi - -### - -fi - -############################################################################### -# Place and Route -############################################################################### - -if [[ $TASKS == *"imp"* ]]; then - -print "nextpnr-$FAMILY" "running 'implementation'" - -INPUT="--json $PROJECT.json" - -if [[ $FAMILY == "ice40" ]]; then - CONSTRAINT="--pcf $CONSTRAINTS" - OUTPUT="--asc $PROJECT.asc" - $OCI_ENGINE $CONT_NEXTPNR_ICE40 $TOOL_NEXTPNR_ICE40 \ - --$DEVICE --package $PACKAGE $CONSTRAINT $INPUT $OUTPUT - $OCI_ENGINE $CONT_ICETIME $TOOL_ICETIME \ - -d $DEVICE -mtr $PROJECT.rpt $PROJECT.asc -fi - -if [[ $FAMILY == "ecp5" ]]; then - CONSTRAINT="--lpf $CONSTRAINTS" - OUTPUT="--textcfg $PROJECT.config" - $OCI_ENGINE $CONT_NEXTPNR_ECP5 $TOOL_NEXTPNR_ECP5 \ - --$DEVICE --package $PACKAGE $CONSTRAINT $INPUT $OUTPUT -fi - -fi - -############################################################################### -# Bitstream generation -############################################################################### - -if [[ $TASKS == *"bit"* ]]; then - -if [[ $FAMILY == "ice40" ]]; then - print "icepack" "running 'bitstream generation'" - $OCI_ENGINE $CONT_ICEPACK $TOOL_ICEPACK \ - $PROJECT.asc $PROJECT.bit -fi - -if [[ $FAMILY == "ecp5" ]]; then - print "eccpack" "running 'bitstream generation'" - $OCI_ENGINE $CONT_ECPPACK $TOOL_ECPPACK \ - --svf $PROJECT.svf $PROJECT.config $PROJECT.bit -fi - -fi diff --git a/fpga/tool/template.tcl b/fpga/tool/template.tcl deleted file mode 100644 index 238f6891..00000000 --- a/fpga/tool/template.tcl +++ /dev/null @@ -1,502 +0,0 @@ -# -# PyFPGA Master Tcl -# -# Copyright (C) 2015-2020 INTI -# Copyright (C) 2015-2020 Rodrigo A. Melo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Description: Tcl script to create a new project and performs synthesis, -# implementation and bitstream generation. -# -# Supported TOOLs: ise, libero, quartus, vivado -# -# Notes: -# * fpga_ is used to avoid name collisions. -# * The 'in' operator was introduced by Tcl 8.5, but some Tools uses 8.4, -# so 'lsearch' is used to test if a value is in a list. -# - -# -# Things to tuneup (#SOMETHING#) for each project -# - -set TOOL #TOOL# -set PRESYNTH #PRESYNTH# -set PROJECT #PROJECT# -set PART #PART# -set FAMILY #FAMILY# -set DEVICE #DEVICE# -set PACKAGE #PACKAGE# -set SPEED #SPEED# -set TOP #TOP# -# TASKS = prj syn imp bit -set TASKS [list #TASKS#] - -set PARAMS [list #PARAMS#] - -proc fpga_files {} { -#FILES# -} - -proc fpga_commands { PHASE } { - fpga_print "setting commands for the phase '$PHASE'" - switch $PHASE { - "prefile" { -#PREFILE_CMDS# - } - "project" { -#PROJECT_CMDS# - } - "preflow" { -#PREFLOW_CMDS# - } - "postsyn" { -#POSTSYN_CMDS# - } - "postimp" { -#POSTIMP_CMDS# - } - "postbit" { -#POSTBIT_CMDS# - } - } -} - -# -# Procedures -# - -proc fpga_print { MSG } { - global TOOL - puts ">>> PyFPGA ($TOOL): $MSG" -} - -proc fpga_create { PROJECT } { - global TOOL - fpga_print "creating the project '$PROJECT'" - switch $TOOL { - "ise" { - if { [ file exists $PROJECT.xise ] } { file delete $PROJECT.xise } - project new $PROJECT.xise - } - "libero" { - if { [ file exists $PROJECT ] } { file delete -force -- $PROJECT } - new_project -name $PROJECT -location $PROJECT -hdl {VHDL} -family {SmartFusion2} - } - "quartus" { - package require ::quartus::project - project_new $PROJECT -overwrite - set_global_assignment -name NUM_PARALLEL_PROCESSORS ALL - } - "vivado" { create_project -force $PROJECT } - } -} - -proc fpga_open { PROJECT } { - global TOOL - fpga_print "opening the project '$PROJECT'" - switch $TOOL { - "ise" { project open $PROJECT.xise } - "libero" { - open_project $PROJECT/$PROJECT.prjx - } - "quartus" { - package require ::quartus::flow - project_open -force $PROJECT.qpf - } - "vivado" { open_project $PROJECT } - } -} - -proc fpga_close {} { - global TOOL - fpga_print "closing the project" - switch $TOOL { - "ise" { project close } - "libero" { close_project } - "quartus" { project_close } - "vivado" { close_project } - } -} - -proc fpga_part { PART } { - global TOOL FAMILY DEVICE PACKAGE SPEED - fpga_print "adding the part '$PART'" - switch $TOOL { - "ise" { - project set family $FAMILY - project set device $DEVICE - project set package $PACKAGE - project set speed $SPEED - } - "libero" { - set_device -family $FAMILY -die $DEVICE -package $PACKAGE -speed $SPEED - } - "quartus" { - set_global_assignment -name DEVICE $PART - } - "vivado" { - set_property "part" $PART [current_project] - } - } -} - -proc fpga_file {FILE {LIBRARY "work"}} { - global TOOL TOP - set message "adding the file '$FILE'" - if { $LIBRARY != "work" } { append message " (into the VHDL library '$LIBRARY')" } - fpga_print $message - regexp -nocase {\.(\w*)$} $FILE -> ext - if { $ext == "tcl" } { - source $FILE - return - } - switch $TOOL { - "ise" { - if {$ext == "xcf"} { - project set "Synthesis Constraints File" $FILE -process "Synthesize - XST" - } elseif { $LIBRARY != "work" } { - lib_vhdl new $LIBRARY - xfile add $FILE -lib_vhdl $LIBRARY - } else { - xfile add $FILE - } - } - "libero" { - global LIBERO_PLACE_CONSTRAINTS - global LIBERO_OTHER_CONSTRAINTS - if {$ext == "pdc"} { - create_links -io_pdc $FILE - append LIBERO_PLACE_CONSTRAINTS "-file $FILE " - } elseif {$ext == "sdc"} { - create_links -sdc $FILE - append LIBERO_PLACE_CONSTRAINTS "-file $FILE " - append LIBERO_OTHER_CONSTRAINTS "-file $FILE " - } else { - create_links -library $LIBRARY -hdl_source $FILE - build_design_hierarchy - } - } - "quartus" { - if {$ext == "v"} { - set TYPE VERILOG_FILE - } elseif {$ext == "sv"} { - set TYPE SYSTEMVERILOG_FILE - } elseif {$ext == "vhdl" || $ext == "vhd"} { - set TYPE VHDL_FILE - } elseif {$ext == "sdc"} { - set TYPE SDC_FILE - } else { - set TYPE SOURCE_FILE - } - if { $LIBRARY != "work" } { - set_global_assignment -name $TYPE $FILE -library $LIBRARY - } else { - set_global_assignment -name $TYPE $FILE - } - } - "vivado" { - if { $LIBRARY != "work" } { - add_files $FILE - set_property library $LIBRARY [get_files $FILE] - } else { - add_files $FILE - } - } - } -} - -proc fpga_include {PATH} { - global TOOL INCLUDED - lappend INCLUDED $PATH - fpga_print "setting '$PATH' as a search location" - switch $TOOL { - "ise" { - # Verilog Included Files are NOT added - project set "Verilog Include Directories" \ - [join $INCLUDED "|"] -process "Synthesize - XST" - } - "libero" { - # Verilog Included Files are ALSO added - # They must be specified after set_root (see fpga_top) - foreach FILE [glob -nocomplain $PATH/*.vh] { - create_links -hdl_source $FILE - } - build_design_hierarchy - } - "quartus" { - # Verilog Included Files are NOT added - foreach INCLUDE $INCLUDED { - set_global_assignment -name SEARCH_PATH $INCLUDE - } - } - "vivado" { - # Verilog Included Files are NOT added - set_property "include_dirs" $INCLUDED [current_fileset] - } - } -} - -proc fpga_design {FILE} { - global TOOL TOP INCLUDED - fpga_print "including the block design '$FILE'" - switch $TOOL { - "vivado" { - if { [info exists INCLUDED] && [llength $INCLUDED] > 0 } { - set_property "ip_repo_paths" $INCLUDED [get_filesets sources_1] - update_ip_catalog -rebuild - } - source $FILE - make_wrapper -force -files [get_files design_1.bd] -top -import - if { $TOP == "UNDEFINED"} { - set TOP design_1_wrapper - } - } - default { puts "UNSUPPORTED by '$TOOL'" } - } -} - -proc fpga_top { TOP } { - global TOOL - fpga_print "specifying the top level '$TOP'" - switch $TOOL { - "ise" { - project set top $TOP - } - "libero" { - set_root $TOP - # Verilog Included files - global INCLUDED PARAMS - set cmd "configure_tool -name {SYNTHESIZE} -params {SYNPLIFY_OPTIONS:" - if { [info exists INCLUDED] && [llength $INCLUDED] > 0 } { - # See /poc/include/libero.tcl for details - set PATHS "../../" - append PATHS [join $INCLUDED ";../../"] - append cmd "set_option -include_path \"$PATHS\"" - append cmd "\n" - } - foreach PARAM $PARAMS { - set assign [join $PARAM] - append cmd "set_option -hdl_param -set \"$assign\"" - append cmd "\n" - } - append cmd "}" - eval $cmd - # Constraints - # PDC is only used for PLACEROUTE. - # SDC is used by ALL (SYNTHESIZE, PLACEROUTE and VERIFYTIMING). - global LIBERO_PLACE_CONSTRAINTS - global LIBERO_OTHER_CONSTRAINTS - if { [info exists LIBERO_OTHER_CONSTRAINTS] } { - set cmd "organize_tool_files -tool {SYNTHESIZE} " - append cmd $LIBERO_OTHER_CONSTRAINTS - append cmd "-module $TOP -input_type {constraint}" - eval $cmd - set cmd "organize_tool_files -tool {VERIFYTIMING} " - append cmd $LIBERO_OTHER_CONSTRAINTS - append cmd "-module $TOP -input_type {constraint}" - eval $cmd - } - if { [info exists LIBERO_PLACE_CONSTRAINTS] } { - set cmd "organize_tool_files -tool {PLACEROUTE} " - append cmd $LIBERO_PLACE_CONSTRAINTS - append cmd "-module $TOP -input_type {constraint}" - eval $cmd - } - } - "quartus" { - set_global_assignment -name TOP_LEVEL_ENTITY $TOP - } - "vivado" { - set_property top $TOP [current_fileset] - } - } -} - -proc fpga_params {} { - global TOOL PARAMS - if { [llength $PARAMS] == 0 } { return } - fpga_print "setting generics/parameters" - switch $TOOL { - "ise" { - set assigns [list] - foreach PARAM $PARAMS { lappend assigns [join $PARAM "="] } - project set "Generics, Parameters" "[join $assigns]" -process "Synthesize - XST" - } - "libero" { - # They must be specified after set_root (see fpga_top) - } - "quartus" { - foreach PARAM $PARAMS { - eval "set_parameter -name $PARAM" - } - } - "vivado" { - set assigns [list] - foreach PARAM $PARAMS { lappend assigns [join $PARAM "="] } - set obj [get_filesets sources_1] - set_property "generic" "[join $assigns]" -objects $obj - } - } -} - -proc fpga_run_syn {} { - global TOOL PRESYNTH - fpga_print "running 'synthesis'" - switch $TOOL { - "ise" { - if { $PRESYNTH == "True" } { - project set top_level_module_type "EDIF" - } else { - project clean - process run "Synthesize" - if { [process get "Synthesize" status] == "errors" } { exit 2 } - } - } - "libero" { - run_tool -name {SYNTHESIZE} - } - "quartus" { - execute_module -tool map - } - "vivado" { - if { $PRESYNTH == "True" } { - set_property design_mode GateLvl [current_fileset] - } else { - reset_run synth_1 - launch_runs synth_1 - wait_on_run synth_1 - } - } - default { puts "UNSUPPORTED by '$TOOL'" } - } -} - -proc fpga_run_imp {} { - global TOOL PRESYNTH - fpga_print "running 'implementation'" - switch $TOOL { - "ise" { - process run "Translate" - if { [process get "Translate" status] == "errors" } { exit 2 } - process run "Map" - if { [process get "Map" status] == "errors" } { exit 2 } - process run "Place & Route" - if { [process get "Place & Route" status] == "errors" } { exit 2 } - } - "libero" { - run_tool -name {PLACEROUTE} - run_tool -name {VERIFYTIMING} - } - "quartus" { - execute_module -tool fit - execute_module -tool sta - } - "vivado" { - if {$PRESYNTH == "False"} { - open_run synth_1 - } - launch_runs impl_1 - wait_on_run impl_1 - } - default { puts "UNSUPPORTED by '$TOOL'" } - } -} - -proc fpga_run_bit {} { - global TOOL PROJECT TOP - fpga_print "running 'bitstream generation'" - switch $TOOL { - "ise" { - process run "Generate Programming File" - if { [process get "Generate Programming File" status] == "errors" } { exit 2 } - catch { file rename -force $TOP.bit $PROJECT.bit } - } - "libero" { - run_tool -name {GENERATEPROGRAMMINGFILE} - } - "quartus" { - execute_module -tool asm - } - "vivado" { - open_run impl_1 - write_bitstream -force $PROJECT - } - default { puts "UNSUPPORTED by '$TOOL'" } - } -} - -# -# Start of the script -# - -fpga_print "start of the Tcl script (interpreter $tcl_version)" - -# -# Project Creation -# - -if { [lsearch -exact $TASKS "prj"] >= 0 } { - fpga_print "running the Project Creation" - if { [catch { - fpga_create $PROJECT - fpga_part $PART - fpga_commands "prefile" - fpga_files - fpga_top $TOP - fpga_params - fpga_commands "project" - fpga_close - } ERRMSG]} { - puts "ERROR: there was a problem creating a New Project.\n" - puts $ERRMSG - exit 1 - } -} - -# -# Design Flow -# - -if { [lsearch -regexp $TASKS "syn|imp|bit"] >= 0 } { - fpga_print "running the Design Flow" - if { [catch { - fpga_open $PROJECT - fpga_commands "preflow" - if { [lsearch -exact $TASKS "syn"] >= 0 } { - fpga_run_syn - fpga_commands "postsyn" - } - if { [lsearch -exact $TASKS "imp"] >= 0 } { - fpga_run_imp - fpga_commands "postimp" - } - if { [lsearch -exact $TASKS "bit"] >= 0 } { - fpga_run_bit - fpga_commands "postbit" - } - fpga_close - } ERRMSG]} { - puts "ERROR: there was a problem running the Design Flow.\n" - puts $ERRMSG - exit 2 - } -} - -# -# End of the script -# - -fpga_print "end of the Tcl script" diff --git a/fpga/tool/vivado.py b/fpga/tool/vivado.py deleted file mode 100644 index 067644ed..00000000 --- a/fpga/tool/vivado.py +++ /dev/null @@ -1,97 +0,0 @@ -# -# Copyright (C) 2019-2020 INTI -# Copyright (C) 2019-2020 Rodrigo A. Melo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -"""fpga.tool.vivado - -Implements the support of Vivado (Xilinx). -""" - -from fpga.tool import Tool, run - -_TEMPLATES = { - 'fpga': """\ -if { [ catch { open_hw_manager } ] } { open_hw } -connect_hw_server -open_hw_target -set obj [lindex [get_hw_devices [current_hw_device]] 0] -set_property PROGRAM.FILE #BITSTREAM# $obj -program_hw_devices $obj -""", - 'detect': """\ -if { [ catch { open_hw_manager } ] } { open_hw } -connect_hw_server -open_hw_target -puts [get_hw_devices] -""" -} - - -class Vivado(Tool): - """Implementation of the class to support Vivado.""" - - _TOOL = 'vivado' - _EXTENSION = 'xpr' - _PART = 'xc7k160t-3-fbg484' - _GEN_PROGRAM = 'vivado' - _GEN_COMMAND = 'vivado -mode batch -notrace -quiet -source vivado.tcl' - _TRF_PROGRAM = 'vivado' - _TRF_COMMAND = 'vivado -mode batch -notrace -quiet -source vivado-prog.tcl' - _BIT_EXT = ['bit'] - _DEVTYPES = ['fpga', 'detect'] - _CLEAN = [ - # directories - '*.cache', '*.hw', '*.ip_user_files', '*.runs', '*.sim', '.Xil', - # files - '*.bit', '*.jou', '*.log', '*.rpt', 'vivado_*.zip', - # pyfpga - 'vivado.tcl', 'vivado-prog.tcl' - ] - - def __init__(self, project, frontend=None): - super().__init__(project) - if frontend == 'yosys': - from fpga.tool.openflow import Openflow - self.tool = Openflow( - self.project, - frontend='yosys', - backend='vivado' - ) - self.presynth = True - - def generate(self, to_task, from_task, capture): - if self.presynth and from_task in ['prj', 'syn']: - self.tool.set_part(self.part['name']) - self.tool.set_top(self.top) - self.tool.paths = self.paths - self.tool.files['vhdl'] = self.files['vhdl'] - self.tool.files['verilog'] = self.files['verilog'] - self.tool.params = self.params - output1 = self.tool.generate('syn', 'prj', capture) - self.set_top(self.project) - output2 = super().generate(to_task, from_task, capture) - return str(output1) + str(output2) - return super().generate(to_task, from_task, capture) - - def transfer(self, devtype, position, part, width, capture): - super().transfer(devtype, position, part, width, capture) - temp = _TEMPLATES[devtype] - if devtype != 'detect': - temp = temp.replace('#BITSTREAM#', self.bitstream) - with open('vivado-prog.tcl', 'w', encoding='utf-8') as file: - file.write(temp) - return run(self._TRF_COMMAND, capture) diff --git a/hdl/blinking.v b/hdl/blinking.v deleted file mode 100644 index f8bf669c..00000000 --- a/hdl/blinking.v +++ /dev/null @@ -1,28 +0,0 @@ -`include "freq.vh" -`include "secs.vh" - -module Blinking #( - parameter FREQ = `DEFAULT_FREQ, - parameter SECS = `DEFAULT_SECS -)( - input wire clk_i, - output wire led_o -); - -localparam DIV = FREQ*SECS; - -reg led; -reg [$clog2(DIV)-1:0] cnt = 0; - -always @(posedge clk_i) begin - if (cnt == DIV-1) begin - cnt = 0; - led <= ~led; - end else begin - cnt = cnt + 1; - end -end - -assign led_o = led; - -endmodule diff --git a/hdl/blinking.vhdl b/hdl/blinking.vhdl deleted file mode 100644 index a50fbbbc..00000000 --- a/hdl/blinking.vhdl +++ /dev/null @@ -1,33 +0,0 @@ -library IEEE; -use IEEE.std_logic_1164.all; - -entity Blinking is - generic ( - FREQ : positive:=25e6; - SECS : positive:=1 - ); - port ( - clk_i : in std_logic; - led_o : out std_logic - ); -end entity Blinking; - -architecture RTL of Blinking is - constant DIV : positive:=FREQ*SECS-1; - signal led : std_logic; -begin - blink: - process (clk_i) - variable cnt: natural range 0 to DIV:=0; - begin - if rising_edge(clk_i) then - if cnt=DIV then - cnt:=0; - led <= not(led); - else - cnt:=cnt+1; - end if; - end if; - end process blink; - led_o <= led; -end architecture RTL; diff --git a/hdl/data/memory.dat b/hdl/data/memory.dat deleted file mode 100644 index a7dda8d9..00000000 --- a/hdl/data/memory.dat +++ /dev/null @@ -1,64 +0,0 @@ -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 -00000000000000000000000000000000 diff --git a/hdl/examples_pkg.vhdl b/hdl/examples_pkg.vhdl deleted file mode 100644 index 3f15f041..00000000 --- a/hdl/examples_pkg.vhdl +++ /dev/null @@ -1,17 +0,0 @@ -library IEEE; -use IEEE.std_logic_1164.all; - -package Examples is - - component Blinking is - generic ( - FREQ : positive:=25e6; - SECS : positive:=1 - ); - port ( - clk_i : in std_logic; - led_o : out std_logic - ); - end component Blinking; - -end package Examples; diff --git a/hdl/fakes/generics.vhdl b/hdl/fakes/generics.vhdl deleted file mode 100644 index 4e010ebc..00000000 --- a/hdl/fakes/generics.vhdl +++ /dev/null @@ -1,41 +0,0 @@ -library IEEE; -use IEEE.std_logic_1164.all; -use IEEE.numeric_std.all; - -entity Params is - generic ( - BOO : boolean:=FALSE; - INT : integer:=0; - LOG : std_logic:='0'; - VEC : std_logic_vector(7 downto 0):="00000000"; - STR : string:="ABCD"; - REA : real:=0.0 - ); - port ( - boo_o : out std_logic; - int_o : out std_logic_vector(7 downto 0); - log_o : out std_logic; - vec_o : out std_logic_vector(7 downto 0); - str_o : out std_logic; - rea_o : out std_logic - ); -end entity Params; - -architecture RTL of Params is -begin - - assert BOO=True report "The boolean is not True" severity failure; - assert INT=255 report "The integer is not 255" severity failure; - assert LOG='1' report "The std_logic is not '1'" severity failure; - assert VEC="11111111" report "The std_logic_vector is not 11111111" severity failure; - assert STR="WXYZ" report "The string is not WXYZ" severity failure; - assert REA=1.1 report "The real is not 1.1" severity failure; - - boo_o <= '1' when BOO else '0'; - int_o <= std_logic_vector(to_unsigned(INT, 8)); - log_o <= LOG; - vec_o <= VEC; - str_o <= '1' when STR="WXYZ" else '0'; - rea_o <= '1' when REA=1.1 else '0'; - -end architecture RTL; diff --git a/hdl/fakes/parameters.v b/hdl/fakes/parameters.v deleted file mode 100644 index 9dd81c51..00000000 --- a/hdl/fakes/parameters.v +++ /dev/null @@ -1,51 +0,0 @@ -module Params #( - parameter BOO = 0, - parameter INT = 0, - parameter LOG = 1'b0, - parameter VEC = 8'd0, - parameter STR = "ABCD", - parameter REA = 0.0 -)( - output wire boo_o, - output wire [7:0] int_o, - output wire log_o, - output wire [7:0] vec_o, - output wire str_o, - output wire rea_o -); - - initial begin - if (BOO != 1) begin - $display("The boolean is not True"); - $finish; - end - if (INT != 255) begin - $display("The integer is not 255"); - $finish; - end - if (LOG != 1) begin - $display("The std_logic is not '1'"); - $finish; - end - if (VEC != 8'b11111111) begin - $display("The std_logic_vector is not 11111111"); - $finish; - end - if (STR != "WXYZ") begin - $display("The string is not WXYZ"); - $finish; - end - if (REA != 1.1) begin - $display("The real is not 1.1"); - $finish; - end - end - - assign boo_o = BOO; - assign int_o = INT; - assign log_o = LOG; - assign vec_o = VEC; - assign str_o = (STR=="WXYZ") ? 1'b1 : 1'b0; - assign rea_o = (REA==1.1) ? 1'b1 : 1'b0; - -endmodule diff --git a/hdl/fakes/top.v b/hdl/fakes/top.v deleted file mode 100644 index 3bc0b7c1..00000000 --- a/hdl/fakes/top.v +++ /dev/null @@ -1,10 +0,0 @@ -module Top1 (); -endmodule - -// module Top2 (); -// endmodule - -/* -module Top3 (); -endmodule -*/ diff --git a/hdl/fakes/top.vhdl b/hdl/fakes/top.vhdl deleted file mode 100644 index 01063ae8..00000000 --- a/hdl/fakes/top.vhdl +++ /dev/null @@ -1,5 +0,0 @@ -entity Top1 is -end entity Top1; - --- entity Top2 is --- end entity Top1; diff --git a/hdl/headers1/freq.vh b/hdl/headers1/freq.vh deleted file mode 100644 index 2b27b844..00000000 --- a/hdl/headers1/freq.vh +++ /dev/null @@ -1 +0,0 @@ -`define DEFAULT_FREQ 25000000 diff --git a/hdl/headers2/secs.vh b/hdl/headers2/secs.vh deleted file mode 100644 index aa252d2f..00000000 --- a/hdl/headers2/secs.vh +++ /dev/null @@ -1 +0,0 @@ -`define DEFAULT_SECS 1 diff --git a/hdl/ram.v b/hdl/ram.v deleted file mode 100644 index 35be5b06..00000000 --- a/hdl/ram.v +++ /dev/null @@ -1,21 +0,0 @@ -// A RAM initialized with an external file - -module ram ( - input clk_i, - input we_i, - input [5:0] addr_i, - input [31:0] data_i, - output reg [31:0] data_o -); - -reg [31:0] ram [0:63]; - -initial $readmemb("data/memory.dat",ram); - -always @(posedge clk_i) begin - if (we_i) - ram[addr_i] <= data_i; - data_o <= ram[addr_i]; -end - -endmodule diff --git a/hdl/ram.vhdl b/hdl/ram.vhdl deleted file mode 100644 index 36d68bd1..00000000 --- a/hdl/ram.vhdl +++ /dev/null @@ -1,45 +0,0 @@ --- A RAM initialized with an external file - -library IEEE; -use IEEE.std_logic_1164.all; -use IEEE.numeric_std.all; -use STD.textio.all; - -entity ram is - port( - clk_i : in std_logic; - we_i : in std_logic; - addr_i : in std_logic_vector(5 downto 0); - data_i : in std_logic_vector(31 downto 0); - data_o : out std_logic_vector(31 downto 0) - ); -end ram; - -architecture RTL of ram is - type mem_t is array (0 to 63) of bit_vector(31 downto 0); - - impure function init(filename : in string) return mem_t is - file fh : text is filename; - variable l : line; - variable mem : mem_t; - begin - for i in mem_t'range loop - readline(fh, l); - read(l, mem(i)); - end loop; - return mem; - end function; - - signal ram : mem_t := init("data/memory.dat"); -begin - memory: - process(clk_i) - begin - if rising_edge(clk_i) then - if we_i = '1' then - ram(to_integer(unsigned(addr_i))) <= to_bitvector(data_i); - end if; - data_o <= to_stdlogicvector(ram(to_integer(unsigned(addr_i)))); - end if; - end process; -end architecture RTL; diff --git a/hdl/top.v b/hdl/top.v deleted file mode 100644 index 787fbdbd..00000000 --- a/hdl/top.v +++ /dev/null @@ -1,11 +0,0 @@ -module Top ( - input wire clk_i, - output wire led_o -); - -localparam FREQ = 50000000; - -Blinking #(.FREQ (FREQ), .SECS (1)) - dut (.clk_i (clk_i), .led_o (led_o)); - -endmodule diff --git a/hdl/top.vhdl b/hdl/top.vhdl deleted file mode 100644 index 80919fd6..00000000 --- a/hdl/top.vhdl +++ /dev/null @@ -1,21 +0,0 @@ -library IEEE; -use IEEE.std_logic_1164.all; -library Examples; -use Examples.examples.all; - -entity Top is - port ( - clk_i : in std_logic; - led_o : out std_logic - ); -end entity Top; - -architecture Structural of Top is - constant FREQ : positive := 50e6; -begin - - dut: Blinking - generic map (FREQ => FREQ, SECS => 1) - port map (clk_i => clk_i, led_o => led_o); - -end architecture Structural; diff --git a/pyfpga/__init__.py b/pyfpga/__init__.py new file mode 100644 index 00000000..b9bae46e --- /dev/null +++ b/pyfpga/__init__.py @@ -0,0 +1,3 @@ +"""PyFPGA""" + +__version__ = '0.3.0-dev' diff --git a/pyfpga/diamond.py b/pyfpga/diamond.py new file mode 100644 index 00000000..01226a6d --- /dev/null +++ b/pyfpga/diamond.py @@ -0,0 +1,30 @@ +# +# Copyright (C) 2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +""" +Implements support for Diamond. +""" + +import os +from pyfpga.project import Project + + +class Diamond(Project): + """Class to support Diamond projects.""" + + def _configure(self): + tool = 'diamond' + executable = 'pnmainc' if os.name == 'nt' else 'diamondc' + self.conf['tool'] = tool + self.conf['make_cmd'] = f'{executable} {tool}.tcl' + self.conf['make_ext'] = 'tcl' + self.conf['prog_bit'] = ['bit'] + self.conf['prog_cmd'] = f'sh {tool}-prog.sh' + self.conf['prog_ext'] = 'sh' + + def _make_custom(self): + if 'part' not in self.data: + self.data['part'] = 'LFXP2-5E-5TN144C' diff --git a/pyfpga/factory.py b/pyfpga/factory.py new file mode 100644 index 00000000..6da4029a --- /dev/null +++ b/pyfpga/factory.py @@ -0,0 +1,42 @@ +# +# Copyright (C) 2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +""" +A factory class to create FPGA projects. +""" + +# pylint: disable=too-few-public-methods + +from pyfpga.diamond import Diamond +from pyfpga.ise import Ise +from pyfpga.libero import Libero +from pyfpga.openflow import Openflow +from pyfpga.quartus import Quartus +from pyfpga.vivado import Vivado + + +TOOLS = { + 'diamond': Diamond, + 'ise': Ise, + 'libero': Libero, + 'openflow': Openflow, + 'quartus': Quartus, + 'vivado': Vivado +} + + +class Factory: + """A factory class to create FPGA projects.""" + + def __init__(self, tool='vivado', project=None, odir='results'): + """Class constructor.""" + if tool not in TOOLS: + raise NotImplementedError(f'{tool} is unsupported') + self._instance = TOOLS[tool](project, odir) + + def __getattr__(self, name): + """Delegate attribute access to the tool instance.""" + return getattr(self._instance, name) diff --git a/pyfpga/helpers/bitprog.py b/pyfpga/helpers/bitprog.py new file mode 100644 index 00000000..6af85884 --- /dev/null +++ b/pyfpga/helpers/bitprog.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +""" +A CLI helper utility to transfer a bitstream to a supported device. +""" + +import argparse +import sys + +from pathlib import Path +from pyfpga import __version__ as version +from pyfpga.factory import Factory, TOOLS +from pyfpga.project import STEPS + +tools = list(TOOLS.keys()) +devs = ['fpga', 'spi', 'bpi'] +positions = range(1, 10) +widths = [2**i for i in range(6)] + +EPILOGUE = f""" +Supported values of arguments with choices: +* TOOL = {'|'.join(tools)} +* TYPE = {'|'.join(devs)} +* POSITION = {'|'.join(map(str, positions))} +* WIDTH = {'|'.join(map(str, widths))} +""" + + +def main(): + """Solves the main functionality of this helper.""" + + # Parsing the command-line. + + parser = argparse.ArgumentParser( + description=__doc__, + epilog=EPILOGUE, + formatter_class=argparse.RawDescriptionHelpFormatter + ) + + parser.add_argument( + '-v', '--version', + action='version', + version=f'v{version}' + ) + + parser.add_argument( + '-t', '--tool', + metavar='TOOL', + default='vivado', + choices=tools, + help='backend tool to be used [vivado]' + ) + + parser.add_argument( + '-o', '--odir', + metavar='PATH', + default='results', + help='where to generate files [results]' + ) + + parser.add_argument( + '-d', '--device', + metavar='TYPE', + choices=devs, + default=devs[0], + help=f'the target device type [{devs[0]}]' + ) + + parser.add_argument( + '-p', '--position', + metavar='POSITION', + choices=positions, + type=int, + default=1, + help='the device position into the JTAG chain [1]' + ) + + parser.add_argument( + '-m', '--memname', + metavar='NAME', + help='memory name for SPI or BPI devices [None]' + ) + + parser.add_argument( + '-w', '--width', + metavar='WIDTH', + choices=widths, + type=int, + default=1, + help='memory width for SPI or BPI devices [1]' + ) + + parser.add_argument( + 'bit', + metavar='BITFILE', + help='a bitstream file' + ) + + args = parser.parse_args() + + # ------------------------------------------------------------------------- + # Solving with PyFPGA + # ------------------------------------------------------------------------- + + prj = Factory(args.tool, odir=args.odir) + + try: + if args.device == 'fpga': + prj.prog(args.bit, args.position) + if args.device == 'spi': + prj.prog_spi(args.bit, args.position, args.width, args.memname) + if args.device == 'bpi': + prj.prog_bpi(args.bit, args.position, args.width, args.memname) + except Exception as e: + sys.exit('{} ({})'.format(type(e).__name__, e)) + + +if __name__ == "__main__": + main() diff --git a/pyfpga/helpers/hdl2bit.py b/pyfpga/helpers/hdl2bit.py new file mode 100644 index 00000000..13d36507 --- /dev/null +++ b/pyfpga/helpers/hdl2bit.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +""" +A CLI helper utility to transform HDL design files into a bitstream. +""" + +import argparse +import sys + +from pathlib import Path +from pyfpga import __version__ as version +from pyfpga.factory import Factory, TOOLS +from pyfpga.project import STEPS + +tools = list(TOOLS.keys()) +steps = list(STEPS.keys()) + +EPILOGUE = f""" +Supported values of arguments with choices: +* TOOL = {'|'.join(tools)} +* STEP = {'|'.join(steps)} + +Notes: +* PATH and FILE must be relative to the execution directory. +* The default PART name and how to specify it depends on the selected TOOL. +* More than one '--file', '--include' or '--param' arguments can be specified. +""" + + +def main(): + """Solves the main functionality of this helper.""" + + # Parsing the command-line. + + parser = argparse.ArgumentParser( + description=__doc__, + epilog=EPILOGUE, + formatter_class=argparse.RawDescriptionHelpFormatter + ) + + parser.add_argument( + '-v', '--version', + action='version', + version=f'v{version}' + ) + + parser.add_argument( + '-t', '--tool', + metavar='TOOL', + default='vivado', + choices=tools, + help='backend tool to be used [vivado]' + ) + + parser.add_argument( + '-o', '--odir', + metavar='PATH', + default='results', + help='where to generate files [results]' + ) + + parser.add_argument( + '-p', '--part', + metavar='PART', + help='the target device' + ) + + parser.add_argument( + '-f', '--file', + metavar='FILE[,LIBRARY]', + action='append', + help='add a design file (optionally specifying a VHDL library)' + ) + + parser.add_argument( + '-i', '--include', + metavar='PATH', + action='append', + help='specify a Verilog Include directory' + ) + + parser.add_argument( + '--define', + metavar=('DEFINE', 'VALUE'), + action='append', + nargs=2, + help='define and set the value of a Verilog Define' + ) + + parser.add_argument( + '--param', + metavar=('PARAMETER', 'VALUE'), + action='append', + nargs=2, + help='set the value of a Generic/Parameter of the top-level' + ) + + parser.add_argument( + '--project', + metavar='PROJECT', + help='optional PROJECT name' + ) + + parser.add_argument( + '--last', + metavar='STEP', + choices=steps, + default='bit', + help=f'last step to perform [{steps[-1]}] ({"|".join(steps)})' + ) + + parser.add_argument( + 'toplevel', + metavar='TOPLEVEL', + help='the top-level name' + ) + + args = parser.parse_args() + + # ------------------------------------------------------------------------- + # Solving with PyFPGA + # ------------------------------------------------------------------------- + + project = args.project or args.tool + + prj = Factory(args.tool, project, odir=args.odir) + + if args.part is not None: + prj.set_part(args.part) + + if args.include is not None: + for include in args.include: + prj.add_include(include) + + if args.file is not None: + for file in args.file: + aux = file.split(',') + file = aux[0] + lib = aux[1] if len(aux) > 1 else None + ext = Path(file).suffix.lower() + if ext == '.v': + print(f'* Adding Verilog file: {file}') + prj.add_vlog(file) + elif ext == '.sv': + print(f'* Adding System Verilog file: {file}') + prj.add_slog(file) + elif ext in ['.vhd', '.vhdl']: + if lib: + print(f'* Adding VHDL file: {file} (library={lib})') + else: + print(f'* Adding VHDL file: {file}') + prj.add_vhdl(file, lib) + else: + print(f'* Adding Constraint file: {file}') + prj.add_cons(file) + + if args.define is not None: + for define in args.define: + prj.add_define(define[0], define[1]) + + if args.param is not None: + for param in args.param: + prj.add_param(param[0], param[1]) + + prj.set_top(args.toplevel) + + try: + prj.make(last=args.last) + except Exception as e: + sys.exit('{} ({})'.format(type(e).__name__, e)) + + +if __name__ == "__main__": + main() diff --git a/pyfpga/helpers/prj2bit.py b/pyfpga/helpers/prj2bit.py new file mode 100644 index 00000000..ea633b6b --- /dev/null +++ b/pyfpga/helpers/prj2bit.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +""" +A CLI helper utility to deal with a vendor FPGA Project file. +""" + +import argparse +import sys + +from pathlib import Path +from pyfpga import __version__ as version +from pyfpga.factory import Factory, TOOLS +from pyfpga.project import STEPS + +steps = list(STEPS.keys())[1:len(STEPS)] + + +def main(): + """Solves the main functionality of this helper.""" + + # Parsing the command-line. + + parser = argparse.ArgumentParser( + description=__doc__ + ) + + parser.add_argument( + '-v', '--version', + action='version', + version=f'v{version}' + ) + + parser.add_argument( + '--last', + metavar='STEP', + choices=steps, + default='bit', + help=f'last step to perform [{steps[-1]}] ({"|".join(steps)})' + ) + + parser.add_argument( + 'prjfile', + metavar='PRJFILE', + help='a vendor Project File' + ) + + args = parser.parse_args() + + # Detecting a Project file + + tool_per_ext = { + '.xise': 'ise', + '.prjx': 'libero', + '.qpf': 'quartus', + '.xpr': 'vivado' + } + + prjfile = Path(args.prjfile) + + if not prjfile.exists(): + sys.exit(f'ERROR: {prjfile} file not found.') + + directory = prjfile.parent + base_name = prjfile.stem + extension = prjfile.suffix + + tool = '' + if extension in tool_per_ext: + tool = tool_per_ext[extension] + print(f'INFO: {tool} project file found.') + else: + sys.exit(f'ERROR: unknown project file extension ({extension})') + + # ------------------------------------------------------------------------- + # Solving with PyFPGA + # ------------------------------------------------------------------------- + + prj = Factory(tool, base_name, directory) + + try: + prj.make('syn', args.last) + except Exception as e: + sys.exit('{} ({})'.format(type(e).__name__, e)) + + +if __name__ == "__main__": + main() diff --git a/pyfpga/ise.py b/pyfpga/ise.py new file mode 100644 index 00000000..048b140b --- /dev/null +++ b/pyfpga/ise.py @@ -0,0 +1,96 @@ +# +# Copyright (C) 2019-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +""" +Implements support for ISE. +""" + +import re + +from pyfpga.project import Project + + +class Ise(Project): + """Class to support ISE projects.""" + + def _configure(self): + tool = 'ise' + self.conf['tool'] = tool + self.conf['make_cmd'] = f'xtclsh {tool}.tcl' + self.conf['make_ext'] = 'tcl' + self.conf['prog_bit'] = ['bit'] + self.conf['prog_cmd'] = f'impact -batch {tool}-prog.tcl' + self.conf['prog_ext'] = 'tcl' + + def _make_custom(self): + info = get_info(self.data.get('part', 'xc7k160t-3-fbg484')) + self.data['family'] = info['family'] + self.data['device'] = info['device'] + self.data['speed'] = info['speed'] + self.data['package'] = info['package'] + + def add_slog(self, pathname): + """Add System Verilog file/s.""" + raise NotImplementedError('ISE does not support SystemVerilog') + + +# pylint: disable=duplicate-code + +def get_info(part): + """Get info about the FPGA part. + + :param part: the FPGA part as specified by the tool + :returns: a dict with the keys family, device, speed and package + """ + part = part.lower().replace(' ', '') + # Looking for the family + family = None + families = { + r'xc7a\d+l': 'artix7l', + r'xc7a': 'artix7', + r'xc7k\d+l': 'kintex7l', + r'xc7k': 'kintex7', + r'xc3sd\d+a': 'spartan3adsp', + r'xc3s\d+a': 'spartan3a', + r'xc3s\d+e': 'spartan3e', + r'xc3s': 'spartan3', + r'xc6s\d+l': 'spartan6l', + r'xc6s': 'spartan6', + r'xc4v': 'virtex4', + r'xc5v': 'virtex5', + r'xc6v\d+l': 'virtex6l', + r'xc6v': 'virtex6', + r'xc7v\d+l': 'virtex7l', + r'xc7v': 'virtex7', + r'xc7z': 'zynq' + } + for key, value in families.items(): + if re.match(key, part): + family = value + break + # Looking for the other values + device = None + speed = None + package = None + aux = part.split('-') + if len(aux) == 3: + device = aux[0] + if len(aux[1]) < len(aux[2]): + speed = f'-{aux[1]}' + package = aux[2] + else: + speed = f'-{aux[2]}' + package = aux[1] + else: + valid = 'DEVICE-SPEED-PACKAGE or DEVICE-PACKAGE-SPEED' + raise ValueError(f'Invalid PART format ({valid})') + # Finish + return { + 'family': family, + 'device': device, + 'speed': speed, + 'package': package + } diff --git a/pyfpga/libero.py b/pyfpga/libero.py new file mode 100644 index 00000000..37c56650 --- /dev/null +++ b/pyfpga/libero.py @@ -0,0 +1,113 @@ +# +# Copyright (C) 2019-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +""" +Implements support for Libero. +""" + +import re + +from pyfpga.project import Project + + +class Libero(Project): + """Class to support Libero projects.""" + + def _configure(self): + tool = 'libero' + self.conf['tool'] = tool + self.conf['make_cmd'] = f'{tool} SCRIPT:{tool}.tcl' + self.conf['make_ext'] = 'tcl' + self.conf['prog_bit'] = ['ppd', 'stp', 'bit', 'jed'] + self.conf['prog_cmd'] = f'FPExpress SCRIPT:{tool}-prog.tcl' + self.conf['prog_ext'] = 'tcl' + + def _make_custom(self): + info = get_info(self.data.get('part', 'mpf100t-1-fcg484')) + self.data['family'] = info['family'] + self.data['device'] = info['device'] + self.data['speed'] = info['speed'] + self.data['package'] = info['package'] + self.data['prange'] = info['prange'] + + +# pylint: disable=duplicate-code + +def get_info(part): + """Get info about the FPGA part. + + :param part: the FPGA part as specified by the tool + :returns: a dict with the keys family, device, speed, package and prange + """ + part = part.lower().replace(' ', '') + # Looking for the family + family = None + families = { + r'm2s': 'SmartFusion2', + r'm2gl': 'IGLOO2', + r'rt4g': 'RTG4', + r'mpfs': 'PolarFireSoC', + r'mpf': 'PolarFire', + r'a2f': 'SmartFusion', + r'afs': 'Fusion', + r'aglp': 'IGLOO+', + r'agle': 'IGLOOE', + r'agl': 'IGLOO', + r'a3p\d+l': 'ProAsic3L', + r'a3pe': 'ProAsic3E', + r'a3p': 'ProAsic3' + } + for key, value in families.items(): + if re.match(key, part): + family = value + break + # Looking for the other values + device = None + speed = None + package = None + prange = None + aux = part.split('-') + if len(aux) == 2: + device = aux[0] + package = aux[1] + if package[0].isdigit(): + speed = f'-{package[0]}' + package = package[1:] + else: + speed = 'STD' + elif len(aux) == 3: + device = aux[0] + if len(aux[1]) < len(aux[2]): + speed = f'-{aux[1]}' + package = aux[2] + else: + speed = f'-{aux[2]}' + package = aux[1] + else: + valid = 'DEVICE-[SPEED][-]PACKAGE[PRANGE][-SPEED]' + raise ValueError(f'Invalid PART format ({valid})') + pranges = { + 'c': 'COM', + 'e': 'EXT', + 'i': 'IND', + 'm': 'MIL', + 't1': 'TGrade1', + 't2': 'TGrade2' + } + prange = 'COM' + for suffix, name in pranges.items(): + if package.endswith(suffix): + package = package[:-len(suffix)] + prange = name + break + # Finish + return { + 'family': family, + 'device': device, + 'speed': speed, + 'package': package, + 'prange': prange + } diff --git a/pyfpga/openflow.py b/pyfpga/openflow.py new file mode 100644 index 00000000..299abc7c --- /dev/null +++ b/pyfpga/openflow.py @@ -0,0 +1,92 @@ +# +# Copyright (C) 2020-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +""" +Implements support for an Open Source development flow. +""" + +from pyfpga.project import Project + + +class Openflow(Project): + """Class to support Open Source tools.""" + + def _configure(self): + tool = 'openflow' + self.conf['tool'] = tool + self.conf['make_cmd'] = f'bash {tool}.sh' + self.conf['make_ext'] = 'sh' + self.conf['prog_bit'] = ['svf', 'bit'] + self.conf['prog_cmd'] = f'bash {tool}-prog.sh' + self.conf['prog_ext'] = 'sh' + + def _make_custom(self): + info = get_info(self.data.get('part', 'hx8k-ct256')) + self.data['family'] = info['family'] + self.data['device'] = info['device'] + self.data['package'] = info['package'] + + def _prog_custom(self): + info = get_info(self.data.get('part', 'hx8k-ct256')) + self.data['family'] = info['family'] + + +def get_info(part): + """Get info about the FPGA part. + + :param part: the FPGA part as specified by the tool + :returns: a dict with the keys family, device and package + """ + part = part.lower().replace(' ', '') + # Looking for the family + family = None + families = [ + # From /techlibs/xilinx/synth_xilinx.cc + 'xcup', 'xcu', 'xc7', 'xc6s', 'xc6v', 'xc5v', 'xc4v', 'xc3sda', + 'xc3sa', 'xc3se', 'xc3s', 'xc2vp', 'xc2v', 'xcve', 'xcv' + ] + for item in families: + if part.startswith(item): + family = item + break + families = [ + # From /ice40/main.cc + 'lp384', 'lp1k', 'lp4k', 'lp8k', 'hx1k', 'hx4k', 'hx8k', + 'up3k', 'up5k', 'u1k', 'u2k', 'u4k' + ] + if part.startswith(tuple(families)): + family = 'ice40' + families = [ + # From /ecp5/main.cc + '12k', '25k', '45k', '85k', 'um-25k', 'um-45k', 'um-85k', + 'um5g-25k', 'um5g-45k', 'um5g-85k' + ] + if part.startswith(tuple(families)): + family = 'ecp5' + # Looking for the other values + device = None + package = None + aux = part.split('-') + if len(aux) == 2: + device = aux[0] + package = aux[1] + elif len(aux) == 3: + device = f'{aux[0]}-{aux[1]}' + package = aux[2] + else: + valid = 'DEVICE-PACKAGE' + raise ValueError(f'Invalid PART format ({valid})') + if family in ['lp4k', 'hx4k']: # See http://www.clifford.at/icestorm + device = device.replace('4', '8') + package += ":4k" + if family == 'ecp5': + package = package.upper() + # Finish + return { + 'family': family, + 'device': device, + 'package': package + } diff --git a/pyfpga/project.py b/pyfpga/project.py new file mode 100644 index 00000000..75eaec47 --- /dev/null +++ b/pyfpga/project.py @@ -0,0 +1,324 @@ +# +# Copyright (C) 2019-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +""" +Base class that implements agnostic methods to deal with FPGA projects. +""" + +import glob +import logging +import os +import subprocess + +from pathlib import Path +from time import time +from jinja2 import Environment, FileSystemLoader + + +STEPS = { + 'cfg': 'Project Creation', + 'syn': 'Synthesis', + 'par': 'Place and Route', + 'bit': 'Bitstream generation' +} + + +class Project: + """Base class to manage an FPGA project. + + :param project: project name (tool name when nothing specified) + :type project: str, optional + :param odir: output directory + :type odir: str, optional + """ + + def __init__(self, project=None, odir='results'): + """Class constructor.""" + self.conf = {} + self.data = {} + self._configure() + self.data['project'] = project or self.conf['tool'] + self.odir = odir + # logging config + self.logger = logging.getLogger(self.__class__.__name__) + if not self.logger.handlers: + self.logger.setLevel(logging.INFO) + handler = logging.StreamHandler() + formatter = logging.Formatter( + '%(asctime)s - %(levelname)s - %(message)s', + datefmt='%Y-%m-%d %H:%M:%S' + ) + handler.setFormatter(formatter) + self.logger.addHandler(handler) + + def set_part(self, name): + """Set the FPGA part name. + + :param name: FPGA part name + :type name: str + """ + self.logger.debug('Executing set_part: %s', name) + self.data['part'] = name + + def add_include(self, path): + """Add an Include path. + + Specify where to search for Included Verilog Files, IP repos, etc. + + :param path: path of a directory + :type name: str + :raises NotADirectoryError: if path is not a directory + """ + self.logger.debug('Executing add_include: %s', path) + path = Path(path).resolve() + if not path.is_dir(): + raise NotADirectoryError(path) + self.data.setdefault('includes', []).append(path.as_posix()) + + def _add_file(self, pathname, hdl=None, lib=None): + files = glob.glob(pathname, recursive=True) + if len(files) == 0: + raise FileNotFoundError(pathname) + for file in files: + path = Path(file).resolve().as_posix() + attr = {} + if hdl: + attr['hdl'] = hdl + if lib: + attr['lib'] = lib + if path in self.data.get('files', {}): + del self.data['files'][path] + self.data.setdefault('files', {})[path] = attr + + def add_slog(self, pathname): + """Add System Verilog file/s. + + :param pathname: path to a SV file (glob compliant) + :type pathname: str + :raises FileNotFoundError: when pathname is not found + """ + self.logger.debug('Executing add_slog: %s', pathname) + self._add_file(pathname, 'slog') + + def add_vhdl(self, pathname, lib=None): + """Add VHDL file/s. + + :param pathname: path to a SV file (glob compliant) + :type pathname: str + :param lib: VHDL library name + :type lib: str, optional + :raises FileNotFoundError: when pathname is not found + """ + lib_str = 'default library' if lib is None else lib + self.logger.debug('Executing add_vhdl: %s : %s', lib_str, pathname) + self._add_file(pathname, 'vhdl', lib) + + def add_vlog(self, pathname): + """Add Verilog file/s. + + :param pathname: path to a SV file (glob compliant) + :type pathname: str + :raises FileNotFoundError: when pathname is not found + """ + self.logger.debug('Executing add_vlog: %s', pathname) + self._add_file(pathname, 'vlog') + + def add_cons(self, path): + """Add a constraint file. + + :param pathname: path of a file + :type pathname: str + :raises FileNotFoundError: if path is not found + """ + self.logger.debug('Executing add_cons: %s', path) + path = Path(path).resolve() + if not path.is_file(): + raise FileNotFoundError(path) + attr = {} + self.data.setdefault('constraints', {})[path.as_posix()] = attr + + def add_param(self, name, value): + """Add a Parameter/Generic Value. + + :param name: parameter/generic name + :type name: str + :param value: parameter/generic value + :type name: str + """ + self.logger.debug('Executing add_param: %s : %s', name, value) + self.data.setdefault('params', {})[name] = value + + def add_define(self, name, value): + """Add a Verilog Defile Value. + + :param name: define name + :type name: str + :param value: define value + :type name: str + """ + self.logger.debug('Executing add_define: %s : %s', name, value) + self.data.setdefault('defines', {})[name] = value + + def add_fileset(self, pathname): + """Add fileset file/s. + + :param pathname: path to a fileset file + :type pathname: str + :raises FileNotFoundError: when pathname is not found + """ + self.logger.debug('Executing add_fileset: %s', pathname) + if not os.path.exists(pathname): + raise FileNotFoundError(pathname) + raise NotImplementedError() + + def set_top(self, name): + """Set the name of the top level component. + + :param name: top-level name + :type name: str + """ + self.logger.debug('Executing set_top: %s', name) + self.data['top'] = name + + def add_hook(self, stage, hook): + """Add a hook in the specific stage. + + A hook is a place that allows you to insert customized code. + + :param stage: where to insert the hook + :type stage: str + :param hook: a tool-specific command + :type hook: str + :raises ValueError: when stage is invalid + """ + self.logger.debug('Executing add_hook: %s : %s', stage, hook) + stages = [ + 'precfg', 'postcfg', 'presyn', 'postsyn', + 'prepar', 'postpar', 'prebit', 'postbit' + ] + if stage not in stages: + raise ValueError('Invalid stage.') + self.data.setdefault('hooks', {}).setdefault(stage, []).append(hook) + + def set_debug(self): + """Enables debug messages.""" + self.logger.setLevel(logging.DEBUG) + + def make(self, first='cfg', last='bit'): + """Run the underlying tool. + + :param first: first step + :type first: str, optional + :param last: last step + :type last: str, optional + :raises ValueError: for missing or wrong values + :raises RuntimeError: error running the needed underlying tool + + .. note:: valid steps are ``cfg``, ``syn``, ``par`` and ``bit``. + """ + self.logger.debug('Executing make') + if last not in STEPS: + raise ValueError('Invalid last step.') + if first not in STEPS: + raise ValueError('Invalid first step.') + keys = list(STEPS.keys()) + index = [keys.index(first), keys.index(last)] + if index[0] > index[1]: + raise ValueError('Invalid steps combination.') + message = f'from {STEPS[first]} to {STEPS[last]}' + if first == last: + message = STEPS[first] + self.logger.info('Running %s', message) + self.data['steps'] = keys[index[0]:index[1]+1] + self._make_custom() + self._create_file(self.conf['tool'], self.conf['make_ext']) + self._run(self.conf['make_cmd'], 'make.log') + + def prog(self, bitstream=None, position=1): + """Program the FPGA + + :param bitstream: bitstream to be programmed + :type bitstream: str, optional + :param position: position of the device in the JTAG chain + :type position: str, optional + :raises ValueError: for missing or wrong values + :raises FileNotFoundError: when bitstream is not found + :raises RuntimeError: error running the needed underlying tool + """ + self.logger.debug('Executing prog') + if position not in range(1, 9): + raise ValueError('Invalid position.') + self.logger.info('Programming') + if not bitstream: + for ext in self.conf['prog_bit']: + candidate = Path(self.odir) / f'{self.data["project"]}.{ext}' + if candidate.exists(): + bitstream = candidate.resolve() + break + else: + bitstream = Path(bitstream).resolve() + if not bitstream or not bitstream.exists(): + raise FileNotFoundError(bitstream) + self.data['bitstream'] = bitstream + self._prog_custom() + self._create_file(f'{self.conf["tool"]}-prog', self.conf['prog_ext']) + self._run(self.conf['prog_cmd'], 'prog.log') + + def _configure(self): + raise NotImplementedError('Tool-dependent') + + def _make_custom(self): + pass + + def _prog_custom(self): + pass + + def _create_file(self, basename, extension): + tempdir = Path(__file__).parent.joinpath('templates') + jinja_file_loader = FileSystemLoader(str(tempdir)) + jinja_env = Environment(loader=jinja_file_loader) + jinja_template = jinja_env.get_template(f'{basename}.jinja') + content = jinja_template.render(self.data) + directory = Path(self.odir) + directory.mkdir(parents=True, exist_ok=True) + filename = f'{basename}.{extension}' + with open(directory / filename, 'w', encoding='utf-8') as file: + file.write(content) + + def _run(self, command, logname): + num = 20 + error = 0 + old_dir = Path.cwd() + new_dir = Path(self.odir) + start = time() + try: + os.chdir(new_dir) + with open(logname, 'w', encoding='utf-8') as file: + subprocess.run( + command, shell=True, check=True, text=True, + stdout=file, stderr=subprocess.STDOUT + ) + except subprocess.CalledProcessError: + with open(logname, 'r', encoding='utf-8') as file: + lines = file.readlines() + last_lines = lines[-num:] if len(lines) >= num else lines + for line in last_lines: + message = line.strip() + if len(message): + print(f'>> {message}') + error = 1 + finally: + os.chdir(old_dir) + end = time() + elapsed = end - start + self.logger.info( + 'Elapsed time %dh %dm %.2fs', + int(elapsed // 3600), + int((elapsed % 3600) // 60), + elapsed % 60 + ) + if error: + raise RuntimeError('Problem with the underlying tool') diff --git a/pyfpga/quartus.py b/pyfpga/quartus.py new file mode 100644 index 00000000..747c6628 --- /dev/null +++ b/pyfpga/quartus.py @@ -0,0 +1,28 @@ +# +# Copyright (C) 2019-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +""" +Implements support for Quartus. +""" + +from pyfpga.project import Project + + +class Quartus(Project): + """Class to support Quartus projects.""" + + def _configure(self): + tool = 'quartus' + self.conf['tool'] = tool + self.conf['make_cmd'] = f'quartus_sh --script {tool}.tcl' + self.conf['make_ext'] = 'tcl' + self.conf['prog_bit'] = ['sof', 'pof'] + self.conf['prog_cmd'] = f'bash {tool}-prog.tcl' + self.conf['prog_ext'] = 'tcl' + + def _make_custom(self): + if 'part' not in self.data: + self.data['part'] = '10M50SCE144I7G' diff --git a/pyfpga/templates/diamond-prog.jinja b/pyfpga/templates/diamond-prog.jinja new file mode 100644 index 00000000..e1a1e57c --- /dev/null +++ b/pyfpga/templates/diamond-prog.jinja @@ -0,0 +1,19 @@ +{# +# Copyright (C) 2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +#} + +if [ "$DIAMOND_XCF" == "" ]; then + DIAMOND_XCF=impl1/impl1.xcf +fi + +if [ -f "$DIAMOND_XCF" ]; then + pgrcmd -infile $DIAMOND_XCF +else + echo "ERROR: Automatic programming with Diamond is not yet supported." + echo " Please create the `realpath $DIAMOND_XCF` file manually and rerun the prog command." + echo " Hint: You can change the location of the XCF file by setting the DIAMOND_XCF environment variable." + exit 1 +fi diff --git a/pyfpga/templates/diamond.jinja b/pyfpga/templates/diamond.jinja new file mode 100644 index 00000000..175be04d --- /dev/null +++ b/pyfpga/templates/diamond.jinja @@ -0,0 +1,105 @@ +{# +# +# Copyright (C) 2015-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +#} + +{% if 'cfg' in steps %}# Project configuration ------------------------------------------------------- + +prj_project new -name {{ project }} -dev {{ part }} + +# For now, let's enforce Synplify as LSE (the default) has broken top level generic handling +prj_syn set synplify + +{% if hooks %}{{ hooks.precfg | join('\n') }}{% endif %} + +{% if files %}# Files inclusion +{% for name, attr in files.items() %} +prj_src add {% if 'lib' in attr %}-work {{ attr.lib }}{% else %}{% endif %} {{ name }} +{% endfor %} +{% endif %} + +{% if constraints %} +# Constraints inclusion +# Diamond only supports one constraints file, so we need to combine them into the default diamond.lpf. +# We can't just do `prj_src add ` multiple times. +set fileId [open {{ project }}.lpf "w"] +{% for name, attr in constraints.items() %} +set fp [open "{{ name }}" r] +set file_data [read $fp] +close $fp +puts -nonewline $fileId $file_data +{% endfor %} +close $fileId +{% endif %} + +{% if top %}# Top-level specification +prj_impl option top "{{ top }}" +{% endif %} + +{% if includes %}# Verilog Includes +{% for include in includes %} +prj_impl option -append {include path} {{ "{"+include+"}" }} +{% endfor %} +{% endif %} + +{% if defines %}# Verilog Defines +{% for key, value in defines.items() %} +prj_impl option -append VERILOG_DIRECTIVES {{ key }}={{ value }} +{% endfor %} +{% endif %} + +{% if params %}# Verilog Parameters / VHDL Generics +{% for key, value in params.items() %} +prj_impl option -append HDL_PARAM {{ key }}={{ value }} +{% endfor %} +{% endif %} + +{% if hooks %}{{ hooks.postcfg | join('\n') }}{% endif %} + +prj_project save +prj_project close + +{% endif %} + +{% if 'syn' in steps or 'par' in steps or 'bit' in steps %}# Design flow ----------------------------------------------------------------- + +prj_project open {{ project }}.ldf + +{% if 'syn' in steps %}# Synthesis + +{% if hooks %}{{ hooks.presyn | join('\n') }}{% endif %} + +prj_run Synthesis -forceOne + +{% if hooks %}{{ hooks.postsyn | join('\n') }}{% endif %} + +{% endif %} + +{% if 'par' in steps %} # Translate, Map, and Place and Route +{% if hooks %}{{ hooks.prepar | join('\n') }}{% endif %} + +prj_run Translate -forceOne +prj_run Map -forceOne +prj_run PAR -forceOne + +{% if hooks %}{{ hooks.postpar | join('\n') }}{% endif %} + +{% endif %} + +{% if 'bit' in steps %}# Bitstream generation + +{% if hooks %}{{ hooks.prebit | join('\n') }}{% endif %} + +prj_run Export -task Bitgen -forceOne + +{% if hooks %}{{ hooks.postbit | join('\n') }}{% endif %} + +{% endif %} + +prj_project save +prj_project close + +{% endif %} diff --git a/pyfpga/templates/ise-prog.jinja b/pyfpga/templates/ise-prog.jinja new file mode 100644 index 00000000..abf4cb54 --- /dev/null +++ b/pyfpga/templates/ise-prog.jinja @@ -0,0 +1,62 @@ +{# +# +# Copyright (C) 2015-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +#} + +cleancablelock + +{# ------------------------------------------------------------------------- #} + +{% if fpga %} +setMode -bs +setCable -port auto +Identify -inferir +assignFile -p {{ position }} -file {{ bitstream }} +Program -p {{ position }} +{% endif %} + +{# ------------------------------------------------------------------------- #} + +{% if spi %} +setMode -pff +addConfigDevice -name {{ name }} -path . +setSubmode -pffspi +addDesign -version 0 -name 0 +addDeviceChain -index 0 +addDevice -p 1 -file {{ bitstream }} +generate -generic + +setMode -bs +setCable -port auto +Identify +attachflash -position {{ position }} -spi {{ name }} +assignfiletoattachedflash -position {{ position }} -file ./{{ name }}.mcs +Program -p {{ position }} -dataWidth {{ width }} -spionly -e -v -loadfpga +{% endif %} + +{# ------------------------------------------------------------------------- #} + +{% if bpi %} +setMode -pff +addConfigDevice -name {{ name }} -path . +setSubmode -pffbpi +addDesign -version 0 -name 0 +addDeviceChain -index 0 +setAttribute -configdevice -attr flashDataWidth -value {{ width }} +addDevice -p 1 -file {{ bitstream }} +generate -generic + +setMode -bs +setCable -port auto +Identify +attachflash -position {{ position }} -bpi {{ name }} +assignfiletoattachedflash -position {{ position }} -file ./{{ name }}.mcs +Program -p {{ position }} -dataWidth {{ width }} -rs1 NONE -rs0 NONE -bpionly -e -v -loadfpga +{% endif %} + +{# ------------------------------------------------------------------------- #} + +quit diff --git a/pyfpga/templates/ise.jinja b/pyfpga/templates/ise.jinja new file mode 100644 index 00000000..34577302 --- /dev/null +++ b/pyfpga/templates/ise.jinja @@ -0,0 +1,105 @@ +{# +# +# Copyright (C) 2015-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +#} + +{% if 'cfg' in steps %}# Project configuration ------------------------------------------------------- + +if { [ file exists {{ project }}.xise ] } { file delete {{ project }}.xise } +project new {{ project }}.xise +project set family {{ family }} +project set device {{ device }} +project set package {{ package }} +project set speed {{ speed }} + +{% if hooks %}{{ hooks.precfg | join('\n') }}{% endif %} + +{% if files %}# Files inclusion +{% for name, attr in files.items() %} +{% if 'lib' in attr %}lib_vhdl new {{ attr.lib }}{% endif %} +xfile add {{ name }}{% if 'lib' in attr %} -lib_vhdl {{ attr.lib }}{% endif %} +{% endfor %} +{% endif %} + +{% if constraints %}# Constraints inclusion +{% for name, attr in constraints.items() %} +xfile add {{ name }} +{% if name.endswith('.xcf') %} +project set "Synthesis Constraints File" "{{ name }}" -process "Synthesize - XST" +{% endif %} +{% endfor %} +{% endif %} + +{% if top %}# Top-level specification +project set top {{ top }} +{% endif %} + +{% if includes %}# Verilog Includes +project set "Verilog Include Directories" "{{ includes | join('|') }}" -process "Synthesize - XST" +{% endif %} + +{% if defines %}# Verilog Defines +project set "Verilog Macros" "{{ defines.items() | map('join', '=') | join(' | ') }}" -process "Synthesize - XST" +{% endif %} + +{% if params %}# Verilog Parameters / VHDL Generics +project set "Generics, Parameters" "{{ params.items() | map('join', '=') | join(' ') }}" -process "Synthesize - XST" +{% endif %} + +{% if hooks %}{{ hooks.postcfg | join('\n') }}{% endif %} + +project close + +{% endif %} + +{% if 'syn' in steps or 'par' in steps or 'bit' in steps %}# Design flow ----------------------------------------------------------------- + +project open {{ project }}.xise + +{% if 'syn' in steps %}# Synthesis + +{% if hooks %}{{ hooks.presyn | join('\n') }}{% endif %} + +# PRESYNTH +#project set top_level_module_type "EDIF" +project clean +process run "Synthesize" +if { [process get "Synthesize" status] == "errors" } { exit 1 } + +{% if hooks %}{{ hooks.postsyn | join('\n') }}{% endif %} + +{% endif %} + +{% if 'par' in steps %}# Place and Route + +{% if hooks %}{{ hooks.prepar | join('\n') }}{% endif %} + +process run "Translate" +if { [process get "Translate" status] == "errors" } { exit 1 } +process run "Map" +if { [process get "Map" status] == "errors" } { exit 1 } +process run "Place & Route" +if { [process get "Place & Route" status] == "errors" } { exit 1 } + +{% if hooks %}{{ hooks.postpar | join('\n') }}{% endif %} + +{% endif %} + +{% if 'bit' in steps %}# Bitstream generation + +{% if hooks %}{{ hooks.prebit | join('\n') }}{% endif %} + +process run "Generate Programming File" +if { [process get "Generate Programming File" status] == "errors" } { exit 1 } +catch { file copy -force {{ top }}.bit {{ project }}.bit } + +{% if hooks %}{{ hooks.postbit | join('\n') }}{% endif %} + +{% endif %} + +project close + +{% endif %} diff --git a/pyfpga/templates/libero-prog.jinja b/pyfpga/templates/libero-prog.jinja new file mode 100644 index 00000000..3dba4fd8 --- /dev/null +++ b/pyfpga/templates/libero-prog.jinja @@ -0,0 +1,15 @@ +{# +# +# Copyright (C) 2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +#} + +file delete -force -- libero-prog +new_project -name libero -location libero-prog -mode single + +set_programming_file -file {{ bitstream }} +set_programming_action -action {PROGRAM} + +run_selected_actions diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja new file mode 100644 index 00000000..71b9afc1 --- /dev/null +++ b/pyfpga/templates/libero.jinja @@ -0,0 +1,124 @@ +{# +# +# Copyright (C) 2015-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +#} + +{% if 'cfg' in steps %}# Project configuration ------------------------------------------------------- + +if { [ file exists {{ project }} ] } { file delete -force -- {{ project }} } +new_project -name {{ project }} -location libero -hdl VERILOG -family {{ family }} +set_device -family {{ family }} -die {{ device }} -package {{ package}} -speed {{ speed }} -part_range {{ prange }} + +{% if hooks %}{{ hooks.precfg | join('\n') }}{% endif %} + +{% if includes %}# Verilog Includes +set_global_include_path_order -paths "{{ includes | join(' ') }}" +{% endif %} + +{% if files %}# Files inclusion +{% for name, attr in files.items() %} +create_links -hdl_source {{ name }}{% if 'lib' in attr %} -library {{ attr.lib }}{% endif %} +{% endfor %} +{% endif %} + +{% if constraints %}# Constraints inclusion +{% for name, attr in constraints.items() %} +create_links {% if name.endswith('.sdc') %}-sdc{% else %}-io_pdc{% endif %} {{ name }} +{% endfor %} +{% endif %} + +build_design_hierarchy + +{% if top %}# Top-level specification +set_root {{ top }} +{% endif %} + +{% if constraints %}# Constraints configuration +{% set sdc_files = [] %} +{% set pdc_files = [] %} +{% for name, attr in constraints.items() %} +{% if name.endswith('.sdc') %} +{% set _ = sdc_files.append(name) %} +{% endif %} +{% set _ = pdc_files.append(name) %} +{% endfor %} +{% endif %} + +{% if sdc_files %}organize_tool_files -tool {SYNTHESIZE} -file {{ sdc_files | join(' -file ') }} -module {{ top }} -input_type {constraint}{% endif %} +{% if pdc_files %}organize_tool_files -tool {PLACEROUTE} -file {{ pdc_files | join(' -file ') }} -module {{ top }} -input_type {constraint}{% endif %} +{% if sdc_files %}organize_tool_files -tool {VERIFYTIMING} -file {{ sdc_files | join(' -file ') }} -module {{ top }} -input_type {constraint}{% endif %} + +{% if includes or defines or params %}# Synopsys configuration +configure_tool -name {SYNTHESIZE} -params {SYNPLIFY_OPTIONS: + +{% if includes %}# Verilog Includes (Synopsys) +{% for include in includes %} + set_option -include_path "{{ include }}" +{% endfor %} +{% endif %} + +{% if defines %}# Verilog Defines (Synopsys) +{% for key, value in defines.items() %} + set_option -hdl_define -set {{ key }}={{ value }} +{% endfor %} +{% endif %} + +{% if params %}# Verilog Parameters / VHDL Generics (Synopsys) +{% for key, value in params.items() %} + set_option -hdl_param -set {{ key }}={{ value }} +{% endfor %} +{% endif %} + +} +{% endif %} + +{% if hooks %}{{ hooks.postcfg | join('\n') }}{% endif %} + +close_project + +{% endif %} + +{% if 'syn' in steps or 'par' in steps or 'bit' in steps %}# Design flow ----------------------------------------------------------------- + +if { [catch {open_project {{ project }}/{{ project }}.prjx} ] } { + open_project {{ project }}.prjx +} + +{% if 'syn' in steps %}# Synthesis + +{% if hooks %}{{ hooks.presyn | join('\n') }}{% endif %} + +run_tool -name {SYNTHESIZE} + +{% if hooks %}{{ hooks.postsyn | join('\n') }}{% endif %} + +{% endif %} + +{% if 'par' in steps %}# Place and Route + +{% if hooks %}{{ hooks.prepar | join('\n') }}{% endif %} + +run_tool -name {PLACEROUTE} +run_tool -name {VERIFYTIMING} + +{% if hooks %}{{ hooks.postpar | join('\n') }}{% endif %} + +{% endif %} + +{% if 'bit' in steps %}# Bitstream generation + +{% if hooks %}{{ hooks.prebit | join('\n') }}{% endif %} + +run_tool -name {GENERATEPROGRAMMINGFILE} +catch { file copy -force {{ project }}/designer/{{ top }}/{{ top }}.ppd {{ project }}.ppd } + +{% if hooks %}{{ hooks.postbit | join('\n') }}{% endif %} + +{% endif %} + +close_project + +{% endif %} diff --git a/pyfpga/templates/openflow-prog.jinja b/pyfpga/templates/openflow-prog.jinja new file mode 100644 index 00000000..b26de159 --- /dev/null +++ b/pyfpga/templates/openflow-prog.jinja @@ -0,0 +1,17 @@ +{# +# +# Copyright (C) 2020-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +#} + +set -e + +DOCKER="docker run --user $(id -u):$(id -g) --rm -v $HOME:$HOME -w $PWD" + +{% if family == 'ecp5' %} +$DOCKER --device /dev/bus/usb hdlc/prog openocd -f /usr/share/trellis/misc/openocd/ecp5-evn.cfg -c "transport select jtag; init; svf {{ bitstream }}; exit" +{% else %} +$DOCKER --device /dev/bus/usb hdlc/prog iceprog {{ bitstream }} +{% endif %} diff --git a/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja new file mode 100644 index 00000000..79977719 --- /dev/null +++ b/pyfpga/templates/openflow.jinja @@ -0,0 +1,126 @@ +{# +# +# Copyright (C) 2020-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +#} + +set -e + +DOCKER="docker run --user $(id -u):$(id -g) --rm -v $HOME:$HOME -w $PWD" + +{% if 'syn' in steps %}# Synthesis +$DOCKER hdlc/ghdl:yosys /bin/bash -c " +{% if hooks %}{{ hooks.presyn | join('\n') }}{% endif %} + +{% set gflags = '--std=08 -fsynopsys -fexplicit -frelaxed' %} +{% if files %}# VHDL Files inclusion +{% for name, attr in files.items() %} +{% if attr.hdl == "vhdl" %} +ghdl -a {{ gflags }}{% if 'lib' in attr %} --work={{ attr.lib }}{% endif %} {{ name }} +{% endif %} +{% endfor %} +{% endif %} + +yosys -Q -m ghdl -p ' + +{% if includes %}# Verilog Includes +verilog_defaults -add{% for path in includes %} -I{{ path }}{% endfor %} +{% endif %} + +{% if defines %}# Verilog Defines +verilog_defines{% for key, value in defines.items() %} -D{{ key }}={{ value }}{% endfor %} +{% endif %} + +{% if files %}# VLOG Files inclusion +{% for name, attr in files.items() %} +{% if attr.hdl == "vlog" %} +read_verilog -defer {{ name }} +{% elif attr.hdl == "slog" %} +read_verilog -defer -sv {{ name }} +{% elif attr.hdl == "vhdl" %} +{% if loop.first %} +{% if params %}# VHDL Generics +ghdl {{ gflags }}{% for key, value in params.items() %} -g{{ key }}={{ value }}{% endfor %} {{ top }} +{% else %} +ghdl {{ gflags }} {{ top }} +{% endif %} +{% endif %} +{% endif %} +{% if loop.last and attr.hdl in ["vlog", "slog"] and params %}# Verilog Parameters +chparam{% for key, value in params.items() %} -set {{ key }} {{ value }}{% endfor %} +{% endif %} +{% endfor %} +{% endif %} + +# Top-level specification and Syntesis +{% if family in ['ice40', 'ecp5'] %} +synth_{{ family }} -top {{ top }} -json {{ project }}.json +{% elif family in ['xc6s', 'xc6v', 'xc5v', 'xc4v', 'xc3sda', 'xc3sa', 'xc3se', 'xc3s', 'xc2vp', 'xc2v', 'xcve', 'xcv'] %} +synth_xilinx -top {{ top }} -family {{ family }} +write_edif -pvector bra {{ project }}.edif -ise +{% elif family %} +synth_xilinx -top {{ top }} -family {{ family }} +write_edif -pvector bra {{ project }}.edif +{% else %} +synth -top {{ top }} +write_verilog {{ project }}.v +{% endif %} +' +{% if hooks %}{{ hooks.postsyn | join('\n') }}{% endif %} +" +{% endif %} + +{% if 'par' in steps %}# Place and Route + +CONSTRAINTS="{{ constraints | join(' ') }}" + +{% if family == 'ice40' %} +{% if constraints %} +cat $CONSTRAINTS > {{ project }}.pcf +CONSTRAINT="--pcf {{ project }}.pcf" +{% endif %} +$DOCKER hdlc/nextpnr:ice40 /bin/bash -c " +{% if hooks %}{{ hooks.prepar | join('\n') }}{% endif %} +nextpnr-ice40 --{{ device }} --package {{ package }} $CONSTRAINT --json {{ project }}.json --asc {{ project }}.asc +{% if hooks %}{{ hooks.postpar | join('\n') }}{% endif %} +" +$DOCKER hdlc/icestorm /bin/bash -c " +icetime -d {{ device }} -mtr {{ project }}.rpt {{ project }}.asc +" +{% endif %} + +{% if family == 'ecp5' %} +{% if constraints %} +cat $CONSTRAINTS > {{ project }}.lpf +CONSTRAINT="--lpf {{ project }}.lpf" +{% endif %} +$DOCKER hdlc/nextpnr:ecp5 /bin/bash -c " +{% if hooks %}{{ hooks.prepar | join('\n') }}{% endif %} +nextpnr-ecp5 --{{ device }} --package {{ package }} $CONSTRAINT --json {{ project }}.json --textcfg {{ project }}.config +{% if hooks %}{{ hooks.postpar | join('\n') }}{% endif %} +" +{% endif %} + +{% endif %} + +{% if 'bit' in steps %}# Bitstream generation + +{% if family == 'ice40' %} +$DOCKER hdlc/icestorm /bin/bash -c " +{% if hooks %}{{ hooks.prebit | join('\n') }}{% endif %} +icepack {{ project }}.asc {{ project }}.bit +{% if hooks %}{{ hooks.postbit | join('\n') }}{% endif %} +" +{% endif %} + +{% if family == 'ecp5' %} +$DOCKER hdlc/prjtrellis /bin/bash -c " +{% if hooks %}{{ prebit | join('\n') }}{% endif %} +ecppack --svf {{ project }}.svf {{ project }}.config {{ project }}.bit +{% if hooks %}{{ postbit | join('\n') }}{% endif %} +" +{% endif %} + +{% endif %} diff --git a/pyfpga/templates/quartus-prog.jinja b/pyfpga/templates/quartus-prog.jinja new file mode 100644 index 00000000..0d7af942 --- /dev/null +++ b/pyfpga/templates/quartus-prog.jinja @@ -0,0 +1,15 @@ +{# +# +# Copyright (C) 2015-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +#} + +RESULT=$(jtagconfig) +echo "$RESULT" + +# cable = re.match(r"1\) (.*) \[", result).groups()[0] +CABLE=$(echo "$RESULT" | awk -F '1\\) | \\[' '/1\)/ {print $2}') + +quartus_pgm -c $CABLE --mode jtag -o "p;{{ bitstream }}@{{ position }}" diff --git a/pyfpga/templates/quartus.jinja b/pyfpga/templates/quartus.jinja new file mode 100644 index 00000000..5013e1f8 --- /dev/null +++ b/pyfpga/templates/quartus.jinja @@ -0,0 +1,106 @@ +{# +# +# Copyright (C) 2015-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +#} + +{% if 'cfg' in steps %}# Project configuration ------------------------------------------------------- + +package require ::quartus::project +project_new {{ project }} -overwrite +set_global_assignment -name DEVICE {{ part }} + +{% if hooks %}{{ hooks.precfg | join('\n') }}{% endif %} + +{% if files %}# Files inclusion +{% for name, attr in files.items() %} +{% if attr.hdl == "vhdl" %} +set_global_assignment -name VHDL_FILE {{ name }} {% if 'lib' in attr %} -library {{ attr.lib }}{% endif %} +{% elif attr.hdl == "vlog" %} +set_global_assignment -name VERILOG_FILE {{ name }} +{% elif attr.hdl == "slog" %} +set_global_assignment -name SYSTEMVERILOG_FILE {{ name }} +{% endif %} +{% endfor %} +{% endif %} + +{% if constraints %}# Constraints inclusion +{% for name, attr in constraints.items() %} +{% if name.endswith('.sdc') %} +set_global_assignment -name SDC_FILE {{ name }} +{% else %} +source {{ name }} +{% endif %} +{% endfor %} +{% endif %} + +{% if top %}# Top-level specification +set_global_assignment -name TOP_LEVEL_ENTITY {{ top }} +{% endif %} + +{% if includes %}# Verilog Includes +{% for include in includes %} +set_global_assignment -name SEARCH_PATH {{ include }} +{% endfor %} +{% endif %} + +{% if defines %}# Verilog Defines +{% for key, value in defines.items() %} +set_global_assignment -name VERILOG_MACRO {{ key }}={{ value }} +{% endfor %} +{% endif %} + +{% if params %}# Verilog Parameters / VHDL Generics +{% for key, value in params.items() %} +set_parameter -name {{ key }} {{ value }} +{% endfor %} +{% endif %} + +{% if hooks %}{{ hooks.postcfg | join('\n') }}{% endif %} + +project_close + +{% endif %} + +{% if 'syn' in steps or 'par' in steps or 'bit' in steps %}# Design flow ----------------------------------------------------------------- + +package require ::quartus::flow +project_open -force {{ project }}.qpf +# set_global_assignment -name NUM_PARALLEL_PROCESSORS ALL + +{% if 'syn' in steps %}# Synthesis + +{% if hooks %}{{ hooks.presyn | join('\n') }}{% endif %} + +execute_module -tool map + +{% if hooks %}{{ hooks.postsyn | join('\n') }}{% endif %} + +{% endif %} + +{% if 'par' in steps %}# Place and Route + +{% if hooks %}{{ hooks.prepar | join('\n') }}{% endif %} + +execute_module -tool fit +execute_module -tool sta + +{% if hooks %}{{ hooks.postpar | join('\n') }}{% endif %} + +{% endif %} + +{% if 'bit' in steps %}# Bitstream generation + +{% if hooks %}{{ hooks.prebit | join('\n') }}{% endif %} + +execute_module -tool asm + +{% if hooks %}{{ hooks.postbit | join('\n') }}{% endif %} + +{% endif %} + +project_close + +{% endif %} diff --git a/pyfpga/templates/vivado-prog.jinja b/pyfpga/templates/vivado-prog.jinja new file mode 100644 index 00000000..eeeab0ca --- /dev/null +++ b/pyfpga/templates/vivado-prog.jinja @@ -0,0 +1,15 @@ +{# +# +# Copyright (C) 2015-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +#} + +if { [ catch { open_hw_manager } ] } { open_hw } +connect_hw_server +open_hw_target +puts [get_hw_devices] +set obj [lindex [get_hw_devices [current_hw_device]] 0] +set_property PROGRAM.FILE {{ bitstream }} $obj +program_hw_devices $obj diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja new file mode 100644 index 00000000..e214cfb4 --- /dev/null +++ b/pyfpga/templates/vivado.jinja @@ -0,0 +1,102 @@ +{# +# +# Copyright (C) 2015-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +#} + +{% if 'cfg' in steps %}# Project configuration ------------------------------------------------------- + +create_project -force {{ project }} +set_property SOURCE_MGMT_MODE None [current_project] +set_property STEPS.SYNTH_DESIGN.ARGS.ASSERT true [get_runs synth_1] +set_property PART {{ part }} [current_project] + +{% if hooks %}{{ hooks.precfg | join('\n') }}{% endif %} + +{% if files %}# Files inclusion +{% for name, attr in files.items() %} +add_file {{ name }} +{% if 'lib' in attr %}set_property library {{ attr.lib }} [get_files {{ name }}]{% endif %} +{% endfor %} +{% endif %} + +{% if constraints %}# Constraints inclusion +{% for name, attr in constraints.items() %} +add_file -fileset constrs_1 {{ name }} +{% if loop.first %}set_property TARGET_CONSTRS_FILE {{ name }} [current_fileset -constrset]{% endif %} +{% endfor %} +{% endif %} + +{% if top %}# Top-level specification +set_property TOP {{ top }} [current_fileset] +{% endif %} + +{% if includes %}# Verilog Includes +set_property INCLUDE_DIRS { {{ includes | join(' ') }} } [current_fileset] +{% endif %} + +{% if defines %}# Verilog Defines +set_property VERILOG_DEFINE { {{ defines.items() | map('join', '=') | join(' ') }} } [current_fileset] +{% endif %} + +{% if params %}# Verilog Parameters / VHDL Generics +set_property GENERIC { {{ params.items() | map('join', '=') | join(' ') }} } -objects [get_filesets sources_1] +{% endif %} + +{% if hooks %}{{ hooks.postcfg | join('\n') }}{% endif %} + +close_project + +{% endif %} + +{% if 'syn' in steps or 'par' in steps or 'bit' in steps %}# Design flow ----------------------------------------------------------------- + +open_project {{ project }} + +{% if 'syn' in steps %}# Synthesis + +{% if hooks %}{{ hooks.presyn | join('\n') }}{% endif %} + +# PRESYNTH +# set_property DESIGN_MODE GateLvl [current_fileset] +reset_run synth_1 +launch_runs synth_1 +wait_on_run synth_1 +#report_property [get_runs synth_1] +if { [get_property STATUS [get_runs synth_1]] ne "synth_design Complete!" } { exit 1 } + +{% if hooks %}{{ hooks.postsyn | join('\n') }}{% endif %} + +{% endif %} + +{% if 'par' in steps %}# Place and Route + +{% if hooks %}{{ hooks.prepar | join('\n') }}{% endif %} + +reset_run impl_1 +launch_runs impl_1 +wait_on_run impl_1 +#report_property [get_runs impl_1] +if { [get_property STATUS [get_runs impl_1]] ne "route_design Complete!" } { exit 1 } + +{% if hooks %}{{ hooks.postpar | join('\n') }}{% endif %} + +{% endif %} + +{% if 'bit' in steps %}# Bitstream generation + +{% if hooks %}{{ hooks.prebit | join('\n') }}{% endif %} + +open_run impl_1 +write_bitstream -force {{ project }} +write_debug_probes -force -quiet {{ project }}.ltx + +{% if hooks %}{{ hooks.postbit | join('\n') }}{% endif %} + +{% endif %} + +close_project + +{% endif %} diff --git a/pyfpga/vivado.py b/pyfpga/vivado.py new file mode 100644 index 00000000..2b04819d --- /dev/null +++ b/pyfpga/vivado.py @@ -0,0 +1,29 @@ +# +# Copyright (C) 2019-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +""" +Implements support for Vivado. +""" + +from pyfpga.project import Project + + +class Vivado(Project): + """Class to support Vivado projects.""" + + def _configure(self): + tool = 'vivado' + command = 'vivado -mode batch -notrace -quiet -source' + self.conf['tool'] = tool + self.conf['make_cmd'] = f'{command} {tool}.tcl' + self.conf['make_ext'] = 'tcl' + self.conf['prog_bit'] = ['bit'] + self.conf['prog_cmd'] = f'{command} {tool}-prog.tcl' + self.conf['prog_ext'] = 'tcl' + + def _make_custom(self): + if 'part' not in self.data: + self.data['part'] = 'xc7k160t-3-fbg484' diff --git a/setup.py b/setup.py index 90266a5d..a3eaab1d 100644 --- a/setup.py +++ b/setup.py @@ -1,13 +1,13 @@ from setuptools import setup, find_packages -import fpga +import pyfpga with open('README.md', 'r') as fh: long_description = fh.read() setup( name='pyfpga', - version=fpga.__version__, + version=pyfpga.__version__, description='A Python package to use FPGA development tools programmatically', long_description=long_description, long_description_content_type='text/markdown', @@ -15,13 +15,13 @@ author_email='rodrigomelo9@gmail.com', license='GPLv3', url='https://github.com/PyFPGA/pyfpga', - package_data={'': ['tool/*.sh', 'tool/*.tcl']}, + package_data={'': ['templates/*.jinja', 'helpers/*']}, packages=find_packages(), entry_points={ 'console_scripts': [ - 'fpga-hdl2bit = fpga.helpers.hdl2bit:main', - 'fpga-prj2bit = fpga.helpers.prj2bit:main', - 'fpga-bitprog = fpga.helpers.bitprog:main' + 'fpga-hdl2bit = pyfpga.helpers.hdl2bit:main', + 'fpga-prj2bit = pyfpga.helpers.prj2bit:main', + 'fpga-bitprog = pyfpga.helpers.bitprog:main' ], }, classifiers=[ @@ -30,13 +30,14 @@ 'Intended Audience :: Developers', 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', 'Topic :: Utilities', 'Topic :: Software Development :: Build Tools', "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)" ], - install_requires=['pyyaml'] + install_requires=['jinja2'] ) diff --git a/test/test_files.py b/test/test_files.py deleted file mode 100644 index ffe55f81..00000000 --- a/test/test_files.py +++ /dev/null @@ -1,18 +0,0 @@ -import os -import pytest - -from fpga.project import Project, TOOLS - - -def get_path(file): - return os.path.join(os.path.dirname(__file__), file) - - -@pytest.mark.parametrize('tool', TOOLS) -def test_files(tool): - prj = Project(tool) - prj.add_files(get_path('../hdl/*.vhdl')) - prj.add_files(get_path('../hdl/*.v')) - assert len(prj.get_files()['verilog']) == 3 - assert len(prj.get_files()['vhdl']) == 4 - assert len(prj.get_files()['constraint']) == 0 diff --git a/test/test_part.py b/test/test_part.py deleted file mode 100644 index a1c2861a..00000000 --- a/test/test_part.py +++ /dev/null @@ -1,29 +0,0 @@ -import pytest - -from fpga.project import Project - - -def get_part(prj): - return prj.get_configs()['part'].lower() - - -def test_ise(): - prj = Project('ise') - assert get_part(prj) == "xc7k160t-3-fbg484" - prj.set_part('XC6SLX9-2-CSG324') - assert get_part(prj) == "xc6slx9-2-csg324" - prj.set_part('XC6SLX9-2L-CSG324') - assert get_part(prj) == "xc6slx9-2l-csg324" - prj.set_part('XC6SLX9-CSG324-3') - assert get_part(prj) == "xc6slx9-3-csg324" - - -def test_libero(): - prj = Project('libero') - assert get_part(prj) == "mpf100t-1-fcg484" - prj.set_part('m2s010-3-tq144') - assert get_part(prj) == "m2s010-3-tq144" - prj.set_part('m2s010-tq144-2') - assert get_part(prj) == "m2s010-2-tq144" - prj.set_part('m2s010-tq144') - assert get_part(prj) == "m2s010-std-tq144" diff --git a/test/test_top.py b/test/test_top.py deleted file mode 100644 index fdb894f4..00000000 --- a/test/test_top.py +++ /dev/null @@ -1,31 +0,0 @@ -import os -import pytest - -from fpga.project import Project - - -def get_pathfile(file): - return os.path.join(os.path.dirname(__file__), file) - - -def test_default(): - prj = Project() - assert prj.tool.top == "UNDEFINED" - - -def test_names(): - prj = Project() - prj.set_top('test1') - assert prj.tool.top == "test1" - prj.set_top('test2') - assert prj.tool.top == "test2" - - -def test_files(): - prj = Project() - prj.set_top(get_pathfile('../hdl/fakes/top.vhdl')) - assert prj.tool.top == "Top1" - prj.set_top(get_pathfile('../hdl/fakes/top.v')) - assert prj.tool.top == "Top1" - prj.set_top(get_pathfile('../README.md')) - assert prj.tool.top == "UNDEFINED" diff --git a/tests/fakedata/cons/all.xdc b/tests/fakedata/cons/all.xdc new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/cons/par.xdc b/tests/fakedata/cons/par.xdc new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/cons/syn.xdc b/tests/fakedata/cons/syn.xdc new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/dir1/slog1.sv b/tests/fakedata/dir1/slog1.sv new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/dir1/slog1.svh b/tests/fakedata/dir1/slog1.svh new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/dir1/vhdl1.vhd b/tests/fakedata/dir1/vhdl1.vhd new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/dir1/vhdl1.vhdl b/tests/fakedata/dir1/vhdl1.vhdl new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/dir1/vlog1.v b/tests/fakedata/dir1/vlog1.v new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/dir1/vlog1.vh b/tests/fakedata/dir1/vlog1.vh new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/dir2/slog2.sv b/tests/fakedata/dir2/slog2.sv new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/dir2/slog2.svh b/tests/fakedata/dir2/slog2.svh new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/dir2/vhdl2.vhd b/tests/fakedata/dir2/vhdl2.vhd new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/dir2/vhdl2.vhdl b/tests/fakedata/dir2/vhdl2.vhdl new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/dir2/vlog2.v b/tests/fakedata/dir2/vlog2.v new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/dir2/vlog2.vh b/tests/fakedata/dir2/vlog2.vh new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/dir3/slog3.sv b/tests/fakedata/dir3/slog3.sv new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/dir3/slog3.svh b/tests/fakedata/dir3/slog3.svh new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/dir3/vhdl3.vhd b/tests/fakedata/dir3/vhdl3.vhd new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/dir3/vhdl3.vhdl b/tests/fakedata/dir3/vhdl3.vhdl new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/dir3/vlog3.v b/tests/fakedata/dir3/vlog3.v new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/dir3/vlog3.vh b/tests/fakedata/dir3/vlog3.vh new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/slog0.sv b/tests/fakedata/slog0.sv new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/slog0.svh b/tests/fakedata/slog0.svh new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/vhdl0.vhd b/tests/fakedata/vhdl0.vhd new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/vhdl0.vhdl b/tests/fakedata/vhdl0.vhdl new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/vlog0.v b/tests/fakedata/vlog0.v new file mode 100644 index 00000000..e69de29b diff --git a/tests/fakedata/vlog0.vh b/tests/fakedata/vlog0.vh new file mode 100644 index 00000000..e69de29b diff --git a/tests/mocks/FPExpress b/tests/mocks/FPExpress new file mode 100755 index 00000000..8947406b --- /dev/null +++ b/tests/mocks/FPExpress @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +import argparse +import sys + + +parser = argparse.ArgumentParser() + +parser.add_argument('source') + +args = parser.parse_args() + +tool = parser.prog + +if not args.source.startswith("SCRIPT:", 0): + print('ERROR:the parameter should start width "SCRIPT:"') + sys.exit(1) + +print(f'INFO:the {tool.upper()} mock has been executed') diff --git a/tests/mocks/diamondc b/tests/mocks/diamondc new file mode 100755 index 00000000..50303cb3 --- /dev/null +++ b/tests/mocks/diamondc @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +import argparse +import subprocess + + +parser = argparse.ArgumentParser() + +parser.add_argument('source') + +args = parser.parse_args() + +tool = parser.prog + +tcl = f''' +proc unknown args {{ }} + +source {args.source} +''' + +with open(f'{tool}-mock.tcl', 'w', encoding='utf-8') as file: + file.write(tcl) + +subprocess.run( + f'tclsh {tool}-mock.tcl', + shell=True, + check=True, + universal_newlines=True +) + +print(f'INFO:the {tool.upper()} mock has been executed') diff --git a/tests/mocks/impact b/tests/mocks/impact new file mode 100755 index 00000000..1446c5e0 --- /dev/null +++ b/tests/mocks/impact @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2022-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +import argparse + + +parser = argparse.ArgumentParser() + +parser.add_argument('-batch', action='store_true', required=True) +parser.add_argument('source') + +args = parser.parse_args() + +tool = parser.prog + +print(f'INFO:the {tool.upper()} mock has been executed') diff --git a/tests/mocks/libero b/tests/mocks/libero new file mode 100755 index 00000000..94c8dd05 --- /dev/null +++ b/tests/mocks/libero @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2022-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +import argparse +import re +import subprocess +import sys + + +parser = argparse.ArgumentParser() + +parser.add_argument('source') + +args = parser.parse_args() + +tool = parser.prog + +if not args.source.startswith("SCRIPT:", 0): + print('ERROR:the parameter should start width "SCRIPT:"') + sys.exit(1) + +args.source = args.source.replace('SCRIPT:', '') + +tcl = f''' +proc unknown args {{ }} + +source {args.source} +''' + +with open(f'{tool}-mock.tcl', 'w', encoding='utf-8') as file: + file.write(tcl) + +subprocess.run( + f'tclsh {tool}-mock.tcl', + shell=True, + check=True, + universal_newlines=True +) + +pattern = r'new_project\s+-name\s+(\S+)\s' +with open(args.source, 'r', encoding='utf-8') as file: + match = re.search(pattern, file.read()) + if match: + project = match.group(1) + with open(f'{project}.ppd', 'w', encoding='utf-8') as file: + pass + +print(f'INFO:the {tool.upper()} mock has been executed') diff --git a/tests/mocks/quartus_pgm b/tests/mocks/quartus_pgm new file mode 100755 index 00000000..150e1ae9 --- /dev/null +++ b/tests/mocks/quartus_pgm @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2022-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +import argparse + + +parser = argparse.ArgumentParser() + +parser.add_argument('-c', required=True) +parser.add_argument('--mode', choices=['jtag'], required=True) +parser.add_argument('-o', required=True) + +args = parser.parse_args() + +tool = parser.prog + +print(f'INFO:the {tool.upper()} mock has been executed') diff --git a/tests/mocks/quartus_sh b/tests/mocks/quartus_sh new file mode 100755 index 00000000..38f6507d --- /dev/null +++ b/tests/mocks/quartus_sh @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2022-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +import argparse +import os +import re +import subprocess + + +parser = argparse.ArgumentParser() + +parser.add_argument('--script', required=True) + +args = parser.parse_args() + +tool = parser.prog + +tcl = f''' +proc unknown args {{ }} + +package provide ::quartus::project 1.0 +namespace eval ::quartus::project {{ }} + +package provide ::quartus::flow 1.0 +namespace eval ::quartus::flow {{ }} + +source {args.script} +''' + +with open(f'{tool}-mock.tcl', 'w', encoding='utf-8') as file: + file.write(tcl) + +subprocess.run( + f'tclsh {tool}-mock.tcl', + shell=True, + check=True, + universal_newlines=True +) + +pattern = r'project_new\s+(\S+)\s' +with open(args.script, 'r', encoding='utf-8') as file: + match = re.search(pattern, file.read()) + if match: + project = match.group(1) + with open(f'{project}.sof', 'w', encoding='utf-8') as file: + pass + +print(f'INFO:the {tool.upper()} mock has been executed') diff --git a/tests/mocks/source-me.sh b/tests/mocks/source-me.sh new file mode 100644 index 00000000..1f035b47 --- /dev/null +++ b/tests/mocks/source-me.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +MDIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd) + +export PATH=$PATH:$MDIR diff --git a/tests/mocks/vivado b/tests/mocks/vivado new file mode 100755 index 00000000..2f5cbe69 --- /dev/null +++ b/tests/mocks/vivado @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2022-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +import argparse +import re +import subprocess + + +parser = argparse.ArgumentParser() + +parser.add_argument('-mode', choices=['batch'], required=True) +parser.add_argument('-notrace', action='store_true', required=True) +parser.add_argument('-quiet', action='store_true', required=True) +parser.add_argument('-source', required=True) + +args = parser.parse_args() + +tool = parser.prog + +#proc create_project args {{ }} +#proc open_project args {{ }} +#proc current_project args {{ }} +#proc current_fileset args {{ }} +#proc get_filesets args {{ }} +#proc set_property args {{ }} +#proc add_files args {{ }} +#proc get_files args {{ }} +#proc reset_run args {{ }} +#proc launch_runs args {{ }} +#proc get_runs args {{ }} +#proc wait_on_run args {{ }} +#proc open_run args {{ }} +#proc write_bitstream args {{ }} +#proc close_project args {{ }} +#proc current_bd_design args {{ }} +#proc get_bd_cells args {{ }} + +tcl = f''' +proc unknown args {{ }} + +proc get_runs {{run_name}} {{ + return $run_name +}} + +proc get_property {{property run}} {{ + if {{ $property eq "STATUS" }} {{ + if {{ $run eq "synth_1" }} {{ + return "synth_design Complete!" + }} elseif {{ $run eq "impl_1" }} {{ + return "route_design Complete!" + }} + }} + return "" +}} + +source {args.source} +''' + +with open(f'{tool}-mock.tcl', 'w', encoding='utf-8') as file: + file.write(tcl) + +subprocess.run( + f'tclsh {tool}-mock.tcl', + shell=True, + check=True, + universal_newlines=True +) + +pattern = r'create_project\s+-force\s+(\S+)' +with open(args.source, 'r', encoding='utf-8') as file: + match = re.search(pattern, file.read()) + if match: + project = match.group(1) + with open(f'{project}.bit', 'w', encoding='utf-8') as file: + pass + +print(f'INFO:the {tool.upper()} mock has been executed') diff --git a/tests/mocks/xtclsh b/tests/mocks/xtclsh new file mode 100755 index 00000000..4064f790 --- /dev/null +++ b/tests/mocks/xtclsh @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2022-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +import argparse +import re +import subprocess + + +parser = argparse.ArgumentParser() + +parser.add_argument('source') + +args = parser.parse_args() + +tool = parser.prog + +tcl = f''' +proc unknown args {{ }} + +source {args.source} +''' + +with open(f'{tool}-mock.tcl', 'w', encoding='utf-8') as file: + file.write(tcl) + +subprocess.run( + f'tclsh {tool}-mock.tcl', + shell=True, + check=True, + universal_newlines=True +) + +pattern = r'project\s+new\s+(\S+)\.xise' +with open(args.source, 'r', encoding='utf-8') as file: + match = re.search(pattern, file.read()) + if match: + project = match.group(1) + with open(f'{project}.bit', 'w', encoding='utf-8') as file: + pass + +print(f'INFO:the {tool.upper()} mock has been executed') diff --git a/tests/support.py b/tests/support.py new file mode 100644 index 00000000..128a17c5 --- /dev/null +++ b/tests/support.py @@ -0,0 +1,132 @@ +"""Support examples.""" + +import argparse +import sys + +from pyfpga.factory import Factory, TOOLS + + +parser = argparse.ArgumentParser() +parser.add_argument( + '--tool', default='openflow', + choices=list(TOOLS.keys()) +) +args = parser.parse_args() + +print(f'INFO: the Tool Under Test is {args.tool}') + +print('INFO: checking basic Verilog Support') +prj = Factory(args.tool) +prj.add_vlog('../examples/sources/vlog/blink.v') +prj.set_top('Blink') +prj.make(last='syn') + +print('INFO: checking advanced Verilog Support') +prj = Factory(args.tool) +prj.add_vlog('../examples/sources/vlog/*.v') +prj.set_top('Top') +prj.add_include('../examples/sources/vlog/include1') +prj.add_include('../examples/sources/vlog/include2') +prj.add_define('DEFINE1', '1') +prj.add_define('DEFINE2', '1') +prj.add_param('FREQ', '1') +prj.add_param('SECS', '1') +prj.make(last='syn') + +try: + print('INFO: checking Verilog Includes Support') + prj = Factory(args.tool) + prj.add_vlog('../examples/sources/vlog/*.v') + prj.set_top('Top') + prj.add_define('DEFINE1', '1') + prj.add_define('DEFINE2', '1') + prj.add_param('FREQ', '1') + prj.add_param('SECS', '1') + prj.make(last='syn') + sys.exit('ERROR: something does not work as expected') +except SystemExit: + raise +except Exception: + pass + +try: + print('INFO: checking Verilog Defines Support') + prj = Factory(args.tool) + prj.add_vlog('../examples/sources/vlog/*.v') + prj.set_top('Top') + prj.add_include('../examples/sources/vlog/include1') + prj.add_include('../examples/sources/vlog/include2') + prj.add_param('FREQ', '1') + prj.add_param('SECS', '1') + prj.make(last='syn') + sys.exit('ERROR: something does not work as expected') +except SystemExit: + raise +except Exception: + pass + +try: + print('INFO: checking Verilog Parameters Support') + prj = Factory(args.tool) + prj.add_vlog('../examples/sources/vlog/*.v') + prj.set_top('Top') + prj.add_include('../examples/sources/vlog/include1') + prj.add_include('../examples/sources/vlog/include2') + prj.add_define('DEFINE1', '1') + prj.add_define('DEFINE2', '1') + prj.make(last='syn') + sys.exit('ERROR: something does not work as expected') +except SystemExit: + raise +except Exception: + pass + +if args.tool not in ['ise']: + print('INFO: checking basic System Verilog Support') + prj = Factory(args.tool) + prj.add_slog('../examples/sources/slog/blink.sv') + prj.set_top('Blink') + prj.make(last='syn') + + print('INFO: checking advanced System Verilog Support') + prj = Factory(args.tool) + prj.add_slog('../examples/sources/slog/*.sv') + prj.set_top('Top') + prj.add_include('../examples/sources/slog/include1') + prj.add_include('../examples/sources/slog/include2') + prj.add_define('DEFINE1', '1') + prj.add_define('DEFINE2', '1') + prj.add_param('FREQ', '1') + prj.add_param('SECS', '1') + prj.make(last='syn') + +if args.tool not in ['openflow']: + print('* INFO: checking basic VHDL Support') + prj = Factory(args.tool) + prj.add_vhdl('../examples/sources/vhdl/blink.vhdl') + prj.set_top('Blink') + prj.make(last='syn') + + print('* INFO: checking advanced VHDL Support') + prj = Factory(args.tool) + prj.add_vhdl('../examples/sources/vhdl/*.vhdl', 'blink_lib') + prj.add_vhdl('../examples/sources/vhdl/top.vhdl') + prj.set_top('Top') + prj.add_param('FREQ', '1') + prj.add_param('SECS', '1') + prj.make(last='syn') + + try: + print('INFO: checking VHDL Generics') + prj = Factory(args.tool) + prj.add_vhdl('../examples/sources/vhdl/*.vhdl', 'blink_lib') + prj.add_vhdl('../examples/sources/vhdl/top.vhdl') + prj.set_top('Top') + prj.make(last='syn') + sys.exit('ERROR: something does not work as expected') + except SystemExit: + raise + except Exception: + pass + +print(f'INFO: {args.tool} support works as expected') diff --git a/tests/test_data.py b/tests/test_data.py new file mode 100644 index 00000000..d4d8d0f8 --- /dev/null +++ b/tests/test_data.py @@ -0,0 +1,122 @@ +from pathlib import Path + +from pyfpga.vivado import Vivado + +tdir = Path(__file__).parent.resolve() + +pattern = { + 'project': 'EXAMPLE', + 'part': 'PARTNAME', + 'includes': [ + Path(tdir / 'fakedata/dir1').resolve().as_posix(), + Path(tdir / 'fakedata/dir2').resolve().as_posix(), + Path(tdir / 'fakedata/dir3').resolve().as_posix() + ], + 'files': { + Path(tdir / 'fakedata/vhdl0.vhdl').resolve().as_posix(): { + 'hdl': 'vhdl', 'lib': 'LIB' + }, + Path(tdir / 'fakedata/dir1/vhdl1.vhdl').resolve().as_posix(): { + 'hdl': 'vhdl', 'lib': 'LIB' + }, + Path(tdir / 'fakedata/dir2/vhdl2.vhdl').resolve().as_posix(): { + 'hdl': 'vhdl', 'lib': 'LIB' + }, + Path(tdir / 'fakedata/dir3/vhdl3.vhdl').resolve().as_posix(): { + 'hdl': 'vhdl', 'lib': 'LIB' + }, + Path(tdir / 'fakedata/vlog0.v').resolve().as_posix(): { + 'hdl': 'vlog' + }, + Path(tdir / 'fakedata/dir1/vlog1.v').resolve().as_posix(): { + 'hdl': 'vlog' + }, + Path(tdir / 'fakedata/dir2/vlog2.v').resolve().as_posix(): { + 'hdl': 'vlog' + }, + Path(tdir / 'fakedata/dir3/vlog3.v').resolve().as_posix(): { + 'hdl': 'vlog' + }, + Path(tdir / 'fakedata/slog0.sv').resolve().as_posix(): { + 'hdl': 'slog' + }, + Path(tdir / 'fakedata/dir1/slog1.sv').resolve().as_posix(): { + 'hdl': 'slog' + }, + Path(tdir / 'fakedata/dir2/slog2.sv').resolve().as_posix(): { + 'hdl': 'slog' + }, + Path(tdir / 'fakedata/dir3/slog3.sv').resolve().as_posix(): { + 'hdl': 'slog' + } + }, + 'top': 'TOPNAME', + 'constraints': { + Path(tdir / 'fakedata/cons/all.xdc').resolve().as_posix(): {}, + Path(tdir / 'fakedata/cons/syn.xdc').resolve().as_posix(): {}, + Path(tdir / 'fakedata/cons/par.xdc').resolve().as_posix(): {} + }, + 'params': { + 'PAR1': 'VAL01', + 'PAR2': 'VAL02', + 'PAR3': 'VAL03' + }, + 'defines': { + 'DEF1': 'VAL01', + 'DEF2': 'VAL02', + 'DEF3': 'VAL03' + }, + 'hooks': { + 'precfg': ['CMD01', 'CMD02'], + 'postcfg': ['CMD03', 'CMD04'], + 'presyn': ['CMD05', 'CMD06'], + 'postsyn': ['CMD07', 'CMD08'], + 'prepar': ['CMD09', 'CMD10'], + 'postpar': ['CMD11', 'CMD12'], + 'prebit': ['CMD13', 'CMD14'], + 'postbit': ['CMD15', 'CMD16'] + } +} + + +def test_data(): + prj = Vivado('EXAMPLE') + prj.set_part('PARTNAME') + prj.set_top('TOPNAME') + prj.add_include(str(tdir / 'fakedata/dir1')) + prj.add_include(str(tdir / 'fakedata/dir2')) + prj.add_include(str(tdir / 'fakedata/dir3')) + prj.add_slog(str(tdir / 'fakedata/**/*.sv')) + prj.add_vhdl(str(tdir / 'fakedata/**/*.vhdl'), 'LIB') + prj.add_vlog(str(tdir / 'fakedata/**/*.v')) + prj.add_cons(str(tdir / 'fakedata/cons/all.xdc')) + prj.add_cons(str(tdir / 'fakedata/cons/syn.xdc')) + prj.add_cons(str(tdir / 'fakedata/cons/par.xdc')) + prj.add_param('PAR1', 'VAL01') + prj.add_param('PAR2', 'VAL02') + prj.add_param('PAR3', 'VAL03') + prj.add_define('DEF1', 'VAL01') + prj.add_define('DEF2', 'VAL02') + prj.add_define('DEF3', 'VAL03') + prj.add_hook('precfg', 'CMD01') + prj.add_hook('precfg', 'CMD02') + prj.add_hook('postcfg', 'CMD03') + prj.add_hook('postcfg', 'CMD04') + prj.add_hook('presyn', 'CMD05') + prj.add_hook('presyn', 'CMD06') + prj.add_hook('postsyn', 'CMD07') + prj.add_hook('postsyn', 'CMD08') + prj.add_hook('prepar', 'CMD09') + prj.add_hook('prepar', 'CMD10') + prj.add_hook('postpar', 'CMD11') + prj.add_hook('postpar', 'CMD12') + prj.add_hook('prebit', 'CMD13') + prj.add_hook('prebit', 'CMD14') + prj.add_hook('postbit', 'CMD15') + prj.add_hook('postbit', 'CMD16') + assert prj.data == pattern, 'ERROR: unexpected data' + paths = prj.data['includes'] + list(prj.data['files'].keys()) + for path in paths: + assert '\\' not in path, ( + f"'{path}' contains a '\\' character, which is not allowed." + ) diff --git a/tests/test_part.py b/tests/test_part.py new file mode 100644 index 00000000..1d155aa8 --- /dev/null +++ b/tests/test_part.py @@ -0,0 +1,119 @@ +from pyfpga.ise import get_info as get_info_ise +from pyfpga.libero import get_info as get_info_libero +from pyfpga.openflow import get_info as get_info_openflow + + +def test_ise(): + info = { + 'family': 'kintex7', + 'device': 'xc7k160t', + 'speed': '-3', + 'package': 'fbg484' + } + assert get_info_ise('xc7k160t-3-fbg484') == info + assert get_info_ise('xc7k160t-fbg484-3') == info + info['speed'] = '-3l' + assert get_info_ise('xc7k160t-3L-fbg484') == info + assert get_info_ise('xc7k160t-fbg484-3L') == info + + +def test_libero(): + info = { + 'family': 'SmartFusion2', + 'device': 'm2s025t', + 'speed': 'STD', + 'package': 'fg484', + 'prange': 'COM' + } + assert get_info_libero('M2S025T-FG484') == info + info['prange'] = 'IND' + assert get_info_libero('M2S025T-FG484I') == info + info['speed'] = '-1' + info['prange'] = 'COM' + assert get_info_libero('M2S025T-1FG484') == info + assert get_info_libero('M2S025T-1-FG484') == info + assert get_info_libero('M2S025T-FG484-1') == info + info['prange'] = 'IND' + assert get_info_libero('M2S025T-1FG484I') == info + assert get_info_libero('M2S025T-1-FG484I') == info + assert get_info_libero('M2S025T-FG484I-1') == info + info['prange'] = 'MIL' + assert get_info_libero('M2S025T-1FG484M') == info + assert get_info_libero('M2S025T-1-FG484M') == info + assert get_info_libero('M2S025T-FG484M-1') == info + info = { + 'family': 'IGLOO2', + 'device': 'm2gl025', + 'speed': 'STD', + 'package': 'fg484', + 'prange': 'COM' + } + assert get_info_libero('M2GL025-FG484') == info + info['prange'] = 'IND' + assert get_info_libero('M2GL025-FG484I') == info + info['speed'] = '-1' + info['prange'] = 'COM' + assert get_info_libero('M2GL025-1FG484') == info + assert get_info_libero('M2GL025-1-FG484') == info + assert get_info_libero('M2GL025-FG484-1') == info + info['prange'] = 'IND' + assert get_info_libero('M2GL025-1FG484I') == info + assert get_info_libero('M2GL025-1-FG484I') == info + assert get_info_libero('M2GL025-FG484I-1') == info + info['prange'] = 'MIL' + assert get_info_libero('M2GL025-1FG484M') == info + assert get_info_libero('M2GL025-1-FG484M') == info + assert get_info_libero('M2GL025-FG484M-1') == info + info['prange'] = 'TGrade1' + assert get_info_libero('M2GL025-1FG484T1') == info + assert get_info_libero('M2GL025-1-FG484T1') == info + assert get_info_libero('M2GL025-FG484T1-1') == info + info = { + 'family': 'PolarFire', + 'device': 'mpf300ts_es', + 'speed': 'STD', + 'package': 'fg484', + 'prange': 'EXT' + } + assert get_info_libero('MPF300TS_ES-FG484E') == info + info['prange'] = 'IND' + assert get_info_libero('MPF300TS_ES-FG484I') == info + info['speed'] = '-1' + info['prange'] = 'EXT' + assert get_info_libero('MPF300TS_ES-1FG484E') == info + assert get_info_libero('MPF300TS_ES-1-FG484E') == info + assert get_info_libero('MPF300TS_ES-FG484E-1') == info + info['prange'] = 'IND' + assert get_info_libero('MPF300TS_ES-1FG484I') == info + assert get_info_libero('MPF300TS_ES-1-FG484I') == info + assert get_info_libero('MPF300TS_ES-FG484I-1') == info + info = { + 'family': 'PolarFireSoC', + 'device': 'mpfs025t', + 'speed': 'STD', + 'package': 'fcvg484', + 'prange': 'EXT' + } + assert get_info_libero('MPFS025T-FCVG484E') == info + info['prange'] = 'IND' + assert get_info_libero('MPFS025T-FCVG484I') == info + info['speed'] = '-1' + info['prange'] = 'EXT' + assert get_info_libero('MPFS025T-1FCVG484E') == info + assert get_info_libero('MPFS025T-1-FCVG484E') == info + assert get_info_libero('MPFS025T-FCVG484E-1') == info + info['prange'] = 'IND' + assert get_info_libero('MPFS025T-1FCVG484I') == info + assert get_info_libero('MPFS025T-1-FCVG484I') == info + assert get_info_libero('MPFS025T-FCVG484I-1') == info + + +def test_openflow(): + info = {'family': 'xc7', 'device': 'xc7k160t-3', 'package': 'fbg484'} + assert get_info_openflow('xc7k160t-3-fbg484') == info + info = {'family': 'ice40', 'device': 'hx1k', 'package': 'tq144'} + assert get_info_openflow('hx1k-tq144') == info + info = {'family': 'ecp5', 'device': '25k', 'package': 'CSFBGA285'} + assert get_info_openflow('25k-CSFBGA285') == info + info = {'family': 'ecp5', 'device': 'um5g-85k', 'package': 'CABGA381'} + assert get_info_openflow('um5g-85k-CABGA381') == info diff --git a/tests/test_tools.py b/tests/test_tools.py new file mode 100644 index 00000000..a2b3e489 --- /dev/null +++ b/tests/test_tools.py @@ -0,0 +1,101 @@ +from pathlib import Path +from pyfpga.factory import Factory + +tdir = Path(__file__).parent.resolve() + + +def test_diamond(): + tool = 'diamond' + generate(tool, 'PARTNAME') + base = f'results/{tool}/{tool}' + assert Path(f'{base}.tcl').exists(), 'file not found' + assert Path(f'{base}-prog.sh').exists(), 'file not found' + + +def test_ise(): + tool = 'ise' + generate(tool, 'DEVICE-PACKAGE-SPEED') + base = f'results/{tool}/{tool}' + assert Path(f'{base}.tcl').exists(), 'file not found' + assert Path(f'{base}-prog.tcl').exists(), 'file not found' + + +def test_libero(): + tool = 'libero' + generate(tool, 'DEVICE-PACKAGE-SPEED') + base = f'results/{tool}/{tool}' + assert Path(f'{base}.tcl').exists(), 'file not found' + assert Path(f'{base}-prog.tcl').exists(), 'file not found' + + +def test_openflow(): + tool = 'openflow' + generate(tool, 'DEVICE-PACKAGE') + base = f'results/{tool}/{tool}' + assert Path(f'{base}.sh').exists(), 'file not found' + assert Path(f'{base}-prog.sh').exists(), 'file not found' + + +def test_quartus(): + tool = 'quartus' + generate(tool, 'PARTNAME') + base = f'results/{tool}/{tool}' + assert Path(f'{base}.tcl').exists(), 'file not found' + assert Path(f'{base}-prog.tcl').exists(), 'file not found' + + +def test_vivado(): + tool = 'vivado' + generate(tool, 'PARTNAME') + base = f'results/{tool}/{tool}' + assert Path(f'{base}.tcl').exists(), 'file not found' + assert Path(f'{base}-prog.tcl').exists(), 'file not found' + + +def generate(tool, part): + prj = Factory(tool, odir=f'results/{tool}') + prj.set_part(part) + prj.set_top('TOPNAME') + prj.add_include(str(tdir / 'fakedata/dir1')) + prj.add_include(str(tdir / 'fakedata/dir2')) + if tool != 'ise': + prj.add_slog(str(tdir / 'fakedata/**/*.sv')) + prj.add_vhdl(str(tdir / 'fakedata/**/*.vhdl'), 'LIB') + prj.add_vlog(str(tdir / 'fakedata/**/*.v')) + prj.add_cons(str(tdir / 'fakedata/cons/all.xdc')) + prj.add_cons(str(tdir / 'fakedata/cons/syn.xdc')) + prj.add_cons(str(tdir / 'fakedata/cons/par.xdc')) + prj.add_param('PAR1', 'VAL1') + prj.add_param('PAR2', 'VAL2') + prj.add_define('DEF1', 'VAL1') + prj.add_define('DEF2', 'VAL2') + prj.add_hook('precfg', 'HOOK01') + prj.add_hook('precfg', 'HOOK02') + prj.add_hook('postcfg', 'HOOK03') + prj.add_hook('postcfg', 'HOOK04') + prj.add_hook('presyn', 'HOOK05') + prj.add_hook('presyn', 'HOOK06') + prj.add_hook('postsyn', 'HOOK07') + prj.add_hook('postsyn', 'HOOK08') + prj.add_hook('prepar', 'HOOK09') + prj.add_hook('prepar', 'HOOK10') + prj.add_hook('postpar', 'HOOK11') + prj.add_hook('postpar', 'HOOK12') + prj.add_hook('prebit', 'HOOK13') + prj.add_hook('prebit', 'HOOK14') + prj.add_hook('postbit', 'HOOK15') + prj.add_hook('postbit', 'HOOK16') + try: + prj.make() + except RuntimeError: + pass + if tool == 'libero': + open(f'results/{tool}/{tool}.ppd', 'w').close() + elif tool == 'quartus': + open(f'results/{tool}/{tool}.sof', 'w').close() + else: + open(f'results/{tool}/{tool}.bit', 'w').close() + try: + prj.prog() + except RuntimeError: + pass