From 3d9b185a98fca180c97e3dc53c3f3c4ae322e60a Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 15 May 2022 20:38:50 -0300 Subject: [PATCH 001/248] Updated README.md and modified version (0.3.0-dev) --- README.md | 30 +++++++++++++++--------------- fpga/__init__.py | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 8222587b..eb969487 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,30 @@ # 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) +![Yosys](https://img.shields.io/badge/Yosys-last-brightgreen.svg?style=flat-square) ![nextpnr](https://img.shields.io/badge/nextpnr-last-brightgreen.svg?style=flat-square) +![icestorm](https://img.shields.io/badge/icestorm-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) +![Vivado](https://img.shields.io/badge/Vivado-2019.2-blue.svg?style=flat-square) +![Quartus](https://img.shields.io/badge/Quartus--Prime-19.1-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. +> **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. -- 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. +PyFPGA is a **Python Package** for **vendor-agnostic** FPGA development. +It provides a **Class** which allows the programmatically execution of **synthesis**, +**place and route**, **bitstream generation** and/or **programming** of FPGA devices. +Additionally, a set of **command-line helpers** are provided for quick and simple runs. -Create your custom FPGA Tool using a workflow tailored to your needs! +Features: +* It's *Version Control Systems* and *Continuous Integration* friendly. +* Allows reproducibility and repeatability. +* Consumes fewer system resources than GUI based workflows. -> **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 custom FPGA tool using a workflow tailored to your needs! ## Usage diff --git a/fpga/__init__.py b/fpga/__init__.py index ffaf5fbc..bd3bc779 100644 --- a/fpga/__init__.py +++ b/fpga/__init__.py @@ -1,5 +1,5 @@ """PyFPGA""" -__version__ = '0.2.0' +__version__ = '0.3.0-dev' from fpga.project import Project From c18b44b6b89eafaaceb1aa985ef86728d1b9aa31 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 15 May 2022 20:40:25 -0300 Subject: [PATCH 002/248] resources: submodule added --- .gitmodules | 3 +++ Makefile | 3 +++ resources | 1 + 3 files changed, 7 insertions(+) create mode 100644 .gitmodules create mode 160000 resources diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..362bc8ae --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "resources"] + path = resources + url = https://github.com/PyFPGA/resources diff --git a/Makefile b/Makefile index 278baebc..8f1d8dc1 100644 --- a/Makefile +++ b/Makefile @@ -9,3 +9,6 @@ check: clean: py3clean . rm -fr build .pytest_cache + +submodule: + git submodule update --init diff --git a/resources b/resources new file mode 160000 index 00000000..6a58bba3 --- /dev/null +++ b/resources @@ -0,0 +1 @@ +Subproject commit 6a58bba3b1c36d238d1111e910db02f0b284aef7 From 1e8a5468811c4fc265e8bcb8e573a0cc965a2a7f Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 22 May 2022 21:28:21 -0300 Subject: [PATCH 003/248] test: added mocks (for vendors tools) --- .github/workflows/test.yml | 2 +- examples/Makefile | 6 +++++- examples/ghdl/Makefile | 2 +- examples/ghdl/ghdl.py | 5 +---- examples/hooks/Makefile | 2 +- examples/hooks/strategies.py | 5 +---- examples/ise/ise.py | 16 +++++----------- examples/libero/Makefile | 2 +- examples/libero/libero.py | 5 +---- examples/misc/Makefile | 2 +- examples/misc/capture.py | 16 +++++----------- examples/multi/Makefile | 2 +- examples/multi/memory.py | 5 +---- examples/multi/parameters.py | 5 +---- examples/multi/projects.py | 5 +---- examples/multi/verilog.py | 5 +---- examples/multi/vhdl.py | 5 +---- examples/openflow/icestorm.py | 10 ++-------- examples/openflow/prjtrellis.py | 10 ++-------- examples/quartus/Makefile | 2 +- examples/quartus/quartus.py | 10 ++-------- examples/vivado/Makefile | 2 +- examples/vivado/design.py | 5 +---- examples/vivado/vivado.py | 10 ++-------- examples/yosys/Makefile | 2 +- examples/yosys/ise.py | 10 ++-------- examples/yosys/vivado.py | 10 ++-------- examples/yosys/yosys.py | 5 +---- fpga/project.py | 16 +++++++--------- test/mocks/impact | 28 ++++++++++++++++++++++++++++ test/mocks/libero | 33 +++++++++++++++++++++++++++++++++ test/mocks/quartus | 27 +++++++++++++++++++++++++++ test/mocks/quartus_pgm | 29 +++++++++++++++++++++++++++++ test/mocks/quartus_sh | 27 +++++++++++++++++++++++++++ test/mocks/vivado | 30 ++++++++++++++++++++++++++++++ test/mocks/xtclsh | 27 +++++++++++++++++++++++++++ 36 files changed, 254 insertions(+), 129 deletions(-) create mode 100755 test/mocks/impact create mode 100755 test/mocks/libero create mode 100644 test/mocks/quartus create mode 100755 test/mocks/quartus_pgm create mode 100755 test/mocks/quartus_sh create mode 100755 test/mocks/vivado create mode 100755 test/mocks/xtclsh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 96b267ed..2e5564e1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,4 +29,4 @@ jobs: - name: Test run: | pytest - make -C examples + cd examples; make MOCKS=1 diff --git a/examples/Makefile b/examples/Makefile index 77edba5c..7bfe3fc4 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,7 +1,11 @@ #!/usr/bin/make +ifdef MOCKS +export PATH := $(PATH):$(PWD)/../test/mocks +$(info INFO: using MOCKS for the vendor EDA tools) +endif DIRS=$(wildcard */) all: - @$(foreach DIR, $(DIRS), make -C $(DIR);) + @$(foreach DIR, $(DIRS), make -C $(DIR) || exit;) diff --git a/examples/ghdl/Makefile b/examples/ghdl/Makefile index cd77fa54..f166a9f9 100644 --- a/examples/ghdl/Makefile +++ b/examples/ghdl/Makefile @@ -3,4 +3,4 @@ SCRIPTS = $(wildcard *.py) all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT);) + @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT) || exit;) diff --git a/examples/ghdl/ghdl.py b/examples/ghdl/ghdl.py index 9962d1bf..da2ef275 100644 --- a/examples/ghdl/ghdl.py +++ b/examples/ghdl/ghdl.py @@ -14,7 +14,4 @@ prj.add_files('../../hdl/top.vhdl') prj.set_top('Top') -try: - prj.generate() -except RuntimeError: - print('ERROR:generate:Docker not found') +prj.generate() diff --git a/examples/hooks/Makefile b/examples/hooks/Makefile index cd77fa54..f166a9f9 100644 --- a/examples/hooks/Makefile +++ b/examples/hooks/Makefile @@ -3,4 +3,4 @@ SCRIPTS = $(wildcard *.py) all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT);) + @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT) || exit;) 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/ise/ise.py b/examples/ise/ise.py index 14a95ae0..c4f90b41 100644 --- a/examples/ise/ise.py +++ b/examples/ise/ise.py @@ -37,16 +37,10 @@ prj.add_files(BOARDS[args.board][2]) if args.action in ['generate', 'all']: - try: - prj.generate() - except RuntimeError: - print('ERROR:generate:ISE not found') + prj.generate() 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') + prj.transfer('fpga') + # prj.transfer('detect') + # prj.transfer('unlock') + # prj.transfer('spi', 1, 'N25Q128', 4) diff --git a/examples/libero/Makefile b/examples/libero/Makefile index cd77fa54..f166a9f9 100644 --- a/examples/libero/Makefile +++ b/examples/libero/Makefile @@ -3,4 +3,4 @@ SCRIPTS = $(wildcard *.py) all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT);) + @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT) || exit;) diff --git a/examples/libero/libero.py b/examples/libero/libero.py index 7f0b39bb..32ef38a5 100644 --- a/examples/libero/libero.py +++ b/examples/libero/libero.py @@ -26,10 +26,7 @@ prj.add_files('mkr.sdc') if args.action in ['generate', 'all']: - try: - prj.generate() - except RuntimeError: - print('ERROR:generate:Libero not found') + prj.generate() if args.action in ['transfer', 'all']: print('ERROR:transfer:Not yet implemented') diff --git a/examples/misc/Makefile b/examples/misc/Makefile index cd77fa54..f166a9f9 100644 --- a/examples/misc/Makefile +++ b/examples/misc/Makefile @@ -3,4 +3,4 @@ SCRIPTS = $(wildcard *.py) all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT);) + @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT) || exit;) diff --git a/examples/misc/capture.py b/examples/misc/capture.py index 8caa5a56..4284b212 100644 --- a/examples/misc/capture.py +++ b/examples/misc/capture.py @@ -16,14 +16,8 @@ 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') +output = PRJ.generate(to_task='syn', capture=True) +print(output) + +output = PRJ.transfer(devtype='detect', capture=True) +print(output) diff --git a/examples/multi/Makefile b/examples/multi/Makefile index cd77fa54..f166a9f9 100644 --- a/examples/multi/Makefile +++ b/examples/multi/Makefile @@ -3,4 +3,4 @@ SCRIPTS = $(wildcard *.py) all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT);) + @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT) || exit;) diff --git a/examples/multi/memory.py b/examples/multi/memory.py index fe67ee57..bbec7300 100644 --- a/examples/multi/memory.py +++ b/examples/multi/memory.py @@ -21,7 +21,4 @@ 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)) + PRJ.generate(to_task='syn') diff --git a/examples/multi/parameters.py b/examples/multi/parameters.py index 45d73ad9..30012888 100644 --- a/examples/multi/parameters.py +++ b/examples/multi/parameters.py @@ -41,7 +41,4 @@ # 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)) + PRJ.generate(to_task='syn') diff --git a/examples/multi/projects.py b/examples/multi/projects.py index 97cc83ec..dd7bb7ce 100644 --- a/examples/multi/projects.py +++ b/examples/multi/projects.py @@ -57,7 +57,4 @@ } for prj in PROJECTS: - try: - PROJECTS[prj].generate('syn') - except RuntimeError: - print('ERROR:generate:tool not found') + PROJECTS[prj].generate('syn') diff --git a/examples/multi/verilog.py b/examples/multi/verilog.py index bfce523e..a751f566 100644 --- a/examples/multi/verilog.py +++ b/examples/multi/verilog.py @@ -21,7 +21,4 @@ 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)) + PRJ.generate(to_task='syn') diff --git a/examples/multi/vhdl.py b/examples/multi/vhdl.py index b2563e41..7c6ee7d8 100644 --- a/examples/multi/vhdl.py +++ b/examples/multi/vhdl.py @@ -18,7 +18,4 @@ 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)) + PRJ.generate(to_task='syn') diff --git a/examples/openflow/icestorm.py b/examples/openflow/icestorm.py index 05d5f96b..77a0e529 100644 --- a/examples/openflow/icestorm.py +++ b/examples/openflow/icestorm.py @@ -44,13 +44,7 @@ prj.set_top('Top') if args.action in ['generate', 'all']: - try: - prj.generate() - except RuntimeError: - print('ERROR:generate:Docker not found') + prj.generate() if args.action in ['transfer', 'all']: - try: - prj.transfer() - except RuntimeError: - print('ERROR:transfer:Docker not found') + prj.transfer() diff --git a/examples/openflow/prjtrellis.py b/examples/openflow/prjtrellis.py index 59eb8311..86255567 100644 --- a/examples/openflow/prjtrellis.py +++ b/examples/openflow/prjtrellis.py @@ -44,13 +44,7 @@ prj.set_top('Top') if args.action in ['generate', 'all']: - try: - prj.generate() - except RuntimeError: - print('ERROR:generate:Docker not found') + prj.generate() if args.action in ['transfer', 'all']: - try: - prj.transfer() - except RuntimeError: - print('ERROR:transfer:Docker not found') + prj.transfer() diff --git a/examples/quartus/Makefile b/examples/quartus/Makefile index cd77fa54..f166a9f9 100644 --- a/examples/quartus/Makefile +++ b/examples/quartus/Makefile @@ -3,4 +3,4 @@ SCRIPTS = $(wildcard *.py) all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT);) + @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT) || exit;) diff --git a/examples/quartus/quartus.py b/examples/quartus/quartus.py index 809036b0..6516fcd0 100644 --- a/examples/quartus/quartus.py +++ b/examples/quartus/quartus.py @@ -26,13 +26,7 @@ prj.add_files('de10nano.tcl') if args.action in ['generate', 'all']: - try: - prj.generate() - except RuntimeError: - print('ERROR:generate:Quartus not found') + prj.generate() if args.action in ['transfer', 'all']: - try: - prj.transfer('fpga', 2) - except RuntimeError: - print('ERROR:transfer:Quartus not found') + prj.transfer('fpga', 2) diff --git a/examples/vivado/Makefile b/examples/vivado/Makefile index cd77fa54..f166a9f9 100644 --- a/examples/vivado/Makefile +++ b/examples/vivado/Makefile @@ -3,4 +3,4 @@ SCRIPTS = $(wildcard *.py) all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT);) + @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT) || exit;) diff --git a/examples/vivado/design.py b/examples/vivado/design.py index 4ea1cf31..7ac4eef3 100644 --- a/examples/vivado/design.py +++ b/examples/vivado/design.py @@ -30,7 +30,4 @@ prj.add_hook(export, 'postbit') -try: - prj.generate() -except Exception as e: - logging.warning('{} ({})'.format(type(e).__name__, e)) +prj.generate() diff --git a/examples/vivado/vivado.py b/examples/vivado/vivado.py index 5b00bd1a..e28c861b 100644 --- a/examples/vivado/vivado.py +++ b/examples/vivado/vivado.py @@ -24,13 +24,7 @@ prj.set_top('Blinking') if args.action in ['generate', 'all']: - try: - prj.generate() - except RuntimeError: - print('ERROR:generate:Vivado not found') + prj.generate() if args.action in ['transfer', 'all']: - try: - prj.transfer('fpga') - except RuntimeError: - print('ERROR:transfer:Vivado not found') + prj.transfer('fpga') diff --git a/examples/yosys/Makefile b/examples/yosys/Makefile index 9546d682..f07825c7 100644 --- a/examples/yosys/Makefile +++ b/examples/yosys/Makefile @@ -3,4 +3,4 @@ SCRIPTS = $(wildcard *.py) all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT); python3 $(SCRIPT) --lang vhdl; ) + @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT) || exit; python3 $(SCRIPT) --lang vhdl || exit; ) diff --git a/examples/yosys/ise.py b/examples/yosys/ise.py index b48d78b5..0d41d445 100644 --- a/examples/yosys/ise.py +++ b/examples/yosys/ise.py @@ -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/yosys/vivado.py index 1a5ea22f..d77061f8 100644 --- a/examples/yosys/vivado.py +++ b/examples/yosys/vivado.py @@ -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/yosys/yosys.py b/examples/yosys/yosys.py index 29316969..3f93ebfe 100644 --- a/examples/yosys/yosys.py +++ b/examples/yosys/yosys.py @@ -28,7 +28,4 @@ prj.set_top('Top') -try: - prj.generate() -except RuntimeError: - print('ERROR:generate:Docker not found') +prj.generate() diff --git a/fpga/project.py b/fpga/project.py index d9bc2f81..c6b5f18f 100644 --- a/fpga/project.py +++ b/fpga/project.py @@ -1,5 +1,5 @@ # -# Copyright (C) 2019-2021 Rodrigo A. Melo +# Copyright (C) 2019-2022 Rodrigo A. Melo # Copyright (C) 2019-2020 INTI # # This program is free software: you can redistribute it and/or modify @@ -17,10 +17,8 @@ # """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. +This module implements the entry-point of PyFPGA, which provides +functionalities to create a project, generate a bitstream and program a device. """ import contextlib @@ -48,7 +46,7 @@ class 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 init: a dict with metadata about the project :param relative_to_script: specifies if the files/directories are relative to the script or the execution directory :raises NotImplementedError: when tool is unsupported @@ -289,8 +287,8 @@ def generate(self, to_task='bit', from_task='prj', capture=False): :param capture: capture STDOUT and STDERR :returns: STDOUT and STDERR messages :raises ValueError: when from_task is later than to_task + :raises ValueError: when to_task or from_task are unsupported :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), @@ -329,9 +327,9 @@ def transfer( :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 + :raises RuntimeError: when the tool to be used is not found """ _log.info( 'transfering "%s" project using "%s" tool from "%s" directory', @@ -352,7 +350,7 @@ def clean(self): @contextlib.contextmanager def _run_in_dir(self): - """Runs the tool in another directory.""" + """Run the tool in another directory.""" start = time.time() try: if not os.path.exists(self.outdir): diff --git a/test/mocks/impact b/test/mocks/impact new file mode 100755 index 00000000..17eee4a6 --- /dev/null +++ b/test/mocks/impact @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 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 <http://www.gnu.org/licenses/>. +# + +import argparse + +parser = argparse.ArgumentParser() + +parser.add_argument('-batch', action='store_true', required=True) +parser.add_argument('source') + +args = parser.parse_args() + +print(f'INFO:the {parser.prog.upper()} mock has been executed') diff --git a/test/mocks/libero b/test/mocks/libero new file mode 100755 index 00000000..3124978a --- /dev/null +++ b/test/mocks/libero @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 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 <http://www.gnu.org/licenses/>. +# + +import argparse +import sys + +parser = argparse.ArgumentParser() + +parser.add_argument('source') + +args = parser.parse_args() + + +if not args.source.startswith("SCRIPT:", 0): + print('ERROR:the parameter should start width "SCRIPT:"') + sys.exit(1) + +print(f'INFO:the {parser.prog.upper()} mock has been executed') diff --git a/test/mocks/quartus b/test/mocks/quartus new file mode 100644 index 00000000..495c255e --- /dev/null +++ b/test/mocks/quartus @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 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 <http://www.gnu.org/licenses/>. +# + +import argparse + +parser = argparse.ArgumentParser() + +parser.add_argument('--script', required=True) + +args = parser.parse_args() + +print(f'INFO:the {parser.prog.upper()} mock has been executed') diff --git a/test/mocks/quartus_pgm b/test/mocks/quartus_pgm new file mode 100755 index 00000000..3353ee46 --- /dev/null +++ b/test/mocks/quartus_pgm @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 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 <http://www.gnu.org/licenses/>. +# + +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() + +print(f'INFO:the {parser.prog.upper()} mock has been executed') diff --git a/test/mocks/quartus_sh b/test/mocks/quartus_sh new file mode 100755 index 00000000..495c255e --- /dev/null +++ b/test/mocks/quartus_sh @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 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 <http://www.gnu.org/licenses/>. +# + +import argparse + +parser = argparse.ArgumentParser() + +parser.add_argument('--script', required=True) + +args = parser.parse_args() + +print(f'INFO:the {parser.prog.upper()} mock has been executed') diff --git a/test/mocks/vivado b/test/mocks/vivado new file mode 100755 index 00000000..7d598f7a --- /dev/null +++ b/test/mocks/vivado @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 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 <http://www.gnu.org/licenses/>. +# + +import argparse + +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() + +print(f'INFO:the {parser.prog.upper()} mock has been executed') diff --git a/test/mocks/xtclsh b/test/mocks/xtclsh new file mode 100755 index 00000000..71046506 --- /dev/null +++ b/test/mocks/xtclsh @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 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 <http://www.gnu.org/licenses/>. +# + +import argparse + +parser = argparse.ArgumentParser() + +parser.add_argument('source') + +args = parser.parse_args() + +print(f'INFO:the {parser.prog.upper()} mock has been executed') From 06c31d433abf5b8de08e4c34d2139ad3eaf4575b Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Tue, 24 May 2022 21:53:10 -0300 Subject: [PATCH 004/248] ci: fixed an 'exit status 2' issue --- .github/workflows/lint.yml | 1 - .github/workflows/test.yml | 22 +++++++++++++--------- fpga/tool/template.sh | 3 ++- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0564dc22..7904c2d7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,7 +12,6 @@ jobs: run: | pip install pycodestyle pip install pylint - pip install . - name: Lint run: | pycodestyle fpga examples test diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2e5564e1..4af9850d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,6 +11,16 @@ jobs: python-version: [3.6, 3.7, 3.8, 3.9] steps: - uses: actions/checkout@v2 + with: + submodules: true + fetch-depth: 0 + - name: Pull container images + run: | + docker pull hdlc/ghdl:yosys + docker pull hdlc/nextpnr:ice40 + docker pull hdlc/nextpnr:ecp5 + docker pull hdlc/icestorm + docker pull hdlc/prjtrellis - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: @@ -19,14 +29,8 @@ jobs: 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 + - name: Run test + run: pytest + - name: Run examples run: | - pytest cd examples; make MOCKS=1 diff --git a/fpga/tool/template.sh b/fpga/tool/template.sh index c10dbdd6..06d82b20 100644 --- a/fpga/tool/template.sh +++ b/fpga/tool/template.sh @@ -73,7 +73,8 @@ MODULE= [ -n "$VHDLS" ] && MODULE="-m ghdl" function print () {{ - tput setaf 6; echo ">>> PyFPGA ($1): $2"; tput sgr0; + # tput setaf 6; echo ">>> PyFPGA ($1): $2"; tput sgr0; + echo ">>> PyFPGA ($1): $2" }} ############################################################################### From b896134e26977808809a731d44fa91376623c7b2 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Tue, 24 May 2022 22:20:55 -0300 Subject: [PATCH 005/248] ci: added python 3.10 --- .github/workflows/test.yml | 6 +++--- setup.py | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4af9850d..533e2019 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8, 3.9] + pyver: ['3.6', '3.7', '3.8', '3.9', '3.10'] steps: - uses: actions/checkout@v2 with: @@ -21,10 +21,10 @@ jobs: docker pull hdlc/nextpnr:ecp5 docker pull hdlc/icestorm docker pull hdlc/prjtrellis - - name: Set up Python ${{ matrix.python-version }} + - name: Set up Python ${{ matrix.pyver }} uses: actions/setup-python@v2 with: - python-version: ${{ matrix.python-version }} + python-version: ${{ matrix.pyver }} - name: Install dependencies run: | pip install pytest diff --git a/setup.py b/setup.py index 90266a5d..c0e7ae4c 100644 --- a/setup.py +++ b/setup.py @@ -34,6 +34,7 @@ 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'Topic :: Utilities', 'Topic :: Software Development :: Build Tools', "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)" From fda3a825c0dbec21a1a2ba8dc0c54fb73f74f5e9 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Tue, 24 May 2022 22:35:30 -0300 Subject: [PATCH 006/248] ci: added schedule based on cron --- .github/workflows/test.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 533e2019..9ff37c04 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,13 +2,18 @@ name: 'test' on: push: + pull_request: + schedule: # Run once a week to ensure tests pass with updated dependencies + - cron: '0 0 * * 6' jobs: test: - runs-on: ubuntu-latest strategy: matrix: + os: ['ubuntu'] pyver: ['3.6', '3.7', '3.8', '3.9', '3.10'] + runs-on: ${{ matrix.os }}-latest + name: ${{ matrix.os }} | ${{ matrix.pyver }} steps: - uses: actions/checkout@v2 with: From dbfc133ad0bcdf0a44361fd0911e17b365aec710 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 25 May 2022 23:01:04 -0300 Subject: [PATCH 007/248] fpga: renamed add_path as add_vlog_include --- doc/basic.rst | 4 ++-- doc/intro.rst | 6 +++--- examples/multi/parameters.py | 4 ++-- examples/multi/verilog.py | 4 ++-- examples/openflow/icestorm.py | 4 ++-- examples/openflow/prjtrellis.py | 4 ++-- examples/yosys/ise.py | 4 ++-- examples/yosys/vivado.py | 4 ++-- examples/yosys/yosys.py | 4 ++-- fpga/helpers/hdl2bit.py | 2 +- fpga/project.py | 8 ++++---- fpga/tool/__init__.py | 4 ++-- 12 files changed, 26 insertions(+), 26 deletions(-) diff --git a/doc/basic.rst b/doc/basic.rst index a0d081ba..7a5456c8 100644 --- a/doc/basic.rst +++ b/doc/basic.rst @@ -70,8 +70,8 @@ file extension, and if it is a member of a VHDL package. 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. + * In case of Verilog, ``add_vlog_include`` can be used to specify where to + search for included files. Finally, the top-level must be specified: diff --git a/doc/intro.rst b/doc/intro.rst index db1d89d4..55d3576c 100644 --- a/doc/intro.rst +++ b/doc/intro.rst @@ -46,11 +46,11 @@ Detailed support +------------------------------+---------+----------+------------+-----------+----------+ |``std_logic_vector`` (*VHDL*) | ``TBD`` | ``TBD`` |``TBD`` | ``TBD`` | ``TBD`` | +------------------------------+---------+----------+------------+-----------+----------+ -|**add_path** | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | +|**add_vlog_include** | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | +------------------------------+---------+----------+------------+-----------+----------+ -|**set_define** (*Verilog*) | ``TBI`` | ``TBI`` | ``TBI`` | ``TBI`` | ``TBI`` | +|**add_vlog_define** | ``TBI`` | ``TBI`` | ``TBI`` | ``TBI`` | ``TBI`` | +------------------------------+---------+----------+------------+-----------+----------+ -|**set_arch** (*VHDL*) | ``TBI`` | ``TBI`` | ``TBI`` | ``TBI`` | ``TBI`` | +|**set_vhdl_arch** | ``TBI`` | ``TBI`` | ``TBI`` | ``TBI`` | ``TBI`` | +------------------------------+---------+----------+------------+-----------+----------+ |**generate** | | | | | | +------------------------------+---------+----------+------------+-----------+----------+ diff --git a/examples/multi/parameters.py b/examples/multi/parameters.py index 30012888..107bd2d4 100644 --- a/examples/multi/parameters.py +++ b/examples/multi/parameters.py @@ -26,8 +26,8 @@ if hdl == 'vhdl': PRJ.add_files('../../hdl/blinking.vhdl') else: - 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.set_top('Blinking') # PRJ.set_param('INT', '15') diff --git a/examples/multi/verilog.py b/examples/multi/verilog.py index a751f566..b16be138 100644 --- a/examples/multi/verilog.py +++ b/examples/multi/verilog.py @@ -16,8 +16,8 @@ continue PRJ = Project(tool) PRJ.set_outdir('../../build/multi/verilog/%s' % tool) - 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') PRJ.set_top('Top') diff --git a/examples/openflow/icestorm.py b/examples/openflow/icestorm.py index 77a0e529..a71bec78 100644 --- a/examples/openflow/icestorm.py +++ b/examples/openflow/icestorm.py @@ -31,8 +31,8 @@ prj.set_part(BOARDS[args.board][0]) 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' diff --git a/examples/openflow/prjtrellis.py b/examples/openflow/prjtrellis.py index 86255567..bece88a9 100644 --- a/examples/openflow/prjtrellis.py +++ b/examples/openflow/prjtrellis.py @@ -31,8 +31,8 @@ prj.set_part(BOARDS[args.board][0]) 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' diff --git a/examples/yosys/ise.py b/examples/yosys/ise.py index 0d41d445..6a900be9 100644 --- a/examples/yosys/ise.py +++ b/examples/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' diff --git a/examples/yosys/vivado.py b/examples/yosys/vivado.py index d77061f8..4dfae60a 100644 --- a/examples/yosys/vivado.py +++ b/examples/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' diff --git a/examples/yosys/yosys.py b/examples/yosys/yosys.py index 3f93ebfe..3553d515 100644 --- a/examples/yosys/yosys.py +++ b/examples/yosys/yosys.py @@ -17,8 +17,8 @@ prj.set_outdir('../../build/yosys-{}'.format(args.lang)) 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' diff --git a/fpga/helpers/hdl2bit.py b/fpga/helpers/hdl2bit.py index 68378ca9..b73d0b8a 100644 --- a/fpga/helpers/hdl2bit.py +++ b/fpga/helpers/hdl2bit.py @@ -133,7 +133,7 @@ def main(): if args.include is not None: for include in args.include: - prj.add_path(include) + prj.add_vlog_include(include) if args.file is not None: for file in args.file: diff --git a/fpga/project.py b/fpga/project.py index c6b5f18f..e2b2d6e9 100644 --- a/fpga/project.py +++ b/fpga/project.py @@ -108,7 +108,7 @@ def _initialize(self, init): if 'paths' in init: for path in init['paths']: _log.debug('PATH = %s', path) - self.add_path(path) + self.add_vlog_include(path) for filetype in ['vhdl', 'verilog', 'constraint']: if filetype in init: for file in init[filetype]: @@ -208,8 +208,8 @@ def get_files(self): """ return self.tool.get_files() - def add_path(self, path): - """Add a search path. + def add_vlog_include(self, path): + """Add a Verilog include path. Useful to specify where to search Verilog Included Files or IP repositories. @@ -221,7 +221,7 @@ def add_path(self, path): path = os.path.normpath(path) if os.path.isdir(path): path = os.path.relpath(path, self.outdir) - self.tool.add_path(path) + self.tool.add_vlog_include(path) else: raise NotADirectoryError(path) diff --git a/fpga/tool/__init__.py b/fpga/tool/__init__.py index 1f82fc3e..54e8f594 100644 --- a/fpga/tool/__init__.py +++ b/fpga/tool/__init__.py @@ -143,8 +143,8 @@ def get_files(self): """Get the files of the project.""" return self.files - def add_path(self, path): - """Add a search path.""" + def add_vlog_include(self, path): + """Add a Verilog include path.""" self.paths.append(path) def set_top(self, top): From 579f275d51a7f1c87e196b90679f71e4e7e9015a Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 25 May 2022 23:06:45 -0300 Subject: [PATCH 008/248] fpga: added add_vlog_define and set_vhdl_arch in project.py (not implemented) --- fpga/project.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fpga/project.py b/fpga/project.py index e2b2d6e9..6567baa1 100644 --- a/fpga/project.py +++ b/fpga/project.py @@ -225,6 +225,14 @@ def add_vlog_include(self, path): else: raise NotADirectoryError(path) + def add_vlog_define(self, name, value): + """Add a Verilog define.""" + raise NotImplementedError() + + def set_vhdl_arch(self, name): + """Set the VHDL architecture.""" + raise NotImplementedError() + def set_top(self, toplevel): """Set the top level of the project. From 6699cd91bd097a1fce2abbb6a647cabf70a75473 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 26 May 2022 22:14:25 -0300 Subject: [PATCH 009/248] fpga: renamed set_param as add_param --- doc/advanced.rst | 4 ++-- doc/intro.rst | 2 +- examples/multi/parameters.py | 14 +++++++------- examples/vivado/vivado.py | 2 +- fpga/helpers/hdl2bit.py | 2 +- fpga/project.py | 8 ++++---- fpga/tool/__init__.py | 4 ++-- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/doc/advanced.rst b/doc/advanced.rst index 0cfea3af..9ff52609 100644 --- a/doc/advanced.rst +++ b/doc/advanced.rst @@ -99,9 +99,9 @@ The generics/parameters of the project can be optionally changed with: .. code-block:: python - prj.set_param('param1', value1) + prj.add_param('param1', value1) ... - prj.set_param('paramN', valueN) + prj.add_param('paramN', valueN) Generate options ================ diff --git a/doc/intro.rst b/doc/intro.rst index 55d3576c..62d93d7e 100644 --- a/doc/intro.rst +++ b/doc/intro.rst @@ -32,7 +32,7 @@ Detailed support +------------------------------+---------+----------+------------+-----------+----------+ |``block_design`` | ``NY`` | ``NY`` | ``NY`` | ``NY`` | ``Yes`` | +------------------------------+---------+----------+------------+-----------+----------+ -|**set_param** | | | | | | +|**add_param** | | | | | | +------------------------------+---------+----------+------------+-----------+----------+ |``boolean`` (*VHDL/Verilog*) | ``TBD`` | ``TBD`` |``TBD`` | ``TBD`` | ``TBD`` | +------------------------------+---------+----------+------------+-----------+----------+ diff --git a/examples/multi/parameters.py b/examples/multi/parameters.py index 107bd2d4..f1912c85 100644 --- a/examples/multi/parameters.py +++ b/examples/multi/parameters.py @@ -20,8 +20,8 @@ if tool in ['openflow', 'yosys', 'yosys-ise', 'yosys-vivado']: continue PRJ = Project(tool) - PRJ.set_param('FREQ', '50000000') - PRJ.set_param('SECS', '2') + PRJ.add_param('FREQ', '50000000') + PRJ.add_param('SECS', '2') PRJ.set_outdir('../../build/multi/params/%s/%s' % (tool, hdl)) if hdl == 'vhdl': PRJ.add_files('../../hdl/blinking.vhdl') @@ -30,11 +30,11 @@ PRJ.add_vlog_include('../../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.add_param('INT', '15') + # PRJ.add_param('REA', '1.5') + # PRJ.add_param('LOG', "'1'") + # PRJ.add_param('VEC', '"10101010"') + # PRJ.add_param('STR', '"WXYZ"') # PRJ.set_outdir('../../build/multi/params/%s/%s' % (tool, hdl)) # if hdl == 'vhdl': # PRJ.add_files('../../hdl/fakes/generics.vhdl') diff --git a/examples/vivado/vivado.py b/examples/vivado/vivado.py index e28c861b..bf1830a1 100644 --- a/examples/vivado/vivado.py +++ b/examples/vivado/vivado.py @@ -18,7 +18,7 @@ prj.set_outdir('../../build/vivado') -prj.set_param('FREQ', '125000000') +prj.add_param('FREQ', '125000000') prj.add_files('../../hdl/blinking.vhdl') prj.add_files('zybo.xdc') prj.set_top('Blinking') diff --git a/fpga/helpers/hdl2bit.py b/fpga/helpers/hdl2bit.py index b73d0b8a..87ed4159 100644 --- a/fpga/helpers/hdl2bit.py +++ b/fpga/helpers/hdl2bit.py @@ -145,7 +145,7 @@ def main(): if args.param is not None: for param in args.param: - prj.set_param(param[0], param[1]) + prj.add_param(param[0], param[1]) prj.add_files(args.top) prj.set_top(args.top) diff --git a/fpga/project.py b/fpga/project.py index 6567baa1..d3f84a68 100644 --- a/fpga/project.py +++ b/fpga/project.py @@ -125,7 +125,7 @@ def _initialize(self, init): if 'params' in init: for parname, parvalue in init['params'].items(): _log.debug('PARAM = %s %s', parname, parvalue) - self.set_param(parname, parvalue) + self.add_param(parname, parvalue) if 'top' in init: _log.debug('TOP = %s', init['top']) self.set_top(init['top']) @@ -154,13 +154,13 @@ def set_part(self, part): """ self.tool.set_part(part) - def set_param(self, name, value): - """Set a Generic/Parameter Value. + def add_param(self, name, value): + """Add a Generic/Parameter Value. :param name: parameter/generic name :param value: value to be assigned """ - self.tool.set_param(name, value) + self.tool.add_param(name, value) def add_files(self, pathname, filetype=None, library=None, options=None): """Adds files to the project. diff --git a/fpga/tool/__init__.py b/fpga/tool/__init__.py index 54e8f594..8ced52bb 100644 --- a/fpga/tool/__init__.py +++ b/fpga/tool/__init__.py @@ -130,8 +130,8 @@ def set_part(self, part): """Set the target PART.""" self.part['name'] = part - def set_param(self, name, value): - """Set a Generic/Parameter Value.""" + def add_param(self, name, value): + """Add a Generic/Parameter Value.""" self.params.append([name, value]) def add_file(self, file, filetype, library, options): From d1bd35cffa035104761e12e3c22f1e4b2f00de4b Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 26 May 2022 22:52:45 -0300 Subject: [PATCH 010/248] fpga: renamed 'imp' as 'par' --- doc/advanced.rst | 8 ++++---- doc/intro.rst | 2 +- fpga/project.py | 4 ++-- fpga/tool/__init__.py | 8 ++++---- fpga/tool/template.sh | 6 +++--- fpga/tool/template.tcl | 20 ++++++++++---------- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/doc/advanced.rst b/doc/advanced.rst index 9ff52609..3ee54d32 100644 --- a/doc/advanced.rst +++ b/doc/advanced.rst @@ -68,9 +68,9 @@ The following table depicts the parts of the *Project Creation* and the +--------------------------+----------------------+ | Files addition | **postsyn** hook | +--------------------------+----------------------+ -| Top specification | Implementation | +| Top specification | Place and Route | +--------------------------+----------------------+ -| Parameters specification | **postimp** hook | +| Parameters specification | **postpar** hook | +--------------------------+----------------------+ | **project** hook | Bitstream generation | +--------------------------+----------------------+ @@ -87,7 +87,7 @@ specify additional *hooks* in different parts of the flow, using: .. NOTE:: * Valid vaues for *phase* are ``prefile``, ``project`` (default), ``preflow``, - ``postsyn``, ``postimp`` and ``postbit``. + ``postsyn``, ``postpar`` 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). @@ -115,7 +115,7 @@ The method ``generate`` (previously seen at the end of 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``. +invoqued. The order and available tasks are ``prj``, ``syn``, ``par`` 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 diff --git a/doc/intro.rst b/doc/intro.rst index 62d93d7e..67c8e087 100644 --- a/doc/intro.rst +++ b/doc/intro.rst @@ -58,7 +58,7 @@ Detailed support +------------------------------+---------+----------+------------+-----------+----------+ |``syn`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | +------------------------------+---------+----------+------------+-----------+----------+ -|``imp`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | +|``par`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | +------------------------------+---------+----------+------------+-----------+----------+ |``bit`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | +------------------------------+---------+----------+------------+-----------+----------+ diff --git a/fpga/project.py b/fpga/project.py index d3f84a68..f3f99b7a 100644 --- a/fpga/project.py +++ b/fpga/project.py @@ -278,8 +278,8 @@ def add_hook(self, hook, phase='project'): ``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 + ``postsyn`` (to perform an action between *syn* and *par*), + ``postpar`` (to perform an action between *par* and *bit*) and ``postbit`` (to perform an action after *bit*) .. warning:: Using a hook, you will be probably broken the vendor diff --git a/fpga/tool/__init__.py b/fpga/tool/__init__.py index 8ced52bb..9fc09685 100644 --- a/fpga/tool/__init__.py +++ b/fpga/tool/__init__.py @@ -30,8 +30,8 @@ FILETYPES = ['verilog', 'vhdl', 'constraint', 'design'] MEMWIDTHS = [1, 2, 4, 8, 16, 32] -PHASES = ['prefile', 'project', 'preflow', 'postsyn', 'postimp', 'postbit'] -TASKS = ['prj', 'syn', 'imp', 'bit'] +PHASES = ['prefile', 'project', 'preflow', 'postsyn', 'postpar', 'postbit'] +TASKS = ['prj', 'syn', 'par', 'bit'] def check_value(value, values): @@ -83,7 +83,7 @@ def __init__(self, project): 'project': [], 'preflow': [], 'postsyn': [], - 'postimp': [], + 'postpar': [], 'postbit': [] } self.files = { @@ -202,7 +202,7 @@ def _create_gen_script(self, tasks): 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('#POSTPAR_CMDS#', '\n'.join(self.cmds['postpar'])) 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) diff --git a/fpga/tool/template.sh b/fpga/tool/template.sh index 06d82b20..f2b271eb 100644 --- a/fpga/tool/template.sh +++ b/fpga/tool/template.sh @@ -40,7 +40,7 @@ INCLUDES="{includes}" VERILOGS="{verilogs}" CONSTRAINTS="{constraints}" -# taks = prj syn imp bit +# taks = prj syn par bit TASKS="{tasks}" # @@ -137,9 +137,9 @@ fi # Place and Route ############################################################################### -if [[ $TASKS == *"imp"* ]]; then +if [[ $TASKS == *"par"* ]]; then -print "nextpnr-$FAMILY" "running 'implementation'" +print "nextpnr-$FAMILY" "running 'place and route'" INPUT="--json $PROJECT.json" diff --git a/fpga/tool/template.tcl b/fpga/tool/template.tcl index 238f6891..7466d534 100644 --- a/fpga/tool/template.tcl +++ b/fpga/tool/template.tcl @@ -18,7 +18,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # # Description: Tcl script to create a new project and performs synthesis, -# implementation and bitstream generation. +# place and route, and bitstream generation. # # Supported TOOLs: ise, libero, quartus, vivado # @@ -41,7 +41,7 @@ set DEVICE #DEVICE# set PACKAGE #PACKAGE# set SPEED #SPEED# set TOP #TOP# -# TASKS = prj syn imp bit +# TASKS = prj syn par bit set TASKS [list #TASKS#] set PARAMS [list #PARAMS#] @@ -65,8 +65,8 @@ proc fpga_commands { PHASE } { "postsyn" { #POSTSYN_CMDS# } - "postimp" { -#POSTIMP_CMDS# + "postpar" { +#POSTPAR_CMDS# } "postbit" { #POSTBIT_CMDS# @@ -384,9 +384,9 @@ proc fpga_run_syn {} { } } -proc fpga_run_imp {} { +proc fpga_run_par {} { global TOOL PRESYNTH - fpga_print "running 'implementation'" + fpga_print "running 'place and route'" switch $TOOL { "ise" { process run "Translate" @@ -470,7 +470,7 @@ if { [lsearch -exact $TASKS "prj"] >= 0 } { # Design Flow # -if { [lsearch -regexp $TASKS "syn|imp|bit"] >= 0 } { +if { [lsearch -regexp $TASKS "syn|par|bit"] >= 0 } { fpga_print "running the Design Flow" if { [catch { fpga_open $PROJECT @@ -479,9 +479,9 @@ if { [lsearch -regexp $TASKS "syn|imp|bit"] >= 0 } { fpga_run_syn fpga_commands "postsyn" } - if { [lsearch -exact $TASKS "imp"] >= 0 } { - fpga_run_imp - fpga_commands "postimp" + if { [lsearch -exact $TASKS "par"] >= 0 } { + fpga_run_par + fpga_commands "postpar" } if { [lsearch -exact $TASKS "bit"] >= 0 } { fpga_run_bit From bbdc605c5ab17c680af87db097bec8a550a1831d Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 26 May 2022 23:32:58 -0300 Subject: [PATCH 011/248] Updated shields in README.md --- README.md | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index eb969487..97fad22f 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,10 @@ # 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) -![Yosys](https://img.shields.io/badge/Yosys-last-brightgreen.svg?style=flat-square) -![nextpnr](https://img.shields.io/badge/nextpnr-last-brightgreen.svg?style=flat-square) -![icestorm](https://img.shields.io/badge/icestorm-last-brightgreen.svg?style=flat-square) -![prjtrellis](https://img.shields.io/badge/prjtrellis-last-brightgreen.svg?style=flat-square) - ![Vivado](https://img.shields.io/badge/Vivado-2019.2-blue.svg?style=flat-square) ![Quartus](https://img.shields.io/badge/Quartus--Prime-19.1-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) +![ISE](https://img.shields.io/badge/ISE-14.7-blue.svg?style=flat-square) +![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) > **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. @@ -95,7 +90,7 @@ Should you achieve either success of failure on non-POSIX systems, please let us **Notes:** - The open-source tools are supported trough container images from the -[ghdl/docker](https://github.com/ghdl/docker) project, so +[HDL containers](https://hdl.github.io/containers) 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 From fe8bcbcd383037fe893d34be206896c4b3c62bff Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 27 May 2022 00:02:29 -0300 Subject: [PATCH 012/248] fpga: renamed 'init' as 'meta' --- examples/multi/projects.py | 6 +++--- fpga/project.py | 40 +++++++++++++++++++------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/examples/multi/projects.py b/examples/multi/projects.py index dd7bb7ce..3d267a93 100644 --- a/examples/multi/projects.py +++ b/examples/multi/projects.py @@ -14,7 +14,7 @@ 'prj1': Project( 'vivado', 'vivado-prj', - { + meta={ 'outdir': '../../build/multi/projects/vivado', 'part': 'xc7k70t-3-fbg484', 'vhdl': [ @@ -28,7 +28,7 @@ 'prj2': Project( 'ise', 'ise-prj', - { + meta={ 'outdir': '../../build/multi/projects/ise', 'part': 'xc6slx9-2-csg324', 'vhdl': [ @@ -40,7 +40,7 @@ 'prj3': Project( 'quartus', 'qurtus-prj', - { + meta={ 'outdir': '../../build/multi/projects/quartus', 'part': '5CEBA2F17A7', 'paths': [ diff --git a/fpga/project.py b/fpga/project.py index f3f99b7a..069d2be7 100644 --- a/fpga/project.py +++ b/fpga/project.py @@ -46,7 +46,7 @@ class Project: :param tool: FPGA tool to be used :param project: project name (the tool name is used if none specified) - :param init: a dict with metadata about the project + :param meta: a dict with metadata about the project :param relative_to_script: specifies if the files/directories are relative to the script or the execution directory :raises NotImplementedError: when tool is unsupported @@ -57,7 +57,7 @@ class Project: """ def __init__( - self, tool='vivado', project=None, init=None, + self, tool='vivado', project=None, meta=None, relative_to_script=True): """Class constructor.""" if tool == 'ghdl': @@ -93,25 +93,25 @@ def __init__( self._absdir = os.path.join(self._rundir, self._reldir) _log.debug('ABSDIR = %s', self._absdir) self.set_outdir('build') - self._initialize(init) + self._initialize(meta) - def _initialize(self, init): + def _initialize(self, meta): """Set some of the most used internal parameters.""" - if init is None: + if meta 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']: + if 'outdir' in meta: + _log.debug('OUTDIR = %s', meta['outdir']) + self.set_outdir(meta['outdir']) + if 'part' in meta: + _log.debug('PART = %s', meta['part']) + self.set_part(meta['part']) + if 'paths' in meta: + for path in meta['paths']: _log.debug('PATH = %s', path) self.add_vlog_include(path) for filetype in ['vhdl', 'verilog', 'constraint']: - if filetype in init: - for file in init[filetype]: + if filetype in meta: + for file in meta[filetype]: if isinstance(file, list): filename = file[0] library = file[1] @@ -122,13 +122,13 @@ def _initialize(self, init): 'FILE = %s %s %s', filename, filetype, library ) self.add_files(filename, filetype, library) - if 'params' in init: - for parname, parvalue in init['params'].items(): + if 'params' in meta: + for parname, parvalue in meta['params'].items(): _log.debug('PARAM = %s %s', parname, parvalue) self.add_param(parname, parvalue) - if 'top' in init: - _log.debug('TOP = %s', init['top']) - self.set_top(init['top']) + if 'top' in meta: + _log.debug('TOP = %s', meta['top']) + self.set_top(meta['top']) def set_outdir(self, outdir): """Sets the OUTput DIRectory (where to put the resulting files). From 8777ee8ebfba2dffd41b29f5d6d330d96610e353 Mon Sep 17 00:00:00 2001 From: lmcapacho <lmcapacho@gmail.com> Date: Tue, 31 Jan 2023 18:46:32 -0500 Subject: [PATCH 013/248] fix: ModuleNotFoundError: No module named 'fpga.helpers' --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c0e7ae4c..05036c4a 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ author_email='rodrigomelo9@gmail.com', license='GPLv3', url='https://github.com/PyFPGA/pyfpga', - package_data={'': ['tool/*.sh', 'tool/*.tcl']}, + package_data={'': ['tool/*.sh', 'tool/*.tcl', 'helpers/*']}, packages=find_packages(), entry_points={ 'console_scripts': [ From 7f49fdf6dc873e56132c61bf902ee463199fa90e Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 19 May 2024 21:27:38 -0300 Subject: [PATCH 014/248] Improved existing mocks --- test/mocks/libero | 22 +++++++++++++++++++++- test/mocks/quartus | 27 --------------------------- test/mocks/quartus_sh | 42 +++++++++++++++++++++++++++++++++++++++++- test/mocks/vivado | 43 ++++++++++++++++++++++++++++++++++++++++++- test/mocks/xtclsh | 23 ++++++++++++++++++++++- 5 files changed, 126 insertions(+), 31 deletions(-) delete mode 100644 test/mocks/quartus diff --git a/test/mocks/libero b/test/mocks/libero index 3124978a..b0f0b691 100755 --- a/test/mocks/libero +++ b/test/mocks/libero @@ -17,17 +17,37 @@ # import argparse +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) -print(f'INFO:the {parser.prog.upper()} mock has been executed') +tcl = f''' +proc unknown {{ cmmd args }} {{ }} + +source {args.source.replace('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, + #stdout=output, stderr=subprocess.STDOUT +) + +print(f'INFO:the {tool.upper()} mock has been executed') diff --git a/test/mocks/quartus b/test/mocks/quartus deleted file mode 100644 index 495c255e..00000000 --- a/test/mocks/quartus +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2022 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 <http://www.gnu.org/licenses/>. -# - -import argparse - -parser = argparse.ArgumentParser() - -parser.add_argument('--script', required=True) - -args = parser.parse_args() - -print(f'INFO:the {parser.prog.upper()} mock has been executed') diff --git a/test/mocks/quartus_sh b/test/mocks/quartus_sh index 495c255e..a002e73d 100755 --- a/test/mocks/quartus_sh +++ b/test/mocks/quartus_sh @@ -17,6 +17,9 @@ # import argparse +import os +import subprocess + parser = argparse.ArgumentParser() @@ -24,4 +27,41 @@ parser.add_argument('--script', required=True) args = parser.parse_args() -print(f'INFO:the {parser.prog.upper()} mock has been executed') +tool = parser.prog + +tcl = f''' +lappend auto_path pkg + +proc unknown {{ cmmd args }} {{ }} + +source {args.script} +''' + +with open(f'{tool}-mock.tcl', 'w', encoding='utf-8') as file: + file.write(tcl) + +tcl = f''' +namespace eval ::quartus {{ + namespace export project +}} +''' + +if not os.path.exists('pkg'): + os.makedirs('pkg') + +package ifneeded ::quartus 1.0 [list source quartus-pkg.tcl] + +pkgIndex.tcl + +with open(f'pkg/quartus.tcl', 'w', encoding='utf-8') as file: + file.write(tcl) + +subprocess.run( + f'tclsh {tool}-mock.tcl', + shell=True, + check=True, + universal_newlines=True, + #stdout=output, stderr=subprocess.STDOUT +) + +print(f'INFO:the {tool.upper()} mock has been executed') diff --git a/test/mocks/vivado b/test/mocks/vivado index 7d598f7a..af5614d1 100755 --- a/test/mocks/vivado +++ b/test/mocks/vivado @@ -17,6 +17,8 @@ # import argparse +import subprocess + parser = argparse.ArgumentParser() @@ -27,4 +29,43 @@ parser.add_argument('-source', required=True) args = parser.parse_args() -print(f'INFO:the {parser.prog.upper()} mock has been executed') +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 {{ cmmd 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, + #stdout=output, stderr=subprocess.STDOUT +) + +print(f'INFO:the {tool.upper()} mock has been executed') diff --git a/test/mocks/xtclsh b/test/mocks/xtclsh index 71046506..12046fa3 100755 --- a/test/mocks/xtclsh +++ b/test/mocks/xtclsh @@ -17,6 +17,8 @@ # import argparse +import subprocess + parser = argparse.ArgumentParser() @@ -24,4 +26,23 @@ parser.add_argument('source') args = parser.parse_args() -print(f'INFO:the {parser.prog.upper()} mock has been executed') +tool = parser.prog + +tcl = f''' +proc unknown {{ cmmd 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, + #stdout=output, stderr=subprocess.STDOUT +) + +print(f'INFO:the {tool.upper()} mock has been executed') From 4cc3513e05b2643475e174b53542e533c6719b77 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 25 May 2024 23:51:41 -0300 Subject: [PATCH 015/248] Added a new WIP Project class under the pyfpga directory --- pyfpga/project.py | 119 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 pyfpga/project.py diff --git a/pyfpga/project.py b/pyfpga/project.py new file mode 100644 index 00000000..881a7187 --- /dev/null +++ b/pyfpga/project.py @@ -0,0 +1,119 @@ +# +# Copyright (C) 2019-2024 Rodrigo A. Melo +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +"""pyfpga.project +This module implements the entry-point of PyFPGA, which provides +functionalities to create a project, generate a bitstream and +program a device. +""" + +from pathlib import Path + +TASKS = ['prj', 'elb', 'syn', 'par', 'bit'] + +TOOLS = [ + 'ghdl', + 'ise', + 'libero', + 'openflow', + 'quartus', + 'vivado', + 'yosys', + 'yosys-ise', + 'yosys-vivado' +] + + +class Project: + """Class to manage an FPGA project. + + :param tool: tool name + :type tool: str + :param name: project name (tool name by default) + :type name: str, optional + :param data: pre-populated data for the project + :type data: dict, optional + :param odir: output directory + :type odir: str, optional + :raises NotImplementedError: unsupported tool + + .. note:: + Supported tool names are: + ``ghdl`` + ``ise`` + ``libero`` + ``openflow`` + ``quartus`` + ``vivado`` + ``yosys`` + ``yosys-ise`` + ``yosys-vivado`` + """ + + def __init__(self, tool, name=None, data=None, odir='results'): + """Class constructor.""" + if tool not in TOOLS: + raise NotImplementedError(f'unsupported tool ({tool}).') + self.tool = tool + self.name = name or tool + self.data = data or {} + self.odir = Path(odir) + self.odir.mkdir(parents=True, exist_ok=True) + + def set_part(self, name): + """Templ placeholder""" + raise NotImplementedError('Method is not implemented yet.') + + def add_file(self, pathname, filetype=None, library=None, options=None): + """Templ placeholder""" + raise NotImplementedError('Method is not implemented yet.') + + def add_vlog(self, pathname, options=None): + """Templ placeholder""" + self.add_file(pathname, filetype='vlog', options=options) + + def add_slog(self, pathname, options=None): + """Templ placeholder""" + self.add_file(pathname, filetype='slog', options=options) + + def add_vhdl(self, pathname, library=None, options=None): + """Templ placeholder""" + self.add_file( + pathname, filetype='vhdl', + library=library, options=options + ) + + def add_param(self, name, value): + """Templ placeholder""" + raise NotImplementedError('Method is not implemented yet.') + + def add_include(self, path): + """Templ placeholder""" + raise NotImplementedError('Method is not implemented yet.') + + def add_define(self, name, value): + """Templ placeholder""" + raise NotImplementedError('Method is not implemented yet.') + + def set_arch(self, name): + """Templ placeholder""" + raise NotImplementedError('Method is not implemented yet.') + + def set_top(self, name): + """Templ placeholder""" + raise NotImplementedError('Method is not implemented yet.') + + def add_hook(self, stage, hook): + """Templ placeholder""" + raise NotImplementedError('Method is not implemented yet.') + + def make(self, last='bit', first='prj', capture=False): + """Templ placeholder""" + raise NotImplementedError('Method is not implemented yet.') + + def prog(self, position=1, bitstream=None): + """Templ placeholder""" + raise NotImplementedError('Method is not implemented yet.') From a9a25b1e37753146b38a21d3723c1ee279341c0d Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 25 May 2024 23:52:35 -0300 Subject: [PATCH 016/248] ci: updated, modified to analyze pyfpga instead of fpga --- .github/workflows/lint.yml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7904c2d7..61d227d9 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,12 +7,25 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Install dependencies run: | pip install pycodestyle pip install pylint - name: Lint run: | - pycodestyle fpga examples test - pylint fpga + pycodestyle pyfpga examples test + pylint pyfpga + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Install dependencies + run: pip install pycodestyle pylint + - name: Run linters + run: | + pycodestyle pyfpga examples test + pylint pyfpga From 4b037ae4b5b9f55522ac00518c8aea1bbf23684a Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 25 May 2024 23:57:13 -0300 Subject: [PATCH 017/248] ci: renamed doc as docs, disabled docs and test --- .github/workflows/{doc.yml => docs.yml} | 4 ++-- .github/workflows/test.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename .github/workflows/{doc.yml => docs.yml} (93%) diff --git a/.github/workflows/doc.yml b/.github/workflows/docs.yml similarity index 93% rename from .github/workflows/doc.yml rename to .github/workflows/docs.yml index ee0ecc54..a89f8848 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/docs.yml @@ -1,11 +1,11 @@ -name: 'doc' +name: 'docs' on: push: branches: - main -jobs: +.jobs: linux: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9ff37c04..25761f18 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,7 +6,7 @@ on: schedule: # Run once a week to ensure tests pass with updated dependencies - cron: '0 0 * * 6' -jobs: +.jobs: test: strategy: matrix: From 828815976d1ed96cfe7666197e51e109a7d1589d Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 25 May 2024 23:59:25 -0300 Subject: [PATCH 018/248] ci: fixed lint action --- .github/workflows/lint.yml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 61d227d9..73ce2436 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -3,20 +3,6 @@ name: 'lint' on: push: -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Install dependencies - run: | - pip install pycodestyle - pip install pylint - - name: Lint - run: | - pycodestyle pyfpga examples test - pylint pyfpga - jobs: lint: runs-on: ubuntu-latest From 73476674de47524c6d6fc9246a9793d294c9e126 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 26 May 2024 09:01:21 -0300 Subject: [PATCH 019/248] ci: updated Makefile and used for the lint action --- .github/workflows/lint.yml | 4 +--- Makefile | 8 +++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 73ce2436..5f912102 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,6 +12,4 @@ jobs: - name: Install dependencies run: pip install pycodestyle pylint - name: Run linters - run: | - pycodestyle pyfpga examples test - pylint pyfpga + run: make lint diff --git a/Makefile b/Makefile index 8f1d8dc1..f8f3ca10 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,11 @@ #!/usr/bin/make -check: - pycodestyle fpga examples test - pylint -s n fpga +lint: + pycodestyle pyfpga examples test + pylint -s n pyfpga git diff --check --cached + +test: pytest test clean: From 4d845b679843cfd66df16fa3fe75df794ba3e9f5 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 26 May 2024 13:42:54 -0300 Subject: [PATCH 020/248] Modified project to employ enumerations --- pyfpga/project.py | 104 ++++++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 45 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index 881a7187..25da8911 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -10,110 +10,124 @@ program a device. """ +from enum import Enum from pathlib import Path TASKS = ['prj', 'elb', 'syn', 'par', 'bit'] -TOOLS = [ - 'ghdl', - 'ise', - 'libero', - 'openflow', - 'quartus', - 'vivado', - 'yosys', - 'yosys-ise', - 'yosys-vivado' -] + +class Tool(Enum): + """Enumeration of supported FPGA tools.""" + GHDL = 'ghdl' + ISE = 'ise' + LIBERO = 'libero' + OPENFLOW = 'openflow' + QUARTUS = 'quartus' + VIVADO = 'vivado' + YOSYS = 'yosys' + YOSYS_ISE = 'yosys-ise' + YOSYS_VIVADO = 'yosys-vivado' + + +class Step(Enum): + """Enumeration of supported Steps""" + PRJ = 'prj' + ELB = 'elb' + SYN = 'syn' + PAR = 'par' + BIT = 'bit' + + +class Hook(Enum): + """Enumeration of supported Hooks""" + PREFILE = 'prefile' + PROJECT = 'project' + PREFLOW = 'preflow' + POSTSYN = 'postsyn' + POSTPAR = 'postpar' + POSTBIT = 'postbit' class Project: """Class to manage an FPGA project. :param tool: tool name - :type tool: str + :type tool: Tool :param name: project name (tool name by default) :type name: str, optional :param data: pre-populated data for the project :type data: dict, optional :param odir: output directory :type odir: str, optional - :raises NotImplementedError: unsupported tool - - .. note:: - Supported tool names are: - ``ghdl`` - ``ise`` - ``libero`` - ``openflow`` - ``quartus`` - ``vivado`` - ``yosys`` - ``yosys-ise`` - ``yosys-vivado`` + :raises TypeError: when a value is not a valid enum + :raises NotImplementedError: when a method is not implemented yet """ def __init__(self, tool, name=None, data=None, odir='results'): """Class constructor.""" - if tool not in TOOLS: - raise NotImplementedError(f'unsupported tool ({tool}).') + if not isinstance(tool, Tool): + raise TypeError('tool must be a Tool enum.') self.tool = tool - self.name = name or tool + self.name = name or tool.value self.data = data or {} self.odir = Path(odir) self.odir.mkdir(parents=True, exist_ok=True) def set_part(self, name): - """Templ placeholder""" + """Temp placeholder""" raise NotImplementedError('Method is not implemented yet.') def add_file(self, pathname, filetype=None, library=None, options=None): - """Templ placeholder""" + """Temp placeholder""" raise NotImplementedError('Method is not implemented yet.') def add_vlog(self, pathname, options=None): - """Templ placeholder""" + """Temp placeholder""" self.add_file(pathname, filetype='vlog', options=options) def add_slog(self, pathname, options=None): - """Templ placeholder""" + """Temp placeholder""" self.add_file(pathname, filetype='slog', options=options) def add_vhdl(self, pathname, library=None, options=None): - """Templ placeholder""" + """Temp placeholder""" self.add_file( pathname, filetype='vhdl', library=library, options=options ) - def add_param(self, name, value): - """Templ placeholder""" + def add_include(self, path): + """Temp placeholder""" raise NotImplementedError('Method is not implemented yet.') - def add_include(self, path): - """Templ placeholder""" + def add_param(self, name, value): + """Temp placeholder""" raise NotImplementedError('Method is not implemented yet.') def add_define(self, name, value): - """Templ placeholder""" + """Temp placeholder""" raise NotImplementedError('Method is not implemented yet.') def set_arch(self, name): - """Templ placeholder""" + """Temp placeholder""" raise NotImplementedError('Method is not implemented yet.') def set_top(self, name): - """Templ placeholder""" + """Temp placeholder""" raise NotImplementedError('Method is not implemented yet.') - def add_hook(self, stage, hook): - """Templ placeholder""" + def add_hook(self, hook, content): + """Temp placeholder""" + # if not isinstance(hook, Hook): + # raise TypeError('hook must be a Hook enum.') raise NotImplementedError('Method is not implemented yet.') - def make(self, last='bit', first='prj', capture=False): - """Templ placeholder""" + def make(self, end=Step.BIT, start=Step.PRJ, capture=False): + """Temp placeholder""" + # if not isinstance(end, Step) or not isinstance(start, Step): + # raise TypeError('start and end must be a Step enum.') raise NotImplementedError('Method is not implemented yet.') def prog(self, position=1, bitstream=None): - """Templ placeholder""" + """Temp placeholder""" raise NotImplementedError('Method is not implemented yet.') From 049af6ba8b6d7654ddbd51ad5a6e26a2ce08eae4 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 26 May 2024 13:43:32 -0300 Subject: [PATCH 021/248] Added docs with the skeleton generated by sphinx-quickstart --- docs/Makefile | 20 +++++++++++++++++++ docs/conf.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ docs/index.rst | 20 +++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 docs/Makefile create mode 100644 docs/conf.py create mode 100644 docs/index.rst diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..d4bb2cbb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..cddcb632 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,52 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +project = 'PyFPGA' +copyright = '2024, Rodrigo Alejandro Melo' +author = 'Rodrigo Alejandro Melo' + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'alabaster' + +# 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'] \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..d2be65e2 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,20 @@ +.. PyFPGA documentation master file, created by + sphinx-quickstart on Sun May 26 13:21:40 2024. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to PyFPGA's documentation! +================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` From 2f67bdeebc6786a8ea6fe39555cbd0c1ebf721f2 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 26 May 2024 13:48:13 -0300 Subject: [PATCH 022/248] ci: updated docs --- .github/workflows/docs.yml | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index a89f8848..a794a243 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -3,17 +3,20 @@ name: 'docs' on: push: branches: - - main +# - main -.jobs: - linux: +jobs: + build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: buildthedocs/btd@v0 + - name: Checkout repository + uses: actions/checkout@v4 + - name: Install dependencies + run: pip install sphinx + - name: Build Sphinx documentation + run: cd docs; make html + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v4 with: - token: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/upload-artifact@master - with: - name: doc - path: doc/_build/html + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: docs/_build/html From 35534ebb012007b1f6456e8540c819bec60e8cfe Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 26 May 2024 15:40:46 -0300 Subject: [PATCH 023/248] Moved content from doc/images into docs/images and docs/_static --- {doc/images => docs/_static}/logo.png | Bin {doc/images => docs/_static}/schema.png | Bin {doc => docs}/images/images.fodg | 0 {doc => docs}/images/logo.fodg | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename {doc/images => docs/_static}/logo.png (100%) rename {doc/images => docs/_static}/schema.png (100%) rename {doc => docs}/images/images.fodg (100%) rename {doc => docs}/images/logo.fodg (100%) diff --git a/doc/images/logo.png b/docs/_static/logo.png similarity index 100% rename from doc/images/logo.png rename to docs/_static/logo.png diff --git a/doc/images/schema.png b/docs/_static/schema.png similarity index 100% rename from doc/images/schema.png rename to docs/_static/schema.png 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 From fe579279e7b277473732fbbd1303fbecefeceaf3 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 26 May 2024 15:41:16 -0300 Subject: [PATCH 024/248] Moved doc contento to docs/wip --- doc/api.rst | 8 -------- {doc => docs/wip}/Makefile | 0 {doc => docs/wip}/advanced.rst | 0 {doc => docs/wip}/basic.rst | 0 {doc => docs/wip}/conf.py | 0 {doc => docs/wip}/dev.rst | 0 {doc => docs/wip}/index.rst | 0 {doc => docs/wip}/intro.rst | 2 +- {doc => docs/wip}/requirements.txt | 0 {doc => docs/wip}/tools.rst | 0 10 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 doc/api.rst rename {doc => docs/wip}/Makefile (100%) rename {doc => docs/wip}/advanced.rst (100%) rename {doc => docs/wip}/basic.rst (100%) rename {doc => docs/wip}/conf.py (100%) rename {doc => docs/wip}/dev.rst (100%) rename {doc => docs/wip}/index.rst (100%) rename {doc => docs/wip}/intro.rst (98%) rename {doc => docs/wip}/requirements.txt (100%) rename {doc => docs/wip}/tools.rst (100%) 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/Makefile b/docs/wip/Makefile similarity index 100% rename from doc/Makefile rename to docs/wip/Makefile diff --git a/doc/advanced.rst b/docs/wip/advanced.rst similarity index 100% rename from doc/advanced.rst rename to docs/wip/advanced.rst diff --git a/doc/basic.rst b/docs/wip/basic.rst similarity index 100% rename from doc/basic.rst rename to docs/wip/basic.rst diff --git a/doc/conf.py b/docs/wip/conf.py similarity index 100% rename from doc/conf.py rename to docs/wip/conf.py diff --git a/doc/dev.rst b/docs/wip/dev.rst similarity index 100% rename from doc/dev.rst rename to docs/wip/dev.rst diff --git a/doc/index.rst b/docs/wip/index.rst similarity index 100% rename from doc/index.rst rename to docs/wip/index.rst diff --git a/doc/intro.rst b/docs/wip/intro.rst similarity index 98% rename from doc/intro.rst rename to docs/wip/intro.rst index 67c8e087..b10d326a 100644 --- a/doc/intro.rst +++ b/docs/wip/intro.rst @@ -84,4 +84,4 @@ Detailed support Next Steps ---------- -You can read the :ref:`basic` and :ref:`advanced` sections, check the detailed :ref:`api` or start with the available :repo:`Examples <examples>`. +You can read the :ref:`basic` and :ref:`advanced` sections, check the detailed :ref:`api` or start with the available :repositoy:`Examples <examples>`. diff --git a/doc/requirements.txt b/docs/wip/requirements.txt similarity index 100% rename from doc/requirements.txt rename to docs/wip/requirements.txt diff --git a/doc/tools.rst b/docs/wip/tools.rst similarity index 100% rename from doc/tools.rst rename to docs/wip/tools.rst From 0717dc143b9d722da00512d49c69220fb7685ec1 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 26 May 2024 15:43:08 -0300 Subject: [PATCH 025/248] docs: added intro (empty) and api (automodule) --- docs/api.rst | 6 ++++++ docs/conf.py | 45 +++++++++++++++++---------------------------- docs/index.rst | 26 ++++++++++---------------- docs/intro.rst | 4 ++++ 4 files changed, 37 insertions(+), 44 deletions(-) create mode 100644 docs/api.rst create mode 100644 docs/intro.rst diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 00000000..b3e29cd3 --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,6 @@ +.. program:: pyfpga + +API Reference +============= + +.. automodule:: pyfpga.project diff --git a/docs/conf.py b/docs/conf.py index cddcb632..10511f00 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,19 +1,4 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) - +# -*- coding: utf-8 -*- # -- Project information ----------------------------------------------------- @@ -21,32 +6,36 @@ copyright = '2024, Rodrigo Alejandro Melo' author = 'Rodrigo Alejandro Melo' - # -- General configuration --------------------------------------------------- -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.extlinks', + 'sphinx.ext.intersphinx', + 'sphinx.ext.todo', + 'sphinx.ext.viewcode', ] -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +autodoc_default_options = { + "members": True, + 'undoc-members': True, + 'inherited-members': True, +} -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +extlinks = { + 'repositoy': ('https://github.com/PyFPGA/pyfpga/tree/main/%s', None) +} +exclude_patterns = ['_build', 'wip'] # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'alabaster' +html_theme = 'sphinx_rtd_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'] \ No newline at end of file +html_static_path = ['_static'] diff --git a/docs/index.rst b/docs/index.rst index d2be65e2..2c9d734c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,20 +1,14 @@ -.. PyFPGA documentation master file, created by - sphinx-quickstart on Sun May 26 13:21:40 2024. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. +.. program:: pyfpga -Welcome to PyFPGA's documentation! -================================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: +PyFPGA's documentation +====================== +.. image:: _static/logo.png + :width: 200 px + :align: center + :target: https://github.com/PyFPGA/pyfpga +.. toctree:: -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` + intro + api diff --git a/docs/intro.rst b/docs/intro.rst new file mode 100644 index 00000000..63b4e29e --- /dev/null +++ b/docs/intro.rst @@ -0,0 +1,4 @@ +.. program:: pyfpga + +Introduction +============ From 33bfdcc0a3c949d377b4ca2063810d89fcb8ab78 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 26 May 2024 15:43:34 -0300 Subject: [PATCH 026/248] ci: updated/enabled docs --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index a794a243..c1dabef5 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -12,7 +12,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Install dependencies - run: pip install sphinx + run: pip install sphinx sphinx-rtd-theme - name: Build Sphinx documentation run: cd docs; make html - name: Deploy to GitHub Pages From ece35b05f221a95f68d922be5bd284cdba27e1da Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 26 May 2024 15:43:54 -0300 Subject: [PATCH 027/248] Removed unused config files --- .btd.yml | 5 ----- .pylintrc | 16 ---------------- 2 files changed, 21 deletions(-) delete mode 100644 .btd.yml delete mode 100644 .pylintrc 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/.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 From c2957c0740a4f41857be713e88c22a3d76777943 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 26 May 2024 15:47:57 -0300 Subject: [PATCH 028/248] ci: updated docs and lint to be similar --- .github/workflows/docs.yml | 4 ++-- .github/workflows/lint.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index c1dabef5..0db33e9b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -6,14 +6,14 @@ on: # - main jobs: - build: + docs: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install dependencies run: pip install sphinx sphinx-rtd-theme - - name: Build Sphinx documentation + - name: Build documentation run: cd docs; make html - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v4 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 5f912102..3907a5c4 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,7 +7,7 @@ jobs: lint: runs-on: ubuntu-latest steps: - - name: Checkout code + - name: Checkout repository uses: actions/checkout@v4 - name: Install dependencies run: pip install pycodestyle pylint From 3d29a99c2f854764074716a54783449da606b673 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 26 May 2024 15:55:30 -0300 Subject: [PATCH 029/248] docs: fixed to find pyfpga.project --- docs/conf.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 10511f00..3e57d268 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,10 @@ # -*- coding: utf-8 -*- +import sys, re +from pathlib import Path + +sys.path.insert(0, str(Path.cwd().resolve().parent)) + # -- Project information ----------------------------------------------------- project = 'PyFPGA' @@ -30,12 +35,5 @@ # -- Options for HTML output ------------------------------------------------- -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# html_theme = 'sphinx_rtd_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'] From fa24be14db12618a4632b68125d29f7493d7d3a9 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 26 May 2024 19:25:14 -0300 Subject: [PATCH 030/248] Implemented some simple methods of project.py --- Makefile | 4 +++- pyfpga/project.py | 22 ++++++++++++++-------- test/test_data.py | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 test/test_data.py diff --git a/Makefile b/Makefile index f8f3ca10..ca7165f0 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,7 @@ #!/usr/bin/make +.PHONY: test + lint: pycodestyle pyfpga examples test pylint -s n pyfpga @@ -13,4 +15,4 @@ clean: rm -fr build .pytest_cache submodule: - git submodule update --init + git submodule update --init diff --git a/pyfpga/project.py b/pyfpga/project.py index 25da8911..778df51f 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -13,8 +13,6 @@ from enum import Enum from pathlib import Path -TASKS = ['prj', 'elb', 'syn', 'par', 'bit'] - class Tool(Enum): """Enumeration of supported FPGA tools.""" @@ -67,6 +65,8 @@ def __init__(self, tool, name=None, data=None, odir='results'): """Class constructor.""" if not isinstance(tool, Tool): raise TypeError('tool must be a Tool enum.') + if data and not isinstance(data, dict): + raise TypeError('data must be a dict.') self.tool = tool self.name = name or tool.value self.data = data or {} @@ -75,7 +75,7 @@ def __init__(self, tool, name=None, data=None, odir='results'): def set_part(self, name): """Temp placeholder""" - raise NotImplementedError('Method is not implemented yet.') + self.data['part'] = name def add_file(self, pathname, filetype=None, library=None, options=None): """Temp placeholder""" @@ -98,23 +98,29 @@ def add_vhdl(self, pathname, library=None, options=None): def add_include(self, path): """Temp placeholder""" - raise NotImplementedError('Method is not implemented yet.') + if 'includes' not in self.data: + self.data['includes'] = [] + self.data['includes'].append(path) def add_param(self, name, value): """Temp placeholder""" - raise NotImplementedError('Method is not implemented yet.') + if 'params' not in self.data: + self.data['params'] = {} + self.data['params'][name] = value def add_define(self, name, value): """Temp placeholder""" - raise NotImplementedError('Method is not implemented yet.') + if 'defines' not in self.data: + self.data['defines'] = {} + self.data['defines'][name] = value def set_arch(self, name): """Temp placeholder""" - raise NotImplementedError('Method is not implemented yet.') + self.data['arch'] = name def set_top(self, name): """Temp placeholder""" - raise NotImplementedError('Method is not implemented yet.') + self.data['top'] = name def add_hook(self, hook, content): """Temp placeholder""" diff --git a/test/test_data.py b/test/test_data.py new file mode 100644 index 00000000..b1d03546 --- /dev/null +++ b/test/test_data.py @@ -0,0 +1,38 @@ +import os +import pytest + +from pyfpga.project import Project, Tool + +pattern = { + 'part': 'PARTNAME', + 'top': 'TOPNAME', + 'arch': 'ARCHNAME', + 'includes': ['INC1', 'INC2', 'INC3'], + 'params': { + 'PARAM1': 'VALUE1', + 'PARAM2': 'VALUE2', + 'PARAM3': 'VALUE3' + }, + 'defines': { + 'DEF1': 'VALUE1', + 'DEF2': 'VALUE2', + 'DEF3': 'VALUE3' + }, +} + + +def test_names(): + prj = Project(Tool.VIVADO) + prj.set_part('PARTNAME') + prj.set_top('TOPNAME') + prj.set_arch('ARCHNAME') + prj.add_include('INC1') + prj.add_include('INC2') + prj.add_include('INC3') + prj.add_param('PARAM1', 'VALUE1') + prj.add_param('PARAM2', 'VALUE2') + prj.add_param('PARAM3', 'VALUE3') + prj.add_define('DEF1', 'VALUE1') + prj.add_define('DEF2', 'VALUE2') + prj.add_define('DEF3', 'VALUE3') + assert prj.data == pattern, 'ERROR: unexpected data' From c545884c04d2e717e7ca1dd27d60d8035432dbdb Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 26 May 2024 19:25:33 -0300 Subject: [PATCH 031/248] ci: simplified/enabled test --- .github/workflows/test.yml | 26 ++++++-------------------- Makefile | 2 +- {fpga => pyfpga}/__init__.py | 2 -- 3 files changed, 7 insertions(+), 23 deletions(-) rename {fpga => pyfpga}/__init__.py (54%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 25761f18..7581de65 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,40 +2,26 @@ name: 'test' on: push: - pull_request: - schedule: # Run once a week to ensure tests pass with updated dependencies - - cron: '0 0 * * 6' -.jobs: +jobs: test: strategy: matrix: os: ['ubuntu'] - pyver: ['3.6', '3.7', '3.8', '3.9', '3.10'] + pyver: [3.8, 3.9, 3.10, 3.11, 3.12] runs-on: ${{ matrix.os }}-latest name: ${{ matrix.os }} | ${{ matrix.pyver }} steps: - - uses: actions/checkout@v2 + - name: Checkout repository + uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 - - name: Pull container images - run: | - docker pull hdlc/ghdl:yosys - docker pull hdlc/nextpnr:ice40 - docker pull hdlc/nextpnr:ecp5 - docker pull hdlc/icestorm - docker pull hdlc/prjtrellis - name: Set up Python ${{ matrix.pyver }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.pyver }} - name: Install dependencies - run: | - pip install pytest - pip install . + run: pip install pytest - name: Run test run: pytest - - name: Run examples - run: | - cd examples; make MOCKS=1 diff --git a/Makefile b/Makefile index ca7165f0..0302d56e 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ lint: git diff --check --cached test: - pytest test + pytest clean: py3clean . diff --git a/fpga/__init__.py b/pyfpga/__init__.py similarity index 54% rename from fpga/__init__.py rename to pyfpga/__init__.py index bd3bc779..b9bae46e 100644 --- a/fpga/__init__.py +++ b/pyfpga/__init__.py @@ -1,5 +1,3 @@ """PyFPGA""" __version__ = '0.3.0-dev' - -from fpga.project import Project From d886fb30c58df9623847c734fcedc52a83a9bb94 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 27 May 2024 22:33:55 -0300 Subject: [PATCH 032/248] Added the target docs at Makefile --- .github/workflows/docs.yml | 2 +- Makefile | 6 +++++- docs/api.rst | 2 -- docs/index.rst | 2 -- docs/intro.rst | 2 -- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 0db33e9b..26c51962 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -14,7 +14,7 @@ jobs: - name: Install dependencies run: pip install sphinx sphinx-rtd-theme - name: Build documentation - run: cd docs; make html + run: make docs - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v4 with: diff --git a/Makefile b/Makefile index 0302d56e..afcec92e 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,9 @@ #!/usr/bin/make -.PHONY: test +.PHONY: docs test + +docs: + cd docs; make html lint: pycodestyle pyfpga examples test @@ -12,6 +15,7 @@ test: clean: py3clean . + cd docs; make clean rm -fr build .pytest_cache submodule: diff --git a/docs/api.rst b/docs/api.rst index b3e29cd3..ef62c294 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,5 +1,3 @@ -.. program:: pyfpga - API Reference ============= diff --git a/docs/index.rst b/docs/index.rst index 2c9d734c..dd21b211 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,3 @@ -.. program:: pyfpga - PyFPGA's documentation ====================== diff --git a/docs/intro.rst b/docs/intro.rst index 63b4e29e..c516b331 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -1,4 +1,2 @@ -.. program:: pyfpga - Introduction ============ From 5d798be15dff72c813eec46cd54a946d442d5555 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 27 May 2024 22:37:50 -0300 Subject: [PATCH 033/248] Renamed test to tests --- .github/workflows/test.yml | 4 ++-- Makefile | 4 ++-- {test => tests}/mocks/impact | 0 {test => tests}/mocks/libero | 0 {test => tests}/mocks/quartus_pgm | 0 {test => tests}/mocks/quartus_sh | 0 {test => tests}/mocks/vivado | 0 {test => tests}/mocks/xtclsh | 0 {test => tests}/test_data.py | 0 {test => tests}/test_files.py | 0 {test => tests}/test_part.py | 0 {test => tests}/test_top.py | 0 12 files changed, 4 insertions(+), 4 deletions(-) rename {test => tests}/mocks/impact (100%) rename {test => tests}/mocks/libero (100%) rename {test => tests}/mocks/quartus_pgm (100%) rename {test => tests}/mocks/quartus_sh (100%) rename {test => tests}/mocks/vivado (100%) rename {test => tests}/mocks/xtclsh (100%) rename {test => tests}/test_data.py (100%) rename {test => tests}/test_files.py (100%) rename {test => tests}/test_part.py (100%) rename {test => tests}/test_top.py (100%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7581de65..6cd55c21 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,5 +23,5 @@ jobs: python-version: ${{ matrix.pyver }} - name: Install dependencies run: pip install pytest - - name: Run test - run: pytest + - name: Run tests + run: make test diff --git a/Makefile b/Makefile index afcec92e..3e3d9555 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,12 @@ #!/usr/bin/make -.PHONY: docs test +.PHONY: docs docs: cd docs; make html lint: - pycodestyle pyfpga examples test + pycodestyle pyfpga examples tests pylint -s n pyfpga git diff --check --cached diff --git a/test/mocks/impact b/tests/mocks/impact similarity index 100% rename from test/mocks/impact rename to tests/mocks/impact diff --git a/test/mocks/libero b/tests/mocks/libero similarity index 100% rename from test/mocks/libero rename to tests/mocks/libero diff --git a/test/mocks/quartus_pgm b/tests/mocks/quartus_pgm similarity index 100% rename from test/mocks/quartus_pgm rename to tests/mocks/quartus_pgm diff --git a/test/mocks/quartus_sh b/tests/mocks/quartus_sh similarity index 100% rename from test/mocks/quartus_sh rename to tests/mocks/quartus_sh diff --git a/test/mocks/vivado b/tests/mocks/vivado similarity index 100% rename from test/mocks/vivado rename to tests/mocks/vivado diff --git a/test/mocks/xtclsh b/tests/mocks/xtclsh similarity index 100% rename from test/mocks/xtclsh rename to tests/mocks/xtclsh diff --git a/test/test_data.py b/tests/test_data.py similarity index 100% rename from test/test_data.py rename to tests/test_data.py diff --git a/test/test_files.py b/tests/test_files.py similarity index 100% rename from test/test_files.py rename to tests/test_files.py diff --git a/test/test_part.py b/tests/test_part.py similarity index 100% rename from test/test_part.py rename to tests/test_part.py diff --git a/test/test_top.py b/tests/test_top.py similarity index 100% rename from test/test_top.py rename to tests/test_top.py From 833cb4ae3a836d22290b70c7b87498320a66771e Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 27 May 2024 23:03:58 -0300 Subject: [PATCH 034/248] Removed tool and data from Project --- pyfpga/project.py | 41 ++++++----------------------------------- tests/test_data.py | 4 ++-- 2 files changed, 8 insertions(+), 37 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index 778df51f..a963bea7 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -4,29 +4,14 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -"""pyfpga.project -This module implements the entry-point of PyFPGA, which provides -functionalities to create a project, generate a bitstream and -program a device. +""" +Base class that implements agnostic methods to deal with FPGA projects. """ from enum import Enum from pathlib import Path -class Tool(Enum): - """Enumeration of supported FPGA tools.""" - GHDL = 'ghdl' - ISE = 'ise' - LIBERO = 'libero' - OPENFLOW = 'openflow' - QUARTUS = 'quartus' - VIVADO = 'vivado' - YOSYS = 'yosys' - YOSYS_ISE = 'yosys-ise' - YOSYS_VIVADO = 'yosys-vivado' - - class Step(Enum): """Enumeration of supported Steps""" PRJ = 'prj' @@ -47,29 +32,19 @@ class Hook(Enum): class Project: - """Class to manage an FPGA project. + """Base class to manage an FPGA project. - :param tool: tool name - :type tool: Tool :param name: project name (tool name by default) :type name: str, optional - :param data: pre-populated data for the project - :type data: dict, optional :param odir: output directory :type odir: str, optional - :raises TypeError: when a value is not a valid enum :raises NotImplementedError: when a method is not implemented yet """ - def __init__(self, tool, name=None, data=None, odir='results'): + def __init__(self, name=None, odir='results'): """Class constructor.""" - if not isinstance(tool, Tool): - raise TypeError('tool must be a Tool enum.') - if data and not isinstance(data, dict): - raise TypeError('data must be a dict.') - self.tool = tool - self.name = name or tool.value - self.data = data or {} + self.data = {} + self.name = name self.odir = Path(odir) self.odir.mkdir(parents=True, exist_ok=True) @@ -124,14 +99,10 @@ def set_top(self, name): def add_hook(self, hook, content): """Temp placeholder""" - # if not isinstance(hook, Hook): - # raise TypeError('hook must be a Hook enum.') raise NotImplementedError('Method is not implemented yet.') def make(self, end=Step.BIT, start=Step.PRJ, capture=False): """Temp placeholder""" - # if not isinstance(end, Step) or not isinstance(start, Step): - # raise TypeError('start and end must be a Step enum.') raise NotImplementedError('Method is not implemented yet.') def prog(self, position=1, bitstream=None): diff --git a/tests/test_data.py b/tests/test_data.py index b1d03546..e603af21 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -1,7 +1,7 @@ import os import pytest -from pyfpga.project import Project, Tool +from pyfpga.project import Project pattern = { 'part': 'PARTNAME', @@ -22,7 +22,7 @@ def test_names(): - prj = Project(Tool.VIVADO) + prj = Project() prj.set_part('PARTNAME') prj.set_top('TOPNAME') prj.set_arch('ARCHNAME') From 292e6ea4560111eb90082818fc1eddcf62f80976 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 27 May 2024 23:12:19 -0300 Subject: [PATCH 035/248] ci: attempt to fix test --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6cd55c21..061381db 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: os: ['ubuntu'] - pyver: [3.8, 3.9, 3.10, 3.11, 3.12] + pyver: ['3.8', '3.9', '3.10', '3.11', '3.12'] runs-on: ${{ matrix.os }}-latest name: ${{ matrix.os }} | ${{ matrix.pyver }} steps: From 13b5169914db1956096d45b5ba929f7cff33fd7e Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 27 May 2024 23:38:30 -0300 Subject: [PATCH 036/248] Added add_cons for constraint files --- pyfpga/project.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index a963bea7..231fcb2c 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -56,9 +56,9 @@ def add_file(self, pathname, filetype=None, library=None, options=None): """Temp placeholder""" raise NotImplementedError('Method is not implemented yet.') - def add_vlog(self, pathname, options=None): + def add_cons(self, pathname, options=None): """Temp placeholder""" - self.add_file(pathname, filetype='vlog', options=options) + self.add_file(pathname, filetype='cons', options=options) def add_slog(self, pathname, options=None): """Temp placeholder""" @@ -71,6 +71,10 @@ def add_vhdl(self, pathname, library=None, options=None): library=library, options=options ) + def add_vlog(self, pathname, options=None): + """Temp placeholder""" + self.add_file(pathname, filetype='vlog', options=options) + def add_include(self, path): """Temp placeholder""" if 'includes' not in self.data: From f6958f6f90ef478e7e66814eab6572dfa4377dad Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Tue, 28 May 2024 21:40:51 -0300 Subject: [PATCH 037/248] Added logging into the new Project class and an example --- examples/misc/logging.py | 25 +++++++++++++++++++++++++ pyfpga/project.py | 16 ++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 examples/misc/logging.py diff --git a/examples/misc/logging.py b/examples/misc/logging.py new file mode 100644 index 00000000..55a214c7 --- /dev/null +++ b/examples/misc/logging.py @@ -0,0 +1,25 @@ +""" +This script demonstrates how to utilize the logging functionality within the +pyfpga package. The following steps are covered: + +1. Creating an instance of the Project class. +2. Testing logging with the default INFO level. +3. Setting the logging level to DEBUG to capture more detailed information. +4. Disabling logging by removing all handlers. + +Usage: +- By default, the logger captures messages with level INFO and higher. +- To see more detailed debug information, set the logger level to DEBUG. +- To disable logging, remove all handlers from the logger. +""" + +import logging + +from pyfpga.project import Project + +prj = Project() +prj._test_logging() +prj.logger.setLevel(logging.DEBUG) +prj._test_logging() +prj.logger.handlers = [] +prj._test_logging() diff --git a/pyfpga/project.py b/pyfpga/project.py index 231fcb2c..61d772f5 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -8,6 +8,8 @@ Base class that implements agnostic methods to deal with FPGA projects. """ +import logging + from enum import Enum from pathlib import Path @@ -47,6 +49,16 @@ def __init__(self, name=None, odir='results'): self.name = name self.odir = Path(odir) self.odir.mkdir(parents=True, exist_ok=True) + # logging config + self.logger = logging.getLogger(self.__class__.__name__) + 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): """Temp placeholder""" @@ -112,3 +124,7 @@ def make(self, end=Step.BIT, start=Step.PRJ, capture=False): def prog(self, position=1, bitstream=None): """Temp placeholder""" raise NotImplementedError('Method is not implemented yet.') + + def _test_logging(self): + self.logger.info('It is an INFO message') + self.logger.debug('It is anDEBUG message') From 9bcc17074d295e622a54f0569794a63c5248fb11 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Tue, 28 May 2024 21:48:41 -0300 Subject: [PATCH 038/248] Added a Makefile target to update the resources module --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3e3d9555..351413fc 100644 --- a/Makefile +++ b/Makefile @@ -18,5 +18,8 @@ clean: cd docs; make clean rm -fr build .pytest_cache -submodule: +submodule-init: git submodule update --init + +submodule-update: + cd resources; git checkout main; git pull From 1b2fbe97d43dbe5d883577c43b28ea2c9b57faf9 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Tue, 28 May 2024 23:30:36 -0300 Subject: [PATCH 039/248] Renamed logging.py as logger.py (examples/misc) to avoid circular dependencies --- examples/misc/{logging.py => logger.py} | 6 +++--- pyfpga/project.py | 14 ++++++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) rename examples/misc/{logging.py => logger.py} (90%) diff --git a/examples/misc/logging.py b/examples/misc/logger.py similarity index 90% rename from examples/misc/logging.py rename to examples/misc/logger.py index 55a214c7..7f34ca1c 100644 --- a/examples/misc/logging.py +++ b/examples/misc/logger.py @@ -18,8 +18,8 @@ from pyfpga.project import Project prj = Project() -prj._test_logging() +prj.set_part('EXAMPLE') prj.logger.setLevel(logging.DEBUG) -prj._test_logging() +prj.set_part('EXAMPLE') prj.logger.handlers = [] -prj._test_logging() +prj.set_part('EXAMPLE') diff --git a/pyfpga/project.py b/pyfpga/project.py index 61d772f5..59a9e016 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -62,6 +62,7 @@ def __init__(self, name=None, odir='results'): def set_part(self, name): """Temp placeholder""" + self.logger.debug('Executing set_part') self.data['part'] = name def add_file(self, pathname, filetype=None, library=None, options=None): @@ -70,14 +71,17 @@ def add_file(self, pathname, filetype=None, library=None, options=None): def add_cons(self, pathname, options=None): """Temp placeholder""" + self.logger.debug('Executing add_cons') self.add_file(pathname, filetype='cons', options=options) def add_slog(self, pathname, options=None): """Temp placeholder""" + self.logger.debug('Executing add_slog') self.add_file(pathname, filetype='slog', options=options) def add_vhdl(self, pathname, library=None, options=None): """Temp placeholder""" + self.logger.debug('Executing add_vhdl') self.add_file( pathname, filetype='vhdl', library=library, options=options @@ -85,32 +89,38 @@ def add_vhdl(self, pathname, library=None, options=None): def add_vlog(self, pathname, options=None): """Temp placeholder""" + self.logger.debug('Executing add_vlog') self.add_file(pathname, filetype='vlog', options=options) def add_include(self, path): """Temp placeholder""" + self.logger.debug('Executing add_include') if 'includes' not in self.data: self.data['includes'] = [] self.data['includes'].append(path) def add_param(self, name, value): """Temp placeholder""" + self.logger.debug('Executing add_param') if 'params' not in self.data: self.data['params'] = {} self.data['params'][name] = value def add_define(self, name, value): """Temp placeholder""" + self.logger.debug('Executing add_define') if 'defines' not in self.data: self.data['defines'] = {} self.data['defines'][name] = value def set_arch(self, name): """Temp placeholder""" + self.logger.debug('Executing set_arch') self.data['arch'] = name def set_top(self, name): """Temp placeholder""" + self.logger.debug('Executing set_top') self.data['top'] = name def add_hook(self, hook, content): @@ -124,7 +134,3 @@ def make(self, end=Step.BIT, start=Step.PRJ, capture=False): def prog(self, position=1, bitstream=None): """Temp placeholder""" raise NotImplementedError('Method is not implemented yet.') - - def _test_logging(self): - self.logger.info('It is an INFO message') - self.logger.debug('It is anDEBUG message') From abbb2b577891ab758b636487fddd7a6801151e9e Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 29 May 2024 23:11:27 -0300 Subject: [PATCH 040/248] Added a private method to run the underlaying tool --- pyfpga/project.py | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index 59a9e016..a33cb563 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -9,9 +9,13 @@ """ import logging +import os +import subprocess from enum import Enum +from datetime import datetime from pathlib import Path +from time import time class Step(Enum): @@ -47,8 +51,8 @@ def __init__(self, name=None, odir='results'): """Class constructor.""" self.data = {} self.name = name - self.odir = Path(odir) - self.odir.mkdir(parents=True, exist_ok=True) + self.odir = odir + # self.odir.mkdir(parents=True, exist_ok=True) # logging config self.logger = logging.getLogger(self.__class__.__name__) self.logger.setLevel(logging.INFO) @@ -134,3 +138,37 @@ def make(self, end=Step.BIT, start=Step.PRJ, capture=False): def prog(self, position=1, bitstream=None): """Temp placeholder""" raise NotImplementedError('Method is not implemented yet.') + + def _run(self, command): + self.logger.info('Running the underlying tool (%s)', datetime.now()) + run_error = 0 + old_dir = Path.cwd() + new_dir = Path(self.odir) + start = time.time() + try: + os.chdir(new_dir) + with open('run.log', 'w', encoding='utf-8') as logfile: + subprocess.run( + command, shell=True, check=True, text=True, + stdout=logfile, stderr=subprocess.STDOUT + ) + except subprocess.CalledProcessError: + with open('run.log', 'r', encoding='utf-8') as logfile: + lines = logfile.readlines() + last_lines = lines[-10:] if len(lines) >= 10 else lines + for line in last_lines: + self.logger.error(line.strip()) + run_error = 1 + finally: + os.chdir(old_dir) + end = time.time() + self.logger.info('Done (%s)', datetime.now()) + elapsed = end - start + self.logger.info( + 'Elapsed time %dh %dm %.2fs', + int(elapsed // 3600), + int((elapsed % 3600) // 60), + elapsed % 60 + ) + if run_error: + raise RuntimeError('Error running the underlying tool') From 1b1f5a2abaa27107d2c611fc66b0c392d2ac0b49 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 29 May 2024 23:50:13 -0300 Subject: [PATCH 041/248] Replaced add_hook by a one functions for each hook --- pyfpga/project.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index a33cb563..912a61e8 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -21,22 +21,11 @@ class Step(Enum): """Enumeration of supported Steps""" PRJ = 'prj' - ELB = 'elb' SYN = 'syn' PAR = 'par' BIT = 'bit' -class Hook(Enum): - """Enumeration of supported Hooks""" - PREFILE = 'prefile' - PROJECT = 'project' - PREFLOW = 'preflow' - POSTSYN = 'postsyn' - POSTPAR = 'postpar' - POSTBIT = 'postbit' - - class Project: """Base class to manage an FPGA project. @@ -127,7 +116,27 @@ def set_top(self, name): self.logger.debug('Executing set_top') self.data['top'] = name - def add_hook(self, hook, content): + def add_precfg_hook(self, content): + """Temp placeholder""" + raise NotImplementedError('Method is not implemented yet.') + + def add_postcfg_hook(self, content): + """Temp placeholder""" + raise NotImplementedError('Method is not implemented yet.') + + def add_presyn_hook(self, content): + """Temp placeholder""" + raise NotImplementedError('Method is not implemented yet.') + + def add_prepar_hook(self, content): + """Temp placeholder""" + raise NotImplementedError('Method is not implemented yet.') + + def add_prebit_hook(self, content): + """Temp placeholder""" + raise NotImplementedError('Method is not implemented yet.') + + def add_postbit_hook(self, content): """Temp placeholder""" raise NotImplementedError('Method is not implemented yet.') From 992cfab25705ace6051e0bb3e8e58b7b07378400 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 29 May 2024 23:50:40 -0300 Subject: [PATCH 042/248] docs: added hooks --- docs/hooks.rst | 27 +++++++++++++++++++++++++++ docs/index.rst | 1 + 2 files changed, 28 insertions(+) create mode 100644 docs/hooks.rst diff --git a/docs/hooks.rst b/docs/hooks.rst new file mode 100644 index 00000000..a9328332 --- /dev/null +++ b/docs/hooks.rst @@ -0,0 +1,27 @@ +Hooks +===== + +.. code-block:: + + create project + config project + part + precfg hook + params + defines + includes + files + arch + top + postcfg hook + close project + + open project + presyn hook + synthesis + prepar hook + place_and_route + prebit hook + bitstream + postbit hook + close project diff --git a/docs/index.rst b/docs/index.rst index dd21b211..69643350 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -9,4 +9,5 @@ PyFPGA's documentation .. toctree:: intro + hooks api From b9700cd6226a007337d5be8293d6989eaf0e96cb Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 30 May 2024 00:06:18 -0300 Subject: [PATCH 043/248] Updated the NOTICE about PyFPGA being rewritten --- README.md | 4 +-- docs/index.rst | 5 ++++ docs/wip/Makefile | 57 --------------------------------------- docs/wip/index.rst | 32 ---------------------- docs/wip/requirements.txt | 7 ----- 5 files changed, 7 insertions(+), 98 deletions(-) delete mode 100644 docs/wip/Makefile delete mode 100644 docs/wip/index.rst delete mode 100644 docs/wip/requirements.txt diff --git a/README.md b/README.md index 97fad22f..671fa6a1 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ ![ISE](https://img.shields.io/badge/ISE-14.7-blue.svg?style=flat-square) ![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) -> **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. +> **WARNING:** (2024-05-20) PyFPGA is in the process of being strongly rewritten/simplified. +> Most changes are internal, but the API will also change. PyFPGA is a **Python Package** for **vendor-agnostic** FPGA development. It provides a **Class** which allows the programmatically execution of **synthesis**, diff --git a/docs/index.rst b/docs/index.rst index 69643350..96f4b3d9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,6 +6,11 @@ PyFPGA's documentation :align: center :target: https://github.com/PyFPGA/pyfpga +.. ATTENTION:: + + (2024-05-20) PyFPGA is in the process of being strongly rewritten/simplified. + Most changes are internal, but the API will also change. + .. toctree:: intro diff --git a/docs/wip/Makefile b/docs/wip/Makefile deleted file mode 100644 index 247b174d..00000000 --- a/docs/wip/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/docs/wip/index.rst b/docs/wip/index.rst deleted file mode 100644 index 8975c13b..00000000 --- a/docs/wip/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 - - <p style="text-align: center;"> - <a title="GitLab Repository" href="https://github.com/PyFPGA/pyfpga"><img src="https://img.shields.io/badge/-PyFPGA/pyfpga-323131.svg?longCache=true&style=flat-square&logo=github"></a><!-- - --> - </p> - - <hr> - -.. 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/docs/wip/requirements.txt b/docs/wip/requirements.txt deleted file mode 100644 index 2406283f..00000000 --- a/docs/wip/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 From 5c9f31ed96ca05bb543d747cb400ebb1c3d2fc1a Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 30 May 2024 18:08:33 -0300 Subject: [PATCH 044/248] Moved templates from fpga/tool to pyfpga/templates --- fpga/tool/template.sh => pyfpga/templates/openflow.jinja | 0 fpga/tool/template.tcl => pyfpga/templates/vivado.jinja | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename fpga/tool/template.sh => pyfpga/templates/openflow.jinja (100%) rename fpga/tool/template.tcl => pyfpga/templates/vivado.jinja (100%) diff --git a/fpga/tool/template.sh b/pyfpga/templates/openflow.jinja similarity index 100% rename from fpga/tool/template.sh rename to pyfpga/templates/openflow.jinja diff --git a/fpga/tool/template.tcl b/pyfpga/templates/vivado.jinja similarity index 100% rename from fpga/tool/template.tcl rename to pyfpga/templates/vivado.jinja From 93eeb269a2acb4c0f2961f06ea1b04141365fbb1 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 30 May 2024 18:51:53 -0300 Subject: [PATCH 045/248] Content of vivado.jinja was split into 4 templates --- pyfpga/templates/ise.jinja | 263 ++++++++++++++++++++++++++++ pyfpga/templates/libero.jinja | 294 ++++++++++++++++++++++++++++++++ pyfpga/templates/openflow.jinja | 19 +-- pyfpga/templates/quartus.jinja | 261 ++++++++++++++++++++++++++++ pyfpga/templates/vivado.jinja | 237 +------------------------ 5 files changed, 824 insertions(+), 250 deletions(-) create mode 100644 pyfpga/templates/ise.jinja create mode 100644 pyfpga/templates/libero.jinja create mode 100644 pyfpga/templates/quartus.jinja diff --git a/pyfpga/templates/ise.jinja b/pyfpga/templates/ise.jinja new file mode 100644 index 00000000..1830b97b --- /dev/null +++ b/pyfpga/templates/ise.jinja @@ -0,0 +1,263 @@ +# +# PyFPGA +# Copyright (C) 2015-2024 Rodrigo A. Melo +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +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 par 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# + } + "postpar" { +#POSTPAR_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 + } + } +} + +proc fpga_open { PROJECT } { + global TOOL + fpga_print "opening the project '$PROJECT'" + switch $TOOL { + "ise" { project open $PROJECT.xise } + } +} + +proc fpga_close {} { + global TOOL + fpga_print "closing the project" + switch $TOOL { + "ise" { project close } + } +} + +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 + } + } +} + +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 + } + } + } +} + +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" + } + } +} + +proc fpga_top { TOP } { + global TOOL + fpga_print "specifying the top level '$TOP'" + switch $TOOL { + "ise" { + project set top $TOP + } + } +} + +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" + } + } +} + +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 } + } + } + } +} + +proc fpga_run_par {} { + global TOOL PRESYNTH + fpga_print "running 'place and route'" + 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 } + } + } +} + +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 } + } + } +} + +# +# 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|par|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 "par"] >= 0 } { + fpga_run_par + fpga_commands "postpar" + } + 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/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja new file mode 100644 index 00000000..39919f94 --- /dev/null +++ b/pyfpga/templates/libero.jinja @@ -0,0 +1,294 @@ +# +# PyFPGA +# Copyright (C) 2015-2024 Rodrigo A. Melo +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +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 par 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# + } + "postpar" { +#POSTPAR_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 { + "libero" { + if { [ file exists $PROJECT ] } { file delete -force -- $PROJECT } + new_project -name $PROJECT -location $PROJECT -hdl {VHDL} -family {SmartFusion2} + } + } +} + +proc fpga_open { PROJECT } { + global TOOL + fpga_print "opening the project '$PROJECT'" + switch $TOOL { + "libero" { + open_project $PROJECT/$PROJECT.prjx + } + } +} + +proc fpga_close {} { + global TOOL + fpga_print "closing the project" + switch $TOOL { + "libero" { close_project } + } +} + +proc fpga_part { PART } { + global TOOL FAMILY DEVICE PACKAGE SPEED + fpga_print "adding the part '$PART'" + switch $TOOL { + "libero" { + set_device -family $FAMILY -die $DEVICE -package $PACKAGE -speed $SPEED + } + } +} + +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 { + "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 + } + } + } +} + +proc fpga_include {PATH} { + global TOOL INCLUDED + lappend INCLUDED $PATH + fpga_print "setting '$PATH' as a search location" + switch $TOOL { + "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 + } + } +} + +proc fpga_top { TOP } { + global TOOL + fpga_print "specifying the top level '$TOP'" + switch $TOOL { + "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 <ROOT>/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 + } + } + } +} + +proc fpga_params {} { + global TOOL PARAMS + if { [llength $PARAMS] == 0 } { return } + fpga_print "setting generics/parameters" + switch $TOOL { + "libero" { + # They must be specified after set_root (see fpga_top) + } + } +} + +proc fpga_run_syn {} { + global TOOL PRESYNTH + fpga_print "running 'synthesis'" + switch $TOOL { + "libero" { + run_tool -name {SYNTHESIZE} + } + } +} + +proc fpga_run_par {} { + global TOOL PRESYNTH + fpga_print "running 'place and route'" + switch $TOOL { + "libero" { + run_tool -name {PLACEROUTE} + run_tool -name {VERIFYTIMING} + } + } +} + +proc fpga_run_bit {} { + global TOOL PROJECT TOP + fpga_print "running 'bitstream generation'" + switch $TOOL { + "libero" { + run_tool -name {GENERATEPROGRAMMINGFILE} + } + } +} + +# +# 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|par|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 "par"] >= 0 } { + fpga_run_par + fpga_commands "postpar" + } + 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/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja index f2b271eb..f50b0786 100644 --- a/pyfpga/templates/openflow.jinja +++ b/pyfpga/templates/openflow.jinja @@ -1,22 +1,9 @@ #!/bin/bash # -# Copyright (C) 2020 Rodrigo A. Melo +# PyFPGA +# Copyright (C) 2020-2024 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 <http://www.gnu.org/licenses/>. -# -# This file implements an open-source flow based on ghdl, ghdl-yosys-plugin, -# yosys, nextpnr, icestorm and prjtrellis. +# SPDX-License-Identifier: GPL-3.0-or-later # set -e diff --git a/pyfpga/templates/quartus.jinja b/pyfpga/templates/quartus.jinja new file mode 100644 index 00000000..8e54e4ea --- /dev/null +++ b/pyfpga/templates/quartus.jinja @@ -0,0 +1,261 @@ +# +# PyFPGA +# Copyright (C) 2015-2024 Rodrigo A. Melo +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +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 par 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# + } + "postpar" { +#POSTPAR_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 { + "quartus" { + package require ::quartus::project + project_new $PROJECT -overwrite + set_global_assignment -name NUM_PARALLEL_PROCESSORS ALL + } + } +} + +proc fpga_open { PROJECT } { + global TOOL + fpga_print "opening the project '$PROJECT'" + switch $TOOL { + "quartus" { + package require ::quartus::flow + project_open -force $PROJECT.qpf + } + } +} + +proc fpga_close {} { + global TOOL + fpga_print "closing the project" + switch $TOOL { + "quartus" { project_close } + } +} + +proc fpga_part { PART } { + global TOOL FAMILY DEVICE PACKAGE SPEED + fpga_print "adding the part '$PART'" + switch $TOOL { + "quartus" { + set_global_assignment -name DEVICE $PART + } + } +} + +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 { + "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 + } + } + } +} + +proc fpga_include {PATH} { + global TOOL INCLUDED + lappend INCLUDED $PATH + fpga_print "setting '$PATH' as a search location" + switch $TOOL { + "quartus" { + # Verilog Included Files are NOT added + foreach INCLUDE $INCLUDED { + set_global_assignment -name SEARCH_PATH $INCLUDE + } + } + } +} + +proc fpga_top { TOP } { + global TOOL + fpga_print "specifying the top level '$TOP'" + switch $TOOL { + "quartus" { + set_global_assignment -name TOP_LEVEL_ENTITY $TOP + } + } +} + +proc fpga_params {} { + global TOOL PARAMS + if { [llength $PARAMS] == 0 } { return } + fpga_print "setting generics/parameters" + switch $TOOL { + "quartus" { + foreach PARAM $PARAMS { + eval "set_parameter -name $PARAM" + } + } + } +} + +proc fpga_run_syn {} { + global TOOL PRESYNTH + fpga_print "running 'synthesis'" + switch $TOOL { + "quartus" { + execute_module -tool map + } + } +} + +proc fpga_run_par {} { + global TOOL PRESYNTH + fpga_print "running 'place and route'" + switch $TOOL { + "quartus" { + execute_module -tool fit + execute_module -tool sta + } + } +} + +proc fpga_run_bit {} { + global TOOL PROJECT TOP + fpga_print "running 'bitstream generation'" + switch $TOOL { + "quartus" { + execute_module -tool asm + } + } +} + +# +# 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|par|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 "par"] >= 0 } { + fpga_run_par + fpga_commands "postpar" + } + 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/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index 7466d534..5758f1c7 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -1,35 +1,8 @@ # -# PyFPGA Master Tcl +# PyFPGA +# Copyright (C) 2015-2024 Rodrigo A. Melo # -# 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 <http://www.gnu.org/licenses/>. -# -# Description: Tcl script to create a new project and performs synthesis, -# place and route, 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 +# SPDX-License-Identifier: GPL-3.0-or-later # set TOOL #TOOL# @@ -87,19 +60,6 @@ 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 } } } @@ -108,14 +68,6 @@ 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 } } } @@ -124,9 +76,6 @@ proc fpga_close {} { global TOOL fpga_print "closing the project" switch $TOOL { - "ise" { project close } - "libero" { close_project } - "quartus" { project_close } "vivado" { close_project } } } @@ -135,18 +84,6 @@ 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] } @@ -164,49 +101,6 @@ proc fpga_file {FILE {LIBRARY "work"}} { 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 @@ -223,25 +117,6 @@ proc fpga_include {PATH} { 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] @@ -264,7 +139,6 @@ proc fpga_design {FILE} { set TOP design_1_wrapper } } - default { puts "UNSUPPORTED by '$TOOL'" } } } @@ -272,53 +146,6 @@ 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 <ROOT>/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] } @@ -330,19 +157,6 @@ proc fpga_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 "="] } @@ -356,21 +170,6 @@ 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] @@ -380,7 +179,6 @@ proc fpga_run_syn {} { wait_on_run synth_1 } } - default { puts "UNSUPPORTED by '$TOOL'" } } } @@ -388,22 +186,6 @@ proc fpga_run_par {} { global TOOL PRESYNTH fpga_print "running 'place and route'" 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 @@ -411,7 +193,6 @@ proc fpga_run_par {} { launch_runs impl_1 wait_on_run impl_1 } - default { puts "UNSUPPORTED by '$TOOL'" } } } @@ -419,22 +200,10 @@ 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'" } } } From 5c2ad6ffe1084cdbae147569ed04d7e66fecbf59 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 30 May 2024 19:28:49 -0300 Subject: [PATCH 046/248] Templates clean-up --- pyfpga/templates/ise.jinja | 248 +++++++-------------------- pyfpga/templates/libero.jinja | 305 ++++++++++----------------------- pyfpga/templates/quartus.jinja | 240 ++++++-------------------- pyfpga/templates/vivado.jinja | 251 +++++++-------------------- 4 files changed, 258 insertions(+), 786 deletions(-) diff --git a/pyfpga/templates/ise.jinja b/pyfpga/templates/ise.jinja index 1830b97b..2aa7a6b1 100644 --- a/pyfpga/templates/ise.jinja +++ b/pyfpga/templates/ise.jinja @@ -5,7 +5,6 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -set TOOL #TOOL# set PRESYNTH #PRESYNTH# set PROJECT #PROJECT# set PART #PART# @@ -23,241 +22,112 @@ 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# - } - "postpar" { -#POSTPAR_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 - } - } + if { [ file exists $PROJECT.xise ] } { file delete $PROJECT.xise } + project new $PROJECT.xise } proc fpga_open { PROJECT } { - global TOOL - fpga_print "opening the project '$PROJECT'" - switch $TOOL { - "ise" { project open $PROJECT.xise } - } + project open $PROJECT.xise } proc fpga_close {} { - global TOOL - fpga_print "closing the project" - switch $TOOL { - "ise" { project close } - } + project close } 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 - } - } + project set family $FAMILY + project set device $DEVICE + project set package $PACKAGE + project set speed $SPEED } 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 - } - } + 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 } } 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" - } - } + # Verilog Included Files are NOT added + project set "Verilog Include Directories" \ + [join $INCLUDED "|"] -process "Synthesize - XST" } proc fpga_top { TOP } { - global TOOL - fpga_print "specifying the top level '$TOP'" - switch $TOOL { - "ise" { - project set top $TOP - } - } + project set top $TOP } 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" - } - } + set assigns [list] + foreach PARAM $PARAMS { lappend assigns [join $PARAM "="] } + project set "Generics, Parameters" "[join $assigns]" -process "Synthesize - XST" } 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 } - } - } + if { $PRESYNTH == "True" } { + project set top_level_module_type "EDIF" + } else { + project clean + process run "Synthesize" + if { [process get "Synthesize" status] == "errors" } { exit 2 } } } proc fpga_run_par {} { - global TOOL PRESYNTH - fpga_print "running 'place and route'" - 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 } - } - } + 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 } } 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 } - } - } + process run "Generate Programming File" + if { [process get "Generate Programming File" status] == "errors" } { exit 2 } + catch { file rename -force $TOP.bit $PROJECT.bit } } -# -# 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 - } + fpga_create $PROJECT + fpga_part $PART + {{ PRECFG }} + fpga_files + fpga_top $TOP + fpga_params + {{ POSTCFG }} + fpga_close } -# -# Design Flow -# - if { [lsearch -regexp $TASKS "syn|par|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 "par"] >= 0 } { - fpga_run_par - fpga_commands "postpar" - } - 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 + fpga_open $PROJECT + if { [lsearch -exact $TASKS "syn"] >= 0 } { + {{ PRESYN }} + fpga_run_syn + } + if { [lsearch -exact $TASKS "par"] >= 0 } { + {{ PREPAR }} + fpga_run_par + } + if { [lsearch -exact $TASKS "bit"] >= 0 } { + {{ PREBIT }} + fpga_run_bit + {{ POSTBIT }} } + fpga_close } - -# -# End of the script -# - -fpga_print "end of the Tcl script" diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index 39919f94..7cdcfd33 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -5,7 +5,6 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -set TOOL #TOOL# set PRESYNTH #PRESYNTH# set PROJECT #PROJECT# set PART #PART# @@ -23,272 +22,140 @@ 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# - } - "postpar" { -#POSTPAR_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 { - "libero" { - if { [ file exists $PROJECT ] } { file delete -force -- $PROJECT } - new_project -name $PROJECT -location $PROJECT -hdl {VHDL} -family {SmartFusion2} - } - } + if { [ file exists $PROJECT ] } { file delete -force -- $PROJECT } + new_project -name $PROJECT -location $PROJECT -hdl {VHDL} -family {SmartFusion2} } proc fpga_open { PROJECT } { - global TOOL - fpga_print "opening the project '$PROJECT'" - switch $TOOL { - "libero" { - open_project $PROJECT/$PROJECT.prjx - } - } + open_project $PROJECT/$PROJECT.prjx } proc fpga_close {} { - global TOOL - fpga_print "closing the project" - switch $TOOL { - "libero" { close_project } - } + close_project } proc fpga_part { PART } { - global TOOL FAMILY DEVICE PACKAGE SPEED - fpga_print "adding the part '$PART'" - switch $TOOL { - "libero" { - set_device -family $FAMILY -die $DEVICE -package $PACKAGE -speed $SPEED - } - } + set_device -family $FAMILY -die $DEVICE -package $PACKAGE -speed $SPEED } 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 { - "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 - } - } + 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 } } proc fpga_include {PATH} { - global TOOL INCLUDED lappend INCLUDED $PATH - fpga_print "setting '$PATH' as a search location" - switch $TOOL { - "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 - } + # 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 } proc fpga_top { TOP } { - global TOOL - fpga_print "specifying the top level '$TOP'" - switch $TOOL { - "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 <ROOT>/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 - } - } + set_root $TOP + # Verilog Included files + set cmd "configure_tool -name {SYNTHESIZE} -params {SYNPLIFY_OPTIONS:" + if { [info exists INCLUDED] && [llength $INCLUDED] > 0 } { + # See <ROOT>/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 } } proc fpga_params {} { - global TOOL PARAMS if { [llength $PARAMS] == 0 } { return } - fpga_print "setting generics/parameters" - switch $TOOL { - "libero" { - # They must be specified after set_root (see fpga_top) - } - } + # They must be specified after set_root (see fpga_top) } proc fpga_run_syn {} { - global TOOL PRESYNTH - fpga_print "running 'synthesis'" - switch $TOOL { - "libero" { - run_tool -name {SYNTHESIZE} - } - } + run_tool -name {SYNTHESIZE} } proc fpga_run_par {} { - global TOOL PRESYNTH - fpga_print "running 'place and route'" - switch $TOOL { - "libero" { - run_tool -name {PLACEROUTE} - run_tool -name {VERIFYTIMING} - } - } + run_tool -name {PLACEROUTE} + run_tool -name {VERIFYTIMING} } proc fpga_run_bit {} { - global TOOL PROJECT TOP - fpga_print "running 'bitstream generation'" - switch $TOOL { - "libero" { - run_tool -name {GENERATEPROGRAMMINGFILE} - } - } + run_tool -name {GENERATEPROGRAMMINGFILE} } -# -# 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 - } + fpga_create $PROJECT + fpga_part $PART + {{ PRECFG }} + fpga_files + fpga_top $TOP + fpga_params + {{ POSTCFG }} + fpga_close } -# -# Design Flow -# - if { [lsearch -regexp $TASKS "syn|par|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 "par"] >= 0 } { - fpga_run_par - fpga_commands "postpar" - } - 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 + fpga_open $PROJECT + if { [lsearch -exact $TASKS "syn"] >= 0 } { + {{ PRESYN }} + fpga_run_syn + } + if { [lsearch -exact $TASKS "par"] >= 0 } { + {{ PREPAR }} + fpga_run_par + } + if { [lsearch -exact $TASKS "bit"] >= 0 } { + {{ PREBIT }} + fpga_run_bit + {{ POSTBIT }} } + fpga_close } - -# -# End of the script -# - -fpga_print "end of the Tcl script" diff --git a/pyfpga/templates/quartus.jinja b/pyfpga/templates/quartus.jinja index 8e54e4ea..4a46bce8 100644 --- a/pyfpga/templates/quartus.jinja +++ b/pyfpga/templates/quartus.jinja @@ -5,7 +5,6 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -set TOOL #TOOL# set PRESYNTH #PRESYNTH# set PROJECT #PROJECT# set PART #PART# @@ -23,239 +22,108 @@ 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# - } - "postpar" { -#POSTPAR_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 { - "quartus" { - package require ::quartus::project - project_new $PROJECT -overwrite - set_global_assignment -name NUM_PARALLEL_PROCESSORS ALL - } - } + package require ::quartus::project + project_new $PROJECT -overwrite + set_global_assignment -name NUM_PARALLEL_PROCESSORS ALL } proc fpga_open { PROJECT } { - global TOOL - fpga_print "opening the project '$PROJECT'" - switch $TOOL { - "quartus" { - package require ::quartus::flow - project_open -force $PROJECT.qpf - } - } + package require ::quartus::flow + project_open -force $PROJECT.qpf } proc fpga_close {} { - global TOOL - fpga_print "closing the project" - switch $TOOL { - "quartus" { project_close } - } + project_close } proc fpga_part { PART } { - global TOOL FAMILY DEVICE PACKAGE SPEED - fpga_print "adding the part '$PART'" - switch $TOOL { - "quartus" { - set_global_assignment -name DEVICE $PART - } - } + set_global_assignment -name DEVICE $PART } 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 { - "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 - } - } + 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 } } proc fpga_include {PATH} { - global TOOL INCLUDED lappend INCLUDED $PATH - fpga_print "setting '$PATH' as a search location" - switch $TOOL { - "quartus" { - # Verilog Included Files are NOT added - foreach INCLUDE $INCLUDED { - set_global_assignment -name SEARCH_PATH $INCLUDE - } - } + # Verilog Included Files are NOT added + foreach INCLUDE $INCLUDED { + set_global_assignment -name SEARCH_PATH $INCLUDE } } proc fpga_top { TOP } { - global TOOL - fpga_print "specifying the top level '$TOP'" - switch $TOOL { - "quartus" { - set_global_assignment -name TOP_LEVEL_ENTITY $TOP - } - } + set_global_assignment -name TOP_LEVEL_ENTITY $TOP } proc fpga_params {} { - global TOOL PARAMS if { [llength $PARAMS] == 0 } { return } - fpga_print "setting generics/parameters" - switch $TOOL { - "quartus" { - foreach PARAM $PARAMS { - eval "set_parameter -name $PARAM" - } - } + foreach PARAM $PARAMS { + eval "set_parameter -name $PARAM" } } proc fpga_run_syn {} { - global TOOL PRESYNTH - fpga_print "running 'synthesis'" - switch $TOOL { - "quartus" { - execute_module -tool map - } - } + execute_module -tool map } proc fpga_run_par {} { - global TOOL PRESYNTH - fpga_print "running 'place and route'" - switch $TOOL { - "quartus" { - execute_module -tool fit - execute_module -tool sta - } - } + execute_module -tool fit + execute_module -tool sta } proc fpga_run_bit {} { - global TOOL PROJECT TOP - fpga_print "running 'bitstream generation'" - switch $TOOL { - "quartus" { - execute_module -tool asm - } - } + execute_module -tool asm } -# -# 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 - } + fpga_create $PROJECT + fpga_part $PART + {{ PRECFG }} + fpga_files + fpga_top $TOP + fpga_params + {{ POSTCFG }} + fpga_close } -# -# Design Flow -# - if { [lsearch -regexp $TASKS "syn|par|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 "par"] >= 0 } { - fpga_run_par - fpga_commands "postpar" - } - 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 + fpga_open $PROJECT + if { [lsearch -exact $TASKS "syn"] >= 0 } { + {{ PRESYN }} + fpga_run_syn + } + if { [lsearch -exact $TASKS "par"] >= 0 } { + {{ PREPAR }} + fpga_run_par + } + if { [lsearch -exact $TASKS "bit"] >= 0 } { + {{ PREBIT }} + fpga_run_bit + {{ POSTBIT }} } + fpga_close } - -# -# End of the script -# - -fpga_print "end of the Tcl script" diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index 5758f1c7..a8fac10d 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -5,7 +5,6 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -set TOOL #TOOL# set PRESYNTH #PRESYNTH# set PROJECT #PROJECT# set PART #PART# @@ -23,249 +22,117 @@ 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# - } - "postpar" { -#POSTPAR_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 { - "vivado" { create_project -force $PROJECT } - } + create_project -force $PROJECT } proc fpga_open { PROJECT } { - global TOOL - fpga_print "opening the project '$PROJECT'" - switch $TOOL { - "vivado" { open_project $PROJECT } - } + open_project $PROJECT } proc fpga_close {} { - global TOOL - fpga_print "closing the project" - switch $TOOL { - "vivado" { close_project } - } + close_project } proc fpga_part { PART } { - global TOOL FAMILY DEVICE PACKAGE SPEED - fpga_print "adding the part '$PART'" - switch $TOOL { - "vivado" { - set_property "part" $PART [current_project] - } - } -} + 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 { - "vivado" { - if { $LIBRARY != "work" } { - add_files $FILE - set_property library $LIBRARY [get_files $FILE] - } else { - add_files $FILE - } - } + 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 { - "vivado" { - # Verilog Included Files are NOT added - set_property "include_dirs" $INCLUDED [current_fileset] - } - } + # 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 - } - } + 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 } } proc fpga_top { TOP } { - global TOOL - fpga_print "specifying the top level '$TOP'" - switch $TOOL { - "vivado" { - set_property top $TOP [current_fileset] - } - } + set_property top $TOP [current_fileset] } proc fpga_params {} { - global TOOL PARAMS if { [llength $PARAMS] == 0 } { return } - fpga_print "setting generics/parameters" - switch $TOOL { - "vivado" { - set assigns [list] - foreach PARAM $PARAMS { lappend assigns [join $PARAM "="] } - set obj [get_filesets sources_1] - set_property "generic" "[join $assigns]" -objects $obj - } - } + 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 { - "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 - } - } + if { $PRESYNTH == "True" } { + set_property design_mode GateLvl [current_fileset] + } else { + reset_run synth_1 + launch_runs synth_1 + wait_on_run synth_1 } } proc fpga_run_par {} { - global TOOL PRESYNTH - fpga_print "running 'place and route'" - switch $TOOL { - "vivado" { - if {$PRESYNTH == "False"} { - open_run synth_1 - } - launch_runs impl_1 - wait_on_run impl_1 - } + if {$PRESYNTH == "False"} { + open_run synth_1 } + launch_runs impl_1 + wait_on_run impl_1 } proc fpga_run_bit {} { - global TOOL PROJECT TOP - fpga_print "running 'bitstream generation'" - switch $TOOL { - "vivado" { - open_run impl_1 - write_bitstream -force $PROJECT - } - } + open_run impl_1 + write_bitstream -force $PROJECT } -# -# 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 - } + fpga_create $PROJECT + fpga_part $PART + {{ PRECFG }} + fpga_files + fpga_top $TOP + fpga_params + {{ POSTCFG }} + fpga_close } -# -# Design Flow -# - if { [lsearch -regexp $TASKS "syn|par|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 "par"] >= 0 } { - fpga_run_par - fpga_commands "postpar" - } - 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 + fpga_open $PROJECT + if { [lsearch -exact $TASKS "syn"] >= 0 } { + {{ PRESYN }} + fpga_run_syn + } + if { [lsearch -exact $TASKS "par"] >= 0 } { + {{ PREPAR }} + fpga_run_par + } + if { [lsearch -exact $TASKS "bit"] >= 0 } { + {{ PREBIT }} + fpga_run_bit + {{ POSTBIT }} } + fpga_close } - -# -# End of the script -# - -fpga_print "end of the Tcl script" From b20db612652d8d4c85b8c16e99e95722d6133ca9 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 31 May 2024 19:34:40 -0300 Subject: [PATCH 047/248] Added two hook stages, returned to the single function version --- docs/hooks.rst | 2 ++ pyfpga/project.py | 47 +++++++++++++++-------------------------------- 2 files changed, 17 insertions(+), 32 deletions(-) diff --git a/docs/hooks.rst b/docs/hooks.rst index a9328332..498aceed 100644 --- a/docs/hooks.rst +++ b/docs/hooks.rst @@ -19,8 +19,10 @@ Hooks open project presyn hook synthesis + postsyn hook prepar hook place_and_route + postpar hook prebit hook bitstream postbit hook diff --git a/pyfpga/project.py b/pyfpga/project.py index 912a61e8..d700e8df 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -12,20 +12,11 @@ import os import subprocess -from enum import Enum from datetime import datetime from pathlib import Path from time import time -class Step(Enum): - """Enumeration of supported Steps""" - PRJ = 'prj' - SYN = 'syn' - PAR = 'par' - BIT = 'bit' - - class Project: """Base class to manage an FPGA project. @@ -116,32 +107,24 @@ def set_top(self, name): self.logger.debug('Executing set_top') self.data['top'] = name - def add_precfg_hook(self, content): - """Temp placeholder""" - raise NotImplementedError('Method is not implemented yet.') - - def add_postcfg_hook(self, content): - """Temp placeholder""" - raise NotImplementedError('Method is not implemented yet.') - - def add_presyn_hook(self, content): - """Temp placeholder""" - raise NotImplementedError('Method is not implemented yet.') - - def add_prepar_hook(self, content): - """Temp placeholder""" - raise NotImplementedError('Method is not implemented yet.') - - def add_prebit_hook(self, content): - """Temp placeholder""" - raise NotImplementedError('Method is not implemented yet.') - - def add_postbit_hook(self, content): - """Temp placeholder""" + def add_hook(self, stage, hook): + """Add hook for a specific stage.""" + stages = [ + 'precfg', 'postcfg', 'presyn', 'postsyn', + 'prepar', 'postpar', 'prebit', 'postbit' + ] + if stage not in stages: + raise ValueError('Invalid stage.') + _ = self + _ = hook raise NotImplementedError('Method is not implemented yet.') - def make(self, end=Step.BIT, start=Step.PRJ, capture=False): + def make(self, end='bit', start='prj'): """Temp placeholder""" + steps = ['prj', 'syn', 'par', 'bit'] + if end not in steps or start not in steps: + raise ValueError('Invalid steps.') + _ = self raise NotImplementedError('Method is not implemented yet.') def prog(self, position=1, bitstream=None): From b48ad68cda44f6026ecc69b052dca7e635a9f2b7 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 31 May 2024 21:09:02 -0300 Subject: [PATCH 048/248] Templates simplified, still WIP --- pyfpga/templates/ise.jinja | 157 ++++++++++++------------- pyfpga/templates/libero.jinja | 201 +++++++++++++++------------------ pyfpga/templates/quartus.jinja | 133 ++++++++++------------ pyfpga/templates/vivado.jinja | 156 +++++++++++-------------- 4 files changed, 291 insertions(+), 356 deletions(-) diff --git a/pyfpga/templates/ise.jinja b/pyfpga/templates/ise.jinja index 2aa7a6b1..a5f31223 100644 --- a/pyfpga/templates/ise.jinja +++ b/pyfpga/templates/ise.jinja @@ -5,41 +5,19 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -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 par bit -set TASKS [list #TASKS#] - -set PARAMS [list #PARAMS#] +set PRESYNTH {{ PRESYNTH }} +set PROJECT {{ PROJECT }} +set PART {{ PART }} +set FAMILY {{ FAMILY }} +set DEVICE {{ DEVICE }} +set PACKAGE {{ PACKAGE }} +set SPEED {{ SPEED }} +set TOP {{ TOP }} -proc fpga_files {} { -#FILES# -} +set PARAMS [list {{ PARAMS }}] -proc fpga_create { PROJECT } { - if { [ file exists $PROJECT.xise ] } { file delete $PROJECT.xise } - project new $PROJECT.xise -} - -proc fpga_open { PROJECT } { - project open $PROJECT.xise -} - -proc fpga_close {} { - project close -} - -proc fpga_part { PART } { - project set family $FAMILY - project set device $DEVICE - project set package $PACKAGE - project set speed $SPEED +proc fpga_files {} { +{{ FILES }} } proc fpga_file {FILE {LIBRARY "work"}} { @@ -67,10 +45,6 @@ proc fpga_include {PATH} { [join $INCLUDED "|"] -process "Synthesize - XST" } -proc fpga_top { TOP } { - project set top $TOP -} - proc fpga_params {} { if { [llength $PARAMS] == 0 } { return } set assigns [list] @@ -78,56 +52,69 @@ proc fpga_params {} { project set "Generics, Parameters" "[join $assigns]" -process "Synthesize - XST" } -proc fpga_run_syn {} { - if { $PRESYNTH == "True" } { - project set top_level_module_type "EDIF" - } else { - project clean - process run "Synthesize" - if { [process get "Synthesize" status] == "errors" } { exit 2 } - } -} +#------------------------------------------------------------------------------ -proc fpga_run_par {} { - 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 } -} +{% if PRJ %} +if { [ file exists $PROJECT.xise ] } { file delete $PROJECT.xise } +project new $PROJECT.xise -proc fpga_run_bit {} { - process run "Generate Programming File" - if { [process get "Generate Programming File" status] == "errors" } { exit 2 } - catch { file rename -force $TOP.bit $PROJECT.bit } -} +project set family $FAMILY +project set device $DEVICE +project set package $PACKAGE +project set speed $SPEED -if { [lsearch -exact $TASKS "prj"] >= 0 } { - fpga_create $PROJECT - fpga_part $PART - {{ PRECFG }} - fpga_files - fpga_top $TOP - fpga_params - {{ POSTCFG }} - fpga_close -} +{{ PRECFG }} -if { [lsearch -regexp $TASKS "syn|par|bit"] >= 0 } { - fpga_open $PROJECT - if { [lsearch -exact $TASKS "syn"] >= 0 } { - {{ PRESYN }} - fpga_run_syn - } - if { [lsearch -exact $TASKS "par"] >= 0 } { - {{ PREPAR }} - fpga_run_par - } - if { [lsearch -exact $TASKS "bit"] >= 0 } { - {{ PREBIT }} - fpga_run_bit - {{ POSTBIT }} - } - fpga_close +fpga_files + +project set top $TOP + +fpga_params + +{{ POSTCFG }} + +project close +{% endif %} + +{% if SYN or PAR or BIT %} +project open $PROJECT.xise + +{% if SYN %} +{{ PRESYN }} + +if { $PRESYNTH == "True" } { + project set top_level_module_type "EDIF" +} else { + project clean + process run "Synthesize" + if { [process get "Synthesize" status] == "errors" } { exit 2 } } + +{{ POSTSYN }} +{% endif %} + +{% if PAR %} +{{ PREPAR }} + +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 } + +{{ POSTPAR }} +{% endif %} + +{% if BIT %} +{{ PREBIT }} + +process run "Generate Programming File" +if { [process get "Generate Programming File" status] == "errors" } { exit 2 } +catch { file rename -force $TOP.bit $PROJECT.bit } + +{{ POSTBIT }} +{% endif %} + +project close +{% endif %} diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index 7cdcfd33..8e703ed3 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -5,38 +5,19 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -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 par bit -set TASKS [list #TASKS#] - -set PARAMS [list #PARAMS#] +set PRESYNTH {{ PRESYNTH }} +set PROJECT {{ PROJECT }} +set PART {{ PART }} +set FAMILY {{ FAMILY }} +set DEVICE {{ DEVICE }} +set PACKAGE {{ PACKAGE }} +set SPEED {{ SPEED }} +set TOP {{ TOP }} -proc fpga_files {} { -#FILES# -} - -proc fpga_create { PROJECT } { - if { [ file exists $PROJECT ] } { file delete -force -- $PROJECT } - new_project -name $PROJECT -location $PROJECT -hdl {VHDL} -family {SmartFusion2} -} - -proc fpga_open { PROJECT } { - open_project $PROJECT/$PROJECT.prjx -} +set PARAMS [list {{ PARAMS }}] -proc fpga_close {} { - close_project -} - -proc fpga_part { PART } { - set_device -family $FAMILY -die $DEVICE -package $PACKAGE -speed $SPEED +proc fpga_files {} { +{{ FILES }} } proc fpga_file {FILE {LIBRARY "work"}} { @@ -72,90 +53,96 @@ proc fpga_include {PATH} { build_design_hierarchy } -proc fpga_top { TOP } { - set_root $TOP - # Verilog Included files - set cmd "configure_tool -name {SYNTHESIZE} -params {SYNPLIFY_OPTIONS:" - if { [info exists INCLUDED] && [llength $INCLUDED] > 0 } { - # See <ROOT>/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 - } -} - proc fpga_params {} { if { [llength $PARAMS] == 0 } { return } # They must be specified after set_root (see fpga_top) } -proc fpga_run_syn {} { - run_tool -name {SYNTHESIZE} -} +#------------------------------------------------------------------------------ -proc fpga_run_par {} { - run_tool -name {PLACEROUTE} - run_tool -name {VERIFYTIMING} -} +{% if PRJ %} +if { [ file exists $PROJECT ] } { file delete -force -- $PROJECT } +new_project -name $PROJECT -location $PROJECT -hdl {VHDL} -family {SmartFusion2} -proc fpga_run_bit {} { - run_tool -name {GENERATEPROGRAMMINGFILE} -} +set_device -family $FAMILY -die $DEVICE -package $PACKAGE -speed $SPEED -if { [lsearch -exact $TASKS "prj"] >= 0 } { - fpga_create $PROJECT - fpga_part $PART - {{ PRECFG }} - fpga_files - fpga_top $TOP - fpga_params - {{ POSTCFG }} - fpga_close -} +{{ PRECFG }} -if { [lsearch -regexp $TASKS "syn|par|bit"] >= 0 } { - fpga_open $PROJECT - if { [lsearch -exact $TASKS "syn"] >= 0 } { - {{ PRESYN }} - fpga_run_syn - } - if { [lsearch -exact $TASKS "par"] >= 0 } { - {{ PREPAR }} - fpga_run_par - } - if { [lsearch -exact $TASKS "bit"] >= 0 } { - {{ PREBIT }} - fpga_run_bit - {{ POSTBIT }} - } - fpga_close +fpga_files + +set_root $TOP +# Verilog Included files +set cmd "configure_tool -name {SYNTHESIZE} -params {SYNPLIFY_OPTIONS:" +if { [info exists INCLUDED] && [llength $INCLUDED] > 0 } { + # See <ROOT>/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 } + +fpga_params + +{{ POSTCFG }} + +close_project +{% endif %} + +{% if SYN or PAR or BIT %} +open_project $PROJECT/$PROJECT.prjx + +{% if SYN %} +{{ PRESYN }} + +run_tool -name {SYNTHESIZE} + +{{ POSTSYN }} +{% endif %} + +{% if PAR %} +{{ PREPAR }} + +run_tool -name {PLACEROUTE} +run_tool -name {VERIFYTIMING} + +{{ POSTPAR }} +{% endif %} + +{% if BIT %} +{{ PREBIT }} + +run_tool -name {GENERATEPROGRAMMINGFILE} + +{{ POSTBIT }} +{% endif %} + +close_project +{% endif %} diff --git a/pyfpga/templates/quartus.jinja b/pyfpga/templates/quartus.jinja index 4a46bce8..966035ca 100644 --- a/pyfpga/templates/quartus.jinja +++ b/pyfpga/templates/quartus.jinja @@ -5,40 +5,19 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -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 par bit -set TASKS [list #TASKS#] - -set PARAMS [list #PARAMS#] +set PRESYNTH {{ PRESYNTH }} +set PROJECT {{ PROJECT }} +set PART {{ PART }} +set FAMILY {{ FAMILY }} +set DEVICE {{ DEVICE }} +set PACKAGE {{ PACKAGE }} +set SPEED {{ SPEED }} +set TOP {{ TOP }} -proc fpga_files {} { -#FILES# -} +set PARAMS [list {{ PARAMS }}] -proc fpga_create { PROJECT } { - package require ::quartus::project - project_new $PROJECT -overwrite - set_global_assignment -name NUM_PARALLEL_PROCESSORS ALL -} - -proc fpga_open { PROJECT } { - package require ::quartus::flow - project_open -force $PROJECT.qpf -} - -proc fpga_close {} { - project_close -} - -proc fpga_part { PART } { - set_global_assignment -name DEVICE $PART +proc fpga_files {} { +{{ FILES }} } proc fpga_file {FILE {LIBRARY "work"}} { @@ -75,10 +54,6 @@ proc fpga_include {PATH} { } } -proc fpga_top { TOP } { - set_global_assignment -name TOP_LEVEL_ENTITY $TOP -} - proc fpga_params {} { if { [llength $PARAMS] == 0 } { return } foreach PARAM $PARAMS { @@ -86,44 +61,56 @@ proc fpga_params {} { } } -proc fpga_run_syn {} { - execute_module -tool map -} +#------------------------------------------------------------------------------ -proc fpga_run_par {} { - execute_module -tool fit - execute_module -tool sta -} +{% if PRJ %} +package require ::quartus::project +project_new $PROJECT -overwrite +set_global_assignment -name NUM_PARALLEL_PROCESSORS ALL -proc fpga_run_bit {} { - execute_module -tool asm -} +set_global_assignment -name DEVICE $PART -if { [lsearch -exact $TASKS "prj"] >= 0 } { - fpga_create $PROJECT - fpga_part $PART - {{ PRECFG }} - fpga_files - fpga_top $TOP - fpga_params - {{ POSTCFG }} - fpga_close -} +{{ PRECFG }} -if { [lsearch -regexp $TASKS "syn|par|bit"] >= 0 } { - fpga_open $PROJECT - if { [lsearch -exact $TASKS "syn"] >= 0 } { - {{ PRESYN }} - fpga_run_syn - } - if { [lsearch -exact $TASKS "par"] >= 0 } { - {{ PREPAR }} - fpga_run_par - } - if { [lsearch -exact $TASKS "bit"] >= 0 } { - {{ PREBIT }} - fpga_run_bit - {{ POSTBIT }} - } - fpga_close -} +fpga_files + +set_global_assignment -name TOP_LEVEL_ENTITY $TOP + +fpga_params + +{{ POSTCFG }} + +project_close +{% endif %} + +{% if SYN or PAR or BIT %} +package require ::quartus::flow +project_open -force $PROJECT.qpf + +{% if SYN %} +{{ PRESYN }} + +execute_module -tool map + +{{ POSTSYN }} +{% endif %} + +{% if PAR %} +{{ PREPAR }} + +execute_module -tool fit +execute_module -tool sta + +{{ POSTPAR }} +{% endif %} + +{% if BIT %} +{{ PREBIT }} + +execute_module -tool asm + +{{ POSTBIT }} +{% endif %} + +project_close +{% endif %} diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index a8fac10d..0cb772ea 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -5,39 +5,21 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -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 par bit -set TASKS [list #TASKS#] - -set PARAMS [list #PARAMS#] +set PRESYNTH {{ PRESYNTH }} +set PROJECT {{ PROJECT }} +set PART {{ PART }} +set FAMILY {{ FAMILY }} +set DEVICE {{ DEVICE }} +set PACKAGE {{ PACKAGE }} +set SPEED {{ SPEED }} +set TOP {{ TOP }} -proc fpga_files {} { -#FILES# -} - -proc fpga_create { PROJECT } { - create_project -force $PROJECT -} - -proc fpga_open { PROJECT } { - open_project $PROJECT -} +set PARAMS [list {{ PARAMS }}] -proc fpga_close {} { - close_project +proc fpga_files {} { +{{ FILES }} } -proc fpga_part { PART } { - set_property "part" $PART [current_project] - } - proc fpga_file {FILE {LIBRARY "work"}} { set message "adding the file '$FILE'" if { $LIBRARY != "work" } { append message " (into the VHDL library '$LIBRARY')" } @@ -60,23 +42,6 @@ proc fpga_include {PATH} { set_property "include_dirs" $INCLUDED [current_fileset] } -proc fpga_design {FILE} { - fpga_print "including the block design '$FILE'" - 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 - } -} - -proc fpga_top { TOP } { - set_property top $TOP [current_fileset] -} - proc fpga_params {} { if { [llength $PARAMS] == 0 } { return } set assigns [list] @@ -85,54 +50,63 @@ proc fpga_params {} { set_property "generic" "[join $assigns]" -objects $obj } -proc fpga_run_syn {} { - if { $PRESYNTH == "True" } { - set_property design_mode GateLvl [current_fileset] - } else { - reset_run synth_1 - launch_runs synth_1 - wait_on_run synth_1 - } -} +#------------------------------------------------------------------------------ -proc fpga_run_par {} { - if {$PRESYNTH == "False"} { - open_run synth_1 - } - launch_runs impl_1 - wait_on_run impl_1 -} +{% if PRJ %} +create_project -force $PROJECT -proc fpga_run_bit {} { - open_run impl_1 - write_bitstream -force $PROJECT -} +set_property "part" $PART [current_project] + +{{ PRECFG }} + +fpga_files + +set_property top $TOP [current_fileset] + +fpga_params -if { [lsearch -exact $TASKS "prj"] >= 0 } { - fpga_create $PROJECT - fpga_part $PART - {{ PRECFG }} - fpga_files - fpga_top $TOP - fpga_params - {{ POSTCFG }} - fpga_close +{{ POSTCFG }} + +close_project +{% endif %} + +{% if SYN or PAR or BIT %} +open_project $PROJECT + +{% if SYN %} +{{ PRESYN }} + +if { $PRESYNTH == "True" } { + set_property design_mode GateLvl [current_fileset] +} else { + reset_run synth_1 + launch_runs synth_1 + wait_on_run synth_1 } -if { [lsearch -regexp $TASKS "syn|par|bit"] >= 0 } { - fpga_open $PROJECT - if { [lsearch -exact $TASKS "syn"] >= 0 } { - {{ PRESYN }} - fpga_run_syn - } - if { [lsearch -exact $TASKS "par"] >= 0 } { - {{ PREPAR }} - fpga_run_par - } - if { [lsearch -exact $TASKS "bit"] >= 0 } { - {{ PREBIT }} - fpga_run_bit - {{ POSTBIT }} - } - fpga_close +{{ POSTSYN }} +{% endif %} + +{% if PAR %} +{{ PREPAR }} + +if {$PRESYNTH == "False"} { + open_run synth_1 } +launch_runs impl_1 +wait_on_run impl_1 + +{{ POSTPAR }} +{% endif %} + +{% if BIT %} +{{ PREBIT }} + +open_run impl_1 +write_bitstream -force $PROJECT + +{{ POSTBIT }} +{% endif %} + +close_project +{% endif %} From 52e82f1c36816efe0be6f11b4fc61169952a814b Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 31 May 2024 22:02:01 -0300 Subject: [PATCH 049/248] Renamed step PRJ as CFG --- pyfpga/project.py | 2 +- pyfpga/templates/ise.jinja | 6 ++++-- pyfpga/templates/libero.jinja | 6 ++++-- pyfpga/templates/quartus.jinja | 8 +++++--- pyfpga/templates/vivado.jinja | 9 +++++++-- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index d700e8df..26f5a7d9 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -121,7 +121,7 @@ def add_hook(self, stage, hook): def make(self, end='bit', start='prj'): """Temp placeholder""" - steps = ['prj', 'syn', 'par', 'bit'] + steps = ['cfg', 'syn', 'par', 'bit'] if end not in steps or start not in steps: raise ValueError('Invalid steps.') _ = self diff --git a/pyfpga/templates/ise.jinja b/pyfpga/templates/ise.jinja index a5f31223..02adbc57 100644 --- a/pyfpga/templates/ise.jinja +++ b/pyfpga/templates/ise.jinja @@ -52,9 +52,9 @@ proc fpga_params {} { project set "Generics, Parameters" "[join $assigns]" -process "Synthesize - XST" } -#------------------------------------------------------------------------------ +#--[ Project configuration ]--------------------------------------------------- -{% if PRJ %} +{% if CFG %} if { [ file exists $PROJECT.xise ] } { file delete $PROJECT.xise } project new $PROJECT.xise @@ -76,6 +76,8 @@ fpga_params project close {% endif %} +#--[ Design flow ]------------------------------------------------------------- + {% if SYN or PAR or BIT %} project open $PROJECT.xise diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index 8e703ed3..ac335d55 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -58,9 +58,9 @@ proc fpga_params {} { # They must be specified after set_root (see fpga_top) } -#------------------------------------------------------------------------------ +#--[ Project configuration ]--------------------------------------------------- -{% if PRJ %} +{% if CFG %} if { [ file exists $PROJECT ] } { file delete -force -- $PROJECT } new_project -name $PROJECT -location $PROJECT -hdl {VHDL} -family {SmartFusion2} @@ -116,6 +116,8 @@ fpga_params close_project {% endif %} +#--[ Design flow ]------------------------------------------------------------- + {% if SYN or PAR or BIT %} open_project $PROJECT/$PROJECT.prjx diff --git a/pyfpga/templates/quartus.jinja b/pyfpga/templates/quartus.jinja index 966035ca..7c94c7cb 100644 --- a/pyfpga/templates/quartus.jinja +++ b/pyfpga/templates/quartus.jinja @@ -61,12 +61,11 @@ proc fpga_params {} { } } -#------------------------------------------------------------------------------ +#--[ Project configuration ]--------------------------------------------------- -{% if PRJ %} +{% if CFG %} package require ::quartus::project project_new $PROJECT -overwrite -set_global_assignment -name NUM_PARALLEL_PROCESSORS ALL set_global_assignment -name DEVICE $PART @@ -83,9 +82,12 @@ fpga_params project_close {% endif %} +#--[ Design flow ]------------------------------------------------------------- + {% if SYN or PAR or BIT %} package require ::quartus::flow project_open -force $PROJECT.qpf +set_global_assignment -name NUM_PARALLEL_PROCESSORS ALL {% if SYN %} {{ PRESYN }} diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index 0cb772ea..2fc0b4bf 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -50,9 +50,9 @@ proc fpga_params {} { set_property "generic" "[join $assigns]" -objects $obj } -#------------------------------------------------------------------------------ +#--[ Project configuration ]--------------------------------------------------- -{% if PRJ %} +{% if CFG %} create_project -force $PROJECT set_property "part" $PART [current_project] @@ -70,8 +70,11 @@ fpga_params close_project {% endif %} +#--[ Design flow ]------------------------------------------------------------- + {% if SYN or PAR or BIT %} open_project $PROJECT +set_param general.maxThreads [expr {[exec nproc] < 32 ? [exec nproc] : 32}] {% if SYN %} {{ PRESYN }} @@ -93,6 +96,7 @@ if { $PRESYNTH == "True" } { if {$PRESYNTH == "False"} { open_run synth_1 } +reset_run impl_1 launch_runs impl_1 wait_on_run impl_1 @@ -104,6 +108,7 @@ wait_on_run impl_1 open_run impl_1 write_bitstream -force $PROJECT +write_debug_probes -force -quiet $PROJECT.ltx {{ POSTBIT }} {% endif %} From d1eb4b1c54f7d65ec163831dfce3de86a4c5cb51 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 31 May 2024 23:41:42 -0300 Subject: [PATCH 050/248] Last iteration on templates before final customization --- pyfpga/templates/ise.jinja | 21 ++++++--------- pyfpga/templates/libero.jinja | 5 ---- pyfpga/templates/quartus.jinja | 5 ---- pyfpga/templates/vivado.jinja | 47 +++++++++++++++++++++------------- 4 files changed, 37 insertions(+), 41 deletions(-) diff --git a/pyfpga/templates/ise.jinja b/pyfpga/templates/ise.jinja index 02adbc57..08678558 100644 --- a/pyfpga/templates/ise.jinja +++ b/pyfpga/templates/ise.jinja @@ -5,7 +5,6 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -set PRESYNTH {{ PRESYNTH }} set PROJECT {{ PROJECT }} set PART {{ PART }} set FAMILY {{ FAMILY }} @@ -16,10 +15,6 @@ set TOP {{ TOP }} set PARAMS [list {{ PARAMS }}] -proc fpga_files {} { -{{ FILES }} -} - proc fpga_file {FILE {LIBRARY "work"}} { set message "adding the file '$FILE'" if { $LIBRARY != "work" } { append message " (into the VHDL library '$LIBRARY')" } @@ -65,7 +60,7 @@ project set speed $SPEED {{ PRECFG }} -fpga_files +{{ FILES }} project set top $TOP @@ -84,13 +79,13 @@ project open $PROJECT.xise {% if SYN %} {{ PRESYN }} -if { $PRESYNTH == "True" } { - project set top_level_module_type "EDIF" -} else { - project clean - process run "Synthesize" - if { [process get "Synthesize" status] == "errors" } { exit 2 } -} +{% if PRESYNTH %} +project set top_level_module_type "EDIF" +{% else %} +project clean +process run "Synthesize" +if { [process get "Synthesize" status] == "errors" } { exit 2 } +{% endif %} {{ POSTSYN }} {% endif %} diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index ac335d55..35ec3f52 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -5,7 +5,6 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -set PRESYNTH {{ PRESYNTH }} set PROJECT {{ PROJECT }} set PART {{ PART }} set FAMILY {{ FAMILY }} @@ -16,10 +15,6 @@ set TOP {{ TOP }} set PARAMS [list {{ PARAMS }}] -proc fpga_files {} { -{{ FILES }} -} - proc fpga_file {FILE {LIBRARY "work"}} { set message "adding the file '$FILE'" if { $LIBRARY != "work" } { append message " (into the VHDL library '$LIBRARY')" } diff --git a/pyfpga/templates/quartus.jinja b/pyfpga/templates/quartus.jinja index 7c94c7cb..76c29fc5 100644 --- a/pyfpga/templates/quartus.jinja +++ b/pyfpga/templates/quartus.jinja @@ -5,7 +5,6 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -set PRESYNTH {{ PRESYNTH }} set PROJECT {{ PROJECT }} set PART {{ PART }} set FAMILY {{ FAMILY }} @@ -16,10 +15,6 @@ set TOP {{ TOP }} set PARAMS [list {{ PARAMS }}] -proc fpga_files {} { -{{ FILES }} -} - proc fpga_file {FILE {LIBRARY "work"}} { set message "adding the file '$FILE'" if { $LIBRARY != "work" } { append message " (into the VHDL library '$LIBRARY')" } diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index 2fc0b4bf..c1805387 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -5,7 +5,6 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -set PRESYNTH {{ PRESYNTH }} set PROJECT {{ PROJECT }} set PART {{ PART }} set FAMILY {{ FAMILY }} @@ -16,10 +15,6 @@ set TOP {{ TOP }} set PARAMS [list {{ PARAMS }}] -proc fpga_files {} { -{{ FILES }} -} - proc fpga_file {FILE {LIBRARY "work"}} { set message "adding the file '$FILE'" if { $LIBRARY != "work" } { append message " (into the VHDL library '$LIBRARY')" } @@ -54,16 +49,32 @@ proc fpga_params {} { {% if CFG %} 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] +set_property part $PART [current_project] {{ PRECFG }} -fpga_files +{{ FILES }} set_property top $TOP [current_fileset] -fpga_params +{% if DEFINES %} +set_property verilog_define { {{ DEFINES}} } [current_fileset] +{% endif %} + +{% if INCLUDES %} +set_property include_dirs { {{ INCLUDES}} } [current_fileset] +{% endif %} + +{% if PARAMS %} +set_property generic { {{ PARAMS }} } -objects [get_filesets sources_1] +{% endif %} + +{% if ARCH %} +set_property top_arch {{ ARCH }} [current_fileset] +{% endif %} {{ POSTCFG }} @@ -79,13 +90,13 @@ set_param general.maxThreads [expr {[exec nproc] < 32 ? [exec nproc] : 32}] {% if SYN %} {{ PRESYN }} -if { $PRESYNTH == "True" } { - set_property design_mode GateLvl [current_fileset] -} else { - reset_run synth_1 - launch_runs synth_1 - wait_on_run synth_1 -} +{% if PRESYNTH %} +set_property design_mode GateLvl [current_fileset] +{% else %} +reset_run synth_1 +launch_runs synth_1 +wait_on_run synth_1 +{% endif %} {{ POSTSYN }} {% endif %} @@ -93,9 +104,9 @@ if { $PRESYNTH == "True" } { {% if PAR %} {{ PREPAR }} -if {$PRESYNTH == "False"} { - open_run synth_1 -} +{% if not PRESYNTH %} +open_run synth_1 +{% endif %} reset_run impl_1 launch_runs impl_1 wait_on_run impl_1 From 504b1fcbf5b87281f51af17745b77f1286bdf05b Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 1 Jun 2024 00:04:19 -0300 Subject: [PATCH 051/248] docs: re-added previously removed sections, with a WIP notice --- docs/{wip => }/advanced.rst | 18 ++--- docs/{wip => }/basic.rst | 27 +++++-- docs/index.rst | 5 +- docs/{wip/intro.rst => tools.rst} | 48 +++++++---- docs/wip/conf.py | 129 ------------------------------ docs/wip/dev.rst | 75 ----------------- docs/wip/tools.rst | 34 -------- 7 files changed, 64 insertions(+), 272 deletions(-) rename docs/{wip => }/advanced.rst (97%) rename docs/{wip => }/basic.rst (89%) rename docs/{wip/intro.rst => tools.rst} (71%) delete mode 100644 docs/wip/conf.py delete mode 100644 docs/wip/dev.rst delete mode 100644 docs/wip/tools.rst diff --git a/docs/wip/advanced.rst b/docs/advanced.rst similarity index 97% rename from docs/wip/advanced.rst rename to docs/advanced.rst index 3ee54d32..245f4c08 100644 --- a/docs/wip/advanced.rst +++ b/docs/advanced.rst @@ -1,12 +1,12 @@ -.. program:: pyfpga +Advanced usage +============== -.. _advanced: +.. ATTENTION:: -Advanced usage -############## + (2024-05-31) To be updated. Multi project managment -======================= +----------------------- .. code-block:: python @@ -54,7 +54,7 @@ Multi project managment .. _hooks: Hooks -===== +----- The following table depicts the parts of the *Project Creation* and the *Design Flow* internally performed by PyFPGA. @@ -93,7 +93,7 @@ specify additional *hooks* in different parts of the flow, using: method several times (the commands will be executed in order). Parameters -========== +---------- The generics/parameters of the project can be optionally changed with: @@ -104,7 +104,7 @@ The generics/parameters of the project can be optionally changed with: prj.add_param('paramN', valueN) Generate options -================ +---------------- The method ``generate`` (previously seen at the end of [Basic usage](#basic-usage) section) has optional parameters: @@ -143,7 +143,7 @@ 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. diff --git a/docs/wip/basic.rst b/docs/basic.rst similarity index 89% rename from docs/wip/basic.rst rename to docs/basic.rst index 7a5456c8..824aea8d 100644 --- a/docs/wip/basic.rst +++ b/docs/basic.rst @@ -1,12 +1,12 @@ -.. program:: pyfpga +Basic usage +=========== -.. _basic: +.. ATTENTION:: -Basic usage -########### + (2024-05-31) To be updated. 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* @@ -23,6 +23,17 @@ name is used when *no name* is provided). The supported tool are: ``ghdl``, ``ise``, ``libero``, ``openflow``, ``quartus``, ``vivado``, ``yosys``, ``yosys-ise`` and ``yosys-vivado``. +.. 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). + 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. @@ -85,7 +96,7 @@ Finally, the top-level must be specified: 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: @@ -111,7 +122,7 @@ Additionally, you can specify which task to perform: * ``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). @@ -134,7 +145,7 @@ Jtag chain. When a memory is used as *devtype*, the *part* name and the (udev rule, be a part of a group, etc). Logging capabilities -==================== +-------------------- PyFPGA uses the `logging <https://docs.python.org/3/library/logging.html>`_ module, with a *NULL* handler and the *INFO* level by default. diff --git a/docs/index.rst b/docs/index.rst index 96f4b3d9..4984a41d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -8,11 +8,14 @@ PyFPGA's documentation .. ATTENTION:: - (2024-05-20) PyFPGA is in the process of being strongly rewritten/simplified. + (2024-05-31) PyFPGA is in the process of being strongly rewritten/simplified. Most changes are internal, but the API will also change. .. toctree:: intro + basic + advanced hooks + tools api diff --git a/docs/wip/intro.rst b/docs/tools.rst similarity index 71% rename from docs/wip/intro.rst rename to docs/tools.rst index b10d326a..0bbbcc5a 100644 --- a/docs/wip/intro.rst +++ b/docs/tools.rst @@ -1,18 +1,39 @@ -.. program:: pyfpga - -Introduction -############ +Tools support +============= .. ATTENTION:: - PyFPGA assumes that the backend Tool is ready to run. - This implies, depending on the operating system, things such as: + (2024-05-31) To be updated. + ++---------------+-----------+---------+-----+-----------------------------------------------+ +| 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. - * 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). +* 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). Detailed support ---------------- @@ -80,8 +101,3 @@ Detailed support * ``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 :repositoy:`Examples <examples>`. diff --git a/docs/wip/conf.py b/docs/wip/conf.py deleted file mode 100644 index ffead9cc..00000000 --- a/docs/wip/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/docs/wip/dev.rst b/docs/wip/dev.rst deleted file mode 100644 index 39486ea3..00000000 --- a/docs/wip/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/<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 (``<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/docs/wip/tools.rst b/docs/wip/tools.rst deleted file mode 100644 index 365a4844..00000000 --- a/docs/wip/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). From a01bcd7cc20fbc55eaf9306585e8a7d938934d8c Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 1 Jun 2024 01:09:36 -0300 Subject: [PATCH 052/248] README updated/simplified --- README.md | 97 ++++++++++++++----------------------------------------- 1 file changed, 24 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 671fa6a1..3a60df40 100644 --- a/README.md +++ b/README.md @@ -6,24 +6,19 @@ ![ISE](https://img.shields.io/badge/ISE-14.7-blue.svg?style=flat-square) ![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) -> **WARNING:** (2024-05-20) PyFPGA is in the process of being strongly rewritten/simplified. -> Most changes are internal, but the API will also change. +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. -PyFPGA is a **Python Package** for **vendor-agnostic** FPGA development. -It provides a **Class** which allows the programmatically execution of **synthesis**, -**place and route**, **bitstream generation** and/or **programming** of FPGA devices. -Additionally, a set of **command-line helpers** are provided for quick and simple runs. +With PyFPGA, you can create your own FPGA development workflow tailored to your needs! -Features: -* It's *Version Control Systems* and *Continuous Integration* friendly. -* Allows reproducibility and repeatability. -* Consumes fewer system resources than GUI based workflows. +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. -With PyFPGA you can create your custom FPGA tool using a workflow tailored to your needs! - -## Usage - -A minimal example of how to use PyFPGA: +## Basic example ```py from fpga import Project @@ -48,80 +43,36 @@ prj.set_top('Top') prj.generate() ``` -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 -[HDL containers](https://hdl.github.io/containers) 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.7. + +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 -``` - -Then, use pip from the root of the repo: - -``` pip install -e . ``` -> 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. +> 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. From 087e3a6d79829b8a91308ab20b0758ffe01ce098 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 1 Jun 2024 18:56:30 -0300 Subject: [PATCH 053/248] Moved helpers from fpga to pyfpga --- {fpga => pyfpga}/helpers/bitprog.py | 0 {fpga => pyfpga}/helpers/hdl2bit.py | 0 {fpga => pyfpga}/helpers/prj2bit.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {fpga => pyfpga}/helpers/bitprog.py (100%) rename {fpga => pyfpga}/helpers/hdl2bit.py (100%) rename {fpga => pyfpga}/helpers/prj2bit.py (100%) diff --git a/fpga/helpers/bitprog.py b/pyfpga/helpers/bitprog.py similarity index 100% rename from fpga/helpers/bitprog.py rename to pyfpga/helpers/bitprog.py diff --git a/fpga/helpers/hdl2bit.py b/pyfpga/helpers/hdl2bit.py similarity index 100% rename from fpga/helpers/hdl2bit.py rename to pyfpga/helpers/hdl2bit.py diff --git a/fpga/helpers/prj2bit.py b/pyfpga/helpers/prj2bit.py similarity index 100% rename from fpga/helpers/prj2bit.py rename to pyfpga/helpers/prj2bit.py From e9f2dca70c65bfaa177b5989a4b491af5a88c211 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 1 Jun 2024 18:57:38 -0300 Subject: [PATCH 054/248] Renamed fpga/project.py as pyfpga/factory.py --- fpga/project.py => pyfpga/factory.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename fpga/project.py => pyfpga/factory.py (100%) diff --git a/fpga/project.py b/pyfpga/factory.py similarity index 100% rename from fpga/project.py rename to pyfpga/factory.py From b6a0356bb364e7113463687be89bbe803a468ba5 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 1 Jun 2024 19:02:34 -0300 Subject: [PATCH 055/248] Simplified some methods at project, defined internal data structure, updated setup.py --- pyfpga/project.py | 12 +++--------- setup.py | 16 ++++++++-------- tests/test_data.py | 19 +++++++++++++++++-- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index 26f5a7d9..5ac00cd4 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -79,23 +79,17 @@ def add_vlog(self, pathname, options=None): def add_include(self, path): """Temp placeholder""" self.logger.debug('Executing add_include') - if 'includes' not in self.data: - self.data['includes'] = [] - self.data['includes'].append(path) + self.data.setdefault('includes', []).append(path) def add_param(self, name, value): """Temp placeholder""" self.logger.debug('Executing add_param') - if 'params' not in self.data: - self.data['params'] = {} - self.data['params'][name] = value + self.data.setdefault('params', {})[name] = value def add_define(self, name, value): """Temp placeholder""" self.logger.debug('Executing add_define') - if 'defines' not in self.data: - self.data['defines'] = {} - self.data['defines'][name] = value + self.data.setdefault('defines', {})[name] = value def set_arch(self, name): """Temp placeholder""" diff --git a/setup.py b/setup.py index 05036c4a..e6b45d1b 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ 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', 'helpers/*']}, + 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,14 +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'] + ] ) diff --git a/tests/test_data.py b/tests/test_data.py index e603af21..3b7da4df 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -5,9 +5,13 @@ pattern = { 'part': 'PARTNAME', - 'top': 'TOPNAME', - 'arch': 'ARCHNAME', 'includes': ['INC1', 'INC2', 'INC3'], + 'files': { + 'path1': {'type': 'TYPE1', 'options': 'OPTION1', 'library': 'LIBRARY1'}, + 'path2': {'type': 'TYPE2', 'options': 'OPTION2', 'library': 'LIBRARY2'}, + 'path3': {'type': 'TYPE3', 'options': 'OPTION3', 'library': 'LIBRARY3'} + }, + 'top': 'TOPNAME', 'params': { 'PARAM1': 'VALUE1', 'PARAM2': 'VALUE2', @@ -18,6 +22,17 @@ 'DEF2': 'VALUE2', 'DEF3': 'VALUE3' }, + 'arch': 'ARCHNAME', + 'hooks': { + 'precfg': ['HOOK1', 'HOOK2', 'HOOKn'], + 'postcfg': ['HOOK1', 'HOOK2', 'HOOKn'], + 'presyn': ['HOOK1', 'HOOK2', 'HOOKn'], + 'postsyn': ['HOOK1', 'HOOK2', 'HOOKn'], + 'prepar': ['HOOK1', 'HOOK2', 'HOOKn'], + 'postpar': ['HOOK1', 'HOOK2', 'HOOKn'], + 'prebit': ['HOOK1', 'HOOK2', 'HOOKn'], + 'postbit': ['HOOK1', 'HOOK2', 'HOOKn'] + } } From 16b3999a832341b3626e15ae0c6177f2f4284444 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 1 Jun 2024 19:31:11 -0300 Subject: [PATCH 056/248] Fixed pylint complaint --- tests/test_data.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_data.py b/tests/test_data.py index 3b7da4df..66652fd5 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -7,9 +7,9 @@ 'part': 'PARTNAME', 'includes': ['INC1', 'INC2', 'INC3'], 'files': { - 'path1': {'type': 'TYPE1', 'options': 'OPTION1', 'library': 'LIBRARY1'}, - 'path2': {'type': 'TYPE2', 'options': 'OPTION2', 'library': 'LIBRARY2'}, - 'path3': {'type': 'TYPE3', 'options': 'OPTION3', 'library': 'LIBRARY3'} + 'path1': {'type': 'TYPE1', 'options': 'OPT1', 'library': 'LIB1'}, + 'path2': {'type': 'TYPE2', 'options': 'OPT2', 'library': 'LIB2'}, + 'path3': {'type': 'TYPE3', 'options': 'OPT3', 'library': 'LIB3'} }, 'top': 'TOPNAME', 'params': { From 2bf79bc5f5e23b085089b783b39e74148f684243 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 1 Jun 2024 20:17:52 -0300 Subject: [PATCH 057/248] Fixed pylint complaints --- pyfpga/factory.py | 80 ++++++++--------------------------------------- 1 file changed, 13 insertions(+), 67 deletions(-) diff --git a/pyfpga/factory.py b/pyfpga/factory.py index 069d2be7..d05695d1 100644 --- a/pyfpga/factory.py +++ b/pyfpga/factory.py @@ -1,24 +1,11 @@ # -# Copyright (C) 2019-2022 Rodrigo A. Melo -# Copyright (C) 2019-2020 INTI +# Copyright (C) 2019-2024 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 <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # -"""fpga.project -This module implements the entry-point of PyFPGA, which provides -functionalities to create a project, generate a bitstream and program a device. +""" +A factory class to create FPGA projects. """ import contextlib @@ -29,6 +16,12 @@ import re import time +from fpga.tool.ise import Ise +from fpga.tool.libero import Libero +from fpga.tool.openflow import Openflow +from fpga.tool.quartus import Quartus +from fpga.tool.vivado import Vivado + TOOLS = [ 'ghdl', 'ise', 'libero', 'openflow', 'quartus', 'vivado', 'yosys', @@ -57,29 +50,22 @@ class Project: """ def __init__( - self, tool='vivado', project=None, meta=None, + self, tool='vivado', project=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) @@ -93,42 +79,6 @@ def __init__( self._absdir = os.path.join(self._rundir, self._reldir) _log.debug('ABSDIR = %s', self._absdir) self.set_outdir('build') - self._initialize(meta) - - def _initialize(self, meta): - """Set some of the most used internal parameters.""" - if meta is None: - return - if 'outdir' in meta: - _log.debug('OUTDIR = %s', meta['outdir']) - self.set_outdir(meta['outdir']) - if 'part' in meta: - _log.debug('PART = %s', meta['part']) - self.set_part(meta['part']) - if 'paths' in meta: - for path in meta['paths']: - _log.debug('PATH = %s', path) - self.add_vlog_include(path) - for filetype in ['vhdl', 'verilog', 'constraint']: - if filetype in meta: - for file in meta[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 meta: - for parname, parvalue in meta['params'].items(): - _log.debug('PARAM = %s %s', parname, parvalue) - self.add_param(parname, parvalue) - if 'top' in meta: - _log.debug('TOP = %s', meta['top']) - self.set_top(meta['top']) def set_outdir(self, outdir): """Sets the OUTput DIRectory (where to put the resulting files). @@ -324,8 +274,7 @@ def set_bitstream(self, path): self.tool.set_bitstream(path) def transfer( - self, devtype='fpga', position=1, part='', width=1, - capture=False): + self, devtype='fpga', position=1, part='', width=1): """Transfers the generated bitstream to a device. :param devtype: *fpga* or other valid option @@ -333,7 +282,6 @@ def transfer( :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 FileNotFoundError: when the bitstream is not found :raises ValueError: when devtype, position or width are unsupported @@ -344,9 +292,7 @@ def transfer( 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) + return self.tool.transfer(devtype, position, part, width, True) def clean(self): """Clean the generated project files.""" From c88eaa6caee14696a61130a983085a58c8bad0fd Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 1 Jun 2024 20:23:04 -0300 Subject: [PATCH 058/248] Renamed and implemented _add_file, implemented add_hook --- pyfpga/project.py | 19 +++++++++---------- tests/test_data.py | 43 ++++++++++++++++++++++++++++++++----------- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index 5ac00cd4..bb922c9c 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -49,24 +49,25 @@ def set_part(self, name): self.logger.debug('Executing set_part') self.data['part'] = name - def add_file(self, pathname, filetype=None, library=None, options=None): - """Temp placeholder""" - raise NotImplementedError('Method is not implemented yet.') + def _add_file(self, pathname, filetype=None, library=None, options=None): + self.data.setdefault('files', {})[pathname] = { + 'type': filetype, 'options': options, 'library': library + } def add_cons(self, pathname, options=None): """Temp placeholder""" self.logger.debug('Executing add_cons') - self.add_file(pathname, filetype='cons', options=options) + self._add_file(pathname, filetype='cons', options=options) def add_slog(self, pathname, options=None): """Temp placeholder""" self.logger.debug('Executing add_slog') - self.add_file(pathname, filetype='slog', options=options) + self._add_file(pathname, filetype='slog', options=options) def add_vhdl(self, pathname, library=None, options=None): """Temp placeholder""" self.logger.debug('Executing add_vhdl') - self.add_file( + self._add_file( pathname, filetype='vhdl', library=library, options=options ) @@ -74,7 +75,7 @@ def add_vhdl(self, pathname, library=None, options=None): def add_vlog(self, pathname, options=None): """Temp placeholder""" self.logger.debug('Executing add_vlog') - self.add_file(pathname, filetype='vlog', options=options) + self._add_file(pathname, filetype='vlog', options=options) def add_include(self, path): """Temp placeholder""" @@ -109,9 +110,7 @@ def add_hook(self, stage, hook): ] if stage not in stages: raise ValueError('Invalid stage.') - _ = self - _ = hook - raise NotImplementedError('Method is not implemented yet.') + self.data.setdefault('hooks', {}).setdefault(stage, []).append(hook) def make(self, end='bit', start='prj'): """Temp placeholder""" diff --git a/tests/test_data.py b/tests/test_data.py index 66652fd5..b17b2dd4 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -7,9 +7,10 @@ 'part': 'PARTNAME', 'includes': ['INC1', 'INC2', 'INC3'], 'files': { - 'path1': {'type': 'TYPE1', 'options': 'OPT1', 'library': 'LIB1'}, - 'path2': {'type': 'TYPE2', 'options': 'OPT2', 'library': 'LIB2'}, - 'path3': {'type': 'TYPE3', 'options': 'OPT3', 'library': 'LIB3'} + 'path1': {'type': 'vhdl', 'options': 'OPT1', 'library': 'LIB1'}, + 'path2': {'type': 'vlog', 'options': 'OPT2', 'library': None}, + 'path3': {'type': 'slog', 'options': 'OPT3', 'library': None}, + 'path4': {'type': 'cons', 'options': 'OPT4', 'library': None} }, 'top': 'TOPNAME', 'params': { @@ -24,14 +25,14 @@ }, 'arch': 'ARCHNAME', 'hooks': { - 'precfg': ['HOOK1', 'HOOK2', 'HOOKn'], - 'postcfg': ['HOOK1', 'HOOK2', 'HOOKn'], - 'presyn': ['HOOK1', 'HOOK2', 'HOOKn'], - 'postsyn': ['HOOK1', 'HOOK2', 'HOOKn'], - 'prepar': ['HOOK1', 'HOOK2', 'HOOKn'], - 'postpar': ['HOOK1', 'HOOK2', 'HOOKn'], - 'prebit': ['HOOK1', 'HOOK2', 'HOOKn'], - 'postbit': ['HOOK1', 'HOOK2', 'HOOKn'] + 'precfg': ['HOOK1', 'HOOK2'], + 'postcfg': ['HOOK1', 'HOOK2'], + 'presyn': ['HOOK1', 'HOOK2'], + 'postsyn': ['HOOK1', 'HOOK2'], + 'prepar': ['HOOK1', 'HOOK2'], + 'postpar': ['HOOK1', 'HOOK2'], + 'prebit': ['HOOK1', 'HOOK2'], + 'postbit': ['HOOK1', 'HOOK2'] } } @@ -50,4 +51,24 @@ def test_names(): prj.add_define('DEF1', 'VALUE1') prj.add_define('DEF2', 'VALUE2') prj.add_define('DEF3', 'VALUE3') + prj.add_vhdl('path1', 'LIB1', 'OPT1') + prj.add_vlog('path2', 'OPT2') + prj.add_slog('path3', 'OPT3') + prj.add_cons('path4', 'OPT4') + prj.add_hook('precfg', 'HOOK1') + prj.add_hook('precfg', 'HOOK2') + prj.add_hook('postcfg', 'HOOK1') + prj.add_hook('postcfg', 'HOOK2') + prj.add_hook('presyn', 'HOOK1') + prj.add_hook('presyn', 'HOOK2') + prj.add_hook('postsyn', 'HOOK1') + prj.add_hook('postsyn', 'HOOK2') + prj.add_hook('prepar', 'HOOK1') + prj.add_hook('prepar', 'HOOK2') + prj.add_hook('postpar', 'HOOK1') + prj.add_hook('postpar', 'HOOK2') + prj.add_hook('prebit', 'HOOK1') + prj.add_hook('prebit', 'HOOK2') + prj.add_hook('postbit', 'HOOK1') + prj.add_hook('postbit', 'HOOK2') assert prj.data == pattern, 'ERROR: unexpected data' From ecb3aa4c27777859d79902690bdd4b68ae57cc36 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 2 Jun 2024 09:28:00 -0300 Subject: [PATCH 059/248] Added some pending doc-strings --- pyfpga/project.py | 88 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 82 insertions(+), 6 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index bb922c9c..0a5479c0 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -8,6 +8,7 @@ Base class that implements agnostic methods to deal with FPGA projects. """ +# import glob import logging import os import subprocess @@ -45,11 +46,52 @@ def __init__(self, name=None, odir='results'): self.logger.addHandler(handler) def set_part(self, name): - """Temp placeholder""" + """Set the FPGA part name. + + :param name: FPGA part name + :type name: str + """ self.logger.debug('Executing set_part') self.data['part'] = name def _add_file(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) self.data.setdefault('files', {})[pathname] = { 'type': filetype, 'options': options, 'library': library } @@ -78,27 +120,61 @@ def add_vlog(self, pathname, options=None): self._add_file(pathname, filetype='vlog', options=options) def add_include(self, path): - """Temp placeholder""" + """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') + # 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_vlog_include(path) + # else: + # raise NotADirectoryError(path) self.data.setdefault('includes', []).append(path) def add_param(self, name, value): - """Temp placeholder""" + """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') self.data.setdefault('params', {})[name] = value def add_define(self, name, value): - """Temp placeholder""" + """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') self.data.setdefault('defines', {})[name] = value def set_arch(self, name): - """Temp placeholder""" + """Set the VHDL architecture. + + :param name: architecture name + :type name: str + """ self.logger.debug('Executing set_arch') self.data['arch'] = name def set_top(self, name): - """Temp placeholder""" + """Set the name of the top level component. + + :param name: top-level name + :type name: str + """ self.logger.debug('Executing set_top') self.data['top'] = name From 316dfdcb5693c11368d1b2989bd312d2b014d1f1 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 2 Jun 2024 09:28:52 -0300 Subject: [PATCH 060/248] Removed already processed things --- pyfpga/factory.py | 189 +++------------------------------------------- 1 file changed, 12 insertions(+), 177 deletions(-) diff --git a/pyfpga/factory.py b/pyfpga/factory.py index d05695d1..55b92fb3 100644 --- a/pyfpga/factory.py +++ b/pyfpga/factory.py @@ -8,13 +8,9 @@ A factory class to create FPGA projects. """ -import contextlib -import glob import inspect import logging import os -import re -import time from fpga.tool.ise import Ise from fpga.tool.libero import Libero @@ -78,142 +74,6 @@ def __init__( _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') - - 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 add_param(self, name, value): - """Add a Generic/Parameter Value. - - :param name: parameter/generic name - :param value: value to be assigned - """ - self.tool.add_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_vlog_include(self, path): - """Add a Verilog include 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_vlog_include(path) - else: - raise NotADirectoryError(path) - - def add_vlog_define(self, name, value): - """Add a Verilog define.""" - raise NotImplementedError() - - def set_vhdl_arch(self, name): - """Set the VHDL architecture.""" - raise NotImplementedError() - - 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. @@ -254,14 +114,13 @@ def generate(self, to_task='bit', from_task='prj', capture=False): ``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) + # _log.info( + # 'generating "%s" project using "%s" tool into "%s" directory', + # self.tool.project, self.tool.get_configs()['tool'], self.outdir + # ) + 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. @@ -287,32 +146,8 @@ def transfer( :raises ValueError: when devtype, position or width are unsupported :raises RuntimeError: when the tool to be used is not found """ - _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(): - return self.tool.transfer(devtype, position, part, width, True) - - 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): - """Run 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) + # _log.info( + # 'transfering "%s" project using "%s" tool from "%s" directory', + # self.tool.project, self.tool.get_configs()['tool'], self.outdir + # ) + return self.tool.transfer(devtype, position, part, width, True) From 739b12f3967248ed90f665e73088b2aa87bb93aa Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 2 Jun 2024 09:32:05 -0300 Subject: [PATCH 061/248] Removed test_top.py (top autodiscovery was deprecated) --- Makefile | 2 +- tests/test_top.py | 31 ------------------------------- 2 files changed, 1 insertion(+), 32 deletions(-) delete mode 100644 tests/test_top.py diff --git a/Makefile b/Makefile index 351413fc..6e5d920f 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ lint: git diff --check --cached test: - pytest + pytest tests clean: py3clean . diff --git a/tests/test_top.py b/tests/test_top.py deleted file mode 100644 index fdb894f4..00000000 --- a/tests/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" From 62e2fd9dd11846a3c5c237d7b92801ca01fb8896 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 2 Jun 2024 13:34:45 -0300 Subject: [PATCH 062/248] Renamed hooks as internals, added info about the internal data structure --- docs/hooks.rst | 29 ------------------- docs/index.rst | 4 +-- docs/internals.rst | 70 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 31 deletions(-) delete mode 100644 docs/hooks.rst create mode 100644 docs/internals.rst diff --git a/docs/hooks.rst b/docs/hooks.rst deleted file mode 100644 index 498aceed..00000000 --- a/docs/hooks.rst +++ /dev/null @@ -1,29 +0,0 @@ -Hooks -===== - -.. code-block:: - - create project - config project - part - precfg hook - params - defines - includes - files - arch - top - postcfg hook - close project - - open project - presyn hook - synthesis - postsyn hook - prepar hook - place_and_route - postpar hook - prebit hook - bitstream - postbit hook - close project diff --git a/docs/index.rst b/docs/index.rst index 4984a41d..88c75aa4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -16,6 +16,6 @@ PyFPGA's documentation intro basic advanced - hooks - tools api + tools + internals diff --git a/docs/internals.rst b/docs/internals.rst new file mode 100644 index 00000000..0ec8ded5 --- /dev/null +++ b/docs/internals.rst @@ -0,0 +1,70 @@ +Internals +========= + +Underlying tool steps +--------------------- + +.. code-block:: + + create project + config project + part + precfg hook + params + defines + includes + files + arch + top + postcfg hook + close project + + open project + presyn hook + synthesis + postsyn hook + prepar hook + place_and_route + postpar hook + prebit hook + bitstream + postbit hook + close project + +Internal data structure +----------------------- + +.. code-block:: + + data = { + 'part': 'PARTNAME', + 'includes': ['DIR1', 'DIR2', 'DIR3'], + 'files': { + 'file1': {'type': 'vhdl', 'options': 'OPT1', 'library': 'LIB1'}, + 'file2': {'type': 'vlog', 'options': 'OPT2', 'library': None}, + 'file3': {'type': 'slog', 'options': 'OPT3', 'library': None}, + 'file4': {'type': 'cons', 'options': 'OPT4', 'library': None} + }, + 'top': 'TOPNAME', + 'params': { + 'PAR1': 'VAL1', + 'PAR2': 'VAL2', + 'PAR3': 'VAL3' + }, + 'defines': { + 'DEF1': 'VAL1', + 'DEF2': 'VAL2', + 'DEF3': 'VAL3' + }, + 'arch': 'ARCHNAME', + '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'] + } + } From ea3c46820a003258656b453918811dd8a263b7a7 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 2 Jun 2024 13:36:59 -0300 Subject: [PATCH 063/248] Added a directory with empty files, to test methods that deal with directories/files --- tests/fakedata/dir1/slog1.sv | 0 tests/fakedata/dir1/slog1.svh | 0 tests/fakedata/dir1/vhdl1.vhd | 0 tests/fakedata/dir1/vhdl1.vhdl | 0 tests/fakedata/dir1/vlog1.v | 0 tests/fakedata/dir1/vlog1.vh | 0 tests/fakedata/dir2/slog2.sv | 0 tests/fakedata/dir2/slog2.svh | 0 tests/fakedata/dir2/vhdl2.vhd | 0 tests/fakedata/dir2/vhdl2.vhdl | 0 tests/fakedata/dir2/vlog2.v | 0 tests/fakedata/dir2/vlog2.vh | 0 tests/fakedata/dir3/slog3.sv | 0 tests/fakedata/dir3/slog3.svh | 0 tests/fakedata/dir3/vhdl3.vhd | 0 tests/fakedata/dir3/vhdl3.vhdl | 0 tests/fakedata/dir3/vlog3.v | 0 tests/fakedata/dir3/vlog3.vh | 0 tests/fakedata/slog0.sv | 0 tests/fakedata/slog0.svh | 0 tests/fakedata/vhdl0.vhd | 0 tests/fakedata/vhdl0.vhdl | 0 tests/fakedata/vlog0.v | 0 tests/fakedata/vlog0.vh | 0 24 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/fakedata/dir1/slog1.sv create mode 100644 tests/fakedata/dir1/slog1.svh create mode 100644 tests/fakedata/dir1/vhdl1.vhd create mode 100644 tests/fakedata/dir1/vhdl1.vhdl create mode 100644 tests/fakedata/dir1/vlog1.v create mode 100644 tests/fakedata/dir1/vlog1.vh create mode 100644 tests/fakedata/dir2/slog2.sv create mode 100644 tests/fakedata/dir2/slog2.svh create mode 100644 tests/fakedata/dir2/vhdl2.vhd create mode 100644 tests/fakedata/dir2/vhdl2.vhdl create mode 100644 tests/fakedata/dir2/vlog2.v create mode 100644 tests/fakedata/dir2/vlog2.vh create mode 100644 tests/fakedata/dir3/slog3.sv create mode 100644 tests/fakedata/dir3/slog3.svh create mode 100644 tests/fakedata/dir3/vhdl3.vhd create mode 100644 tests/fakedata/dir3/vhdl3.vhdl create mode 100644 tests/fakedata/dir3/vlog3.v create mode 100644 tests/fakedata/dir3/vlog3.vh create mode 100644 tests/fakedata/slog0.sv create mode 100644 tests/fakedata/slog0.svh create mode 100644 tests/fakedata/vhdl0.vhd create mode 100644 tests/fakedata/vhdl0.vhdl create mode 100644 tests/fakedata/vlog0.v create mode 100644 tests/fakedata/vlog0.vh 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 From 1455a0993e9f517127f7623574ca19a4875d4549 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 2 Jun 2024 16:55:51 -0300 Subject: [PATCH 064/248] Removed unused things --- pyfpga/factory.py | 137 +++++----------------------------------------- 1 file changed, 14 insertions(+), 123 deletions(-) diff --git a/pyfpga/factory.py b/pyfpga/factory.py index 55b92fb3..d7fc3502 100644 --- a/pyfpga/factory.py +++ b/pyfpga/factory.py @@ -8,146 +8,37 @@ A factory class to create FPGA projects. """ -import inspect -import logging -import os - from fpga.tool.ise import Ise from fpga.tool.libero import Libero from fpga.tool.openflow import Openflow from fpga.tool.quartus import Quartus from fpga.tool.vivado import Vivado +# pylint: disable=return-in-init +# pylint: disable=no-else-return +# pylint: disable=too-many-return-statements +# pylint: disable=too-few-public-methods -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 meta: a dict with metadata about the project - :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`` - """ +class Factory: + """A factory class to create FPGA projects.""" def __init__( - self, tool='vivado', project=None, - relative_to_script=True): + self, tool='vivado', project=None): """Class constructor.""" if tool == 'ghdl': - self.tool = Openflow(project, frontend='ghdl', backend='vhdl') + return Openflow(project, frontend='ghdl', backend='vhdl') elif tool in ['ise', 'yosys-ise']: - self.tool = Ise(project, 'yosys' if 'yosys' in tool else '') + return Ise(project, 'yosys' if 'yosys' in tool else '') elif tool == 'libero': - self.tool = Libero(project) + return Libero(project) elif tool == 'openflow': - self.tool = Openflow(project) + return Openflow(project) elif tool == 'quartus': - self.tool = Quartus(project) + return Quartus(project) elif tool in ['vivado', 'yosys-vivado']: - self.tool = Vivado(project, 'yosys' if 'yosys' in tool else '') + return Vivado(project, 'yosys' if 'yosys' in tool else '') elif tool == 'yosys': - self.tool = Openflow(project, frontend='yosys', backend='verilog') + return 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) - - 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 *par*), - ``postpar`` (to perform an action between *par* 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 ValueError: when to_task or from_task are unsupported - :raises RuntimeError: when the tool to be used is not found - - .. 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 - # ) - 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): - """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*) - :returns: STDOUT and STDERR messages - :raises FileNotFoundError: when the bitstream is not found - :raises ValueError: when devtype, position or width are unsupported - :raises RuntimeError: when the tool to be used is not found - """ - # _log.info( - # 'transfering "%s" project using "%s" tool from "%s" directory', - # self.tool.project, self.tool.get_configs()['tool'], self.outdir - # ) - return self.tool.transfer(devtype, position, part, width, True) From df2a2a2d94d9802bc0260e0491aba1ada57b70ca Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 2 Jun 2024 16:56:45 -0300 Subject: [PATCH 065/248] Removed test_files.py --- Makefile | 2 +- tests/test_files.py | 18 ------------------ 2 files changed, 1 insertion(+), 19 deletions(-) delete mode 100644 tests/test_files.py diff --git a/Makefile b/Makefile index 6e5d920f..9c8032b2 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ lint: git diff --check --cached test: - pytest tests + cd tests; pytest clean: py3clean . diff --git a/tests/test_files.py b/tests/test_files.py deleted file mode 100644 index ffe55f81..00000000 --- a/tests/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 From 79175ed68107321a9e2a45101b0aedebeb011c87 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 2 Jun 2024 16:57:03 -0300 Subject: [PATCH 066/248] Implemented directory/file exists check --- pyfpga/project.py | 131 +++++++++++++++++++++++++-------------------- tests/test_data.py | 115 ++++++++++++++++++++++----------------- 2 files changed, 140 insertions(+), 106 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index 0a5479c0..f7d49537 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -8,7 +8,7 @@ Base class that implements agnostic methods to deal with FPGA projects. """ -# import glob +import glob import logging import os import subprocess @@ -55,59 +55,48 @@ def set_part(self, name): self.data['part'] = name def _add_file(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) - self.data.setdefault('files', {})[pathname] = { - 'type': filetype, 'options': options, 'library': library - } - - def add_cons(self, pathname, options=None): - """Temp placeholder""" + files = glob.glob(pathname) + if len(files) == 0: + raise FileNotFoundError(pathname) + for file in files: + path = Path(file).resolve() + self.data.setdefault('files', {})[path] = { + 'type': filetype, 'options': options, 'library': library + } + + def add_cons(self, pathname): + """Add constraint file/s. + + :param pathname: path to a constraint file (glob compliant) + :type pathname: str + :raises FileNotFoundError: when pathname is not found + """ self.logger.debug('Executing add_cons') - self._add_file(pathname, filetype='cons', options=options) + self._add_file(pathname, filetype='cons', options=None) def add_slog(self, pathname, options=None): - """Temp placeholder""" + """Add System Verilog file/s. + + :param pathname: path to a SV file (glob compliant) + :type pathname: str + :param options: for the underlying tool + :type options: str, optional + :raises FileNotFoundError: when pathname is not found + """ self.logger.debug('Executing add_slog') self._add_file(pathname, filetype='slog', options=options) def add_vhdl(self, pathname, library=None, options=None): - """Temp placeholder""" + """Add VHDL file/s. + + :param pathname: path to a SV file (glob compliant) + :type pathname: str + :param library: VHDL library name + :type library: str, optional + :param options: for the underlying tool + :type options: str, optional + :raises FileNotFoundError: when pathname is not found + """ self.logger.debug('Executing add_vhdl') self._add_file( pathname, filetype='vhdl', @@ -115,7 +104,14 @@ def add_vhdl(self, pathname, library=None, options=None): ) def add_vlog(self, pathname, options=None): - """Temp placeholder""" + """Add Verilog file/s. + + :param pathname: path to a SV file (glob compliant) + :type pathname: str + :param options: for the underlying tool + :type options: str, optional + :raises FileNotFoundError: when pathname is not found + """ self.logger.debug('Executing add_vlog') self._add_file(pathname, filetype='vlog', options=options) @@ -129,13 +125,9 @@ def add_include(self, path): :raises NotADirectoryError: if path is not a directory """ self.logger.debug('Executing add_include') - # 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_vlog_include(path) - # else: - # raise NotADirectoryError(path) + path = Path(path).resolve() + if not path.is_dir(): + raise NotADirectoryError(path) self.data.setdefault('includes', []).append(path) def add_param(self, name, value): @@ -179,7 +171,16 @@ def set_top(self, name): self.data['top'] = name def add_hook(self, stage, hook): - """Add hook for a specific stage.""" + """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 + """ stages = [ 'precfg', 'postcfg', 'presyn', 'postsyn', 'prepar', 'postpar', 'prebit', 'postbit' @@ -189,7 +190,15 @@ def add_hook(self, stage, hook): self.data.setdefault('hooks', {}).setdefault(stage, []).append(hook) def make(self, end='bit', start='prj'): - """Temp placeholder""" + """Run the underlying tool. + + :param end: last task + :type end: str, optional + :param start: first task + :type start: str, optional + + .. note:: Valid values are ``cfg``, ``syn``, ``imp`` and ``bit``. + """ steps = ['cfg', 'syn', 'par', 'bit'] if end not in steps or start not in steps: raise ValueError('Invalid steps.') @@ -197,7 +206,13 @@ def make(self, end='bit', start='prj'): raise NotImplementedError('Method is not implemented yet.') def prog(self, position=1, bitstream=None): - """Temp placeholder""" + """Program the FPGA + + :param position: position of the device in the JTAG chain + :type position: str, optional + :param bitstream: bitstream to be programmed + :type bitstream: str, optional + """ raise NotImplementedError('Method is not implemented yet.') def _run(self, command): diff --git a/tests/test_data.py b/tests/test_data.py index b17b2dd4..4c363de5 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -1,38 +1,58 @@ import os import pytest +from pathlib import Path + from pyfpga.project import Project pattern = { 'part': 'PARTNAME', - 'includes': ['INC1', 'INC2', 'INC3'], + 'includes': [ + Path('fakedata/dir1').resolve(), + Path('fakedata/dir2').resolve(), + Path('fakedata/dir3').resolve() + ], 'files': { - 'path1': {'type': 'vhdl', 'options': 'OPT1', 'library': 'LIB1'}, - 'path2': {'type': 'vlog', 'options': 'OPT2', 'library': None}, - 'path3': {'type': 'slog', 'options': 'OPT3', 'library': None}, - 'path4': {'type': 'cons', 'options': 'OPT4', 'library': None} + Path('fakedata/dir1/slog1.sv').resolve(): + {'type': 'slog', 'options': None, 'library': None}, + Path('fakedata/dir2/slog2.sv').resolve(): + {'type': 'slog', 'options': None, 'library': None}, + Path('fakedata/dir3/slog3.sv').resolve(): + {'type': 'slog', 'options': None, 'library': None}, + Path('fakedata/dir1/vhdl1.vhdl').resolve(): + {'type': 'vhdl', 'options': None, 'library': None}, + Path('fakedata/dir2/vhdl2.vhdl').resolve(): + {'type': 'vhdl', 'options': None, 'library': None}, + Path('fakedata/dir3/vhdl3.vhdl').resolve(): + {'type': 'vhdl', 'options': None, 'library': None}, + Path('fakedata/dir1/vlog1.v').resolve(): + {'type': 'vlog', 'options': None, 'library': None}, + Path('fakedata/dir2/vlog2.v').resolve(): + {'type': 'vlog', 'options': None, 'library': None}, + Path('fakedata/dir3/vlog3.v').resolve(): + {'type': 'vlog', 'options': None, 'library': None} }, 'top': 'TOPNAME', 'params': { - 'PARAM1': 'VALUE1', - 'PARAM2': 'VALUE2', - 'PARAM3': 'VALUE3' + 'PAR1': 'VAL1', + 'PAR2': 'VAL2', + 'PAR3': 'VAL3' }, 'defines': { - 'DEF1': 'VALUE1', - 'DEF2': 'VALUE2', - 'DEF3': 'VALUE3' + 'DEF1': 'VAL1', + 'DEF2': 'VAL2', + 'DEF3': 'VAL3' }, 'arch': 'ARCHNAME', 'hooks': { - 'precfg': ['HOOK1', 'HOOK2'], - 'postcfg': ['HOOK1', 'HOOK2'], - 'presyn': ['HOOK1', 'HOOK2'], - 'postsyn': ['HOOK1', 'HOOK2'], - 'prepar': ['HOOK1', 'HOOK2'], - 'postpar': ['HOOK1', 'HOOK2'], - 'prebit': ['HOOK1', 'HOOK2'], - 'postbit': ['HOOK1', 'HOOK2'] + 'precfg': ['CMD1', 'CMD2'], + 'postcfg': ['CMD1', 'CMD2'], + 'presyn': ['CMD1', 'CMD2'], + 'postsyn': ['CMD1', 'CMD2'], + 'prepar': ['CMD1', 'CMD2'], + 'postpar': ['CMD1', 'CMD2'], + 'prebit': ['CMD1', 'CMD2'], + 'postbit': ['CMD1', 'CMD2'] } } @@ -42,33 +62,32 @@ def test_names(): prj.set_part('PARTNAME') prj.set_top('TOPNAME') prj.set_arch('ARCHNAME') - prj.add_include('INC1') - prj.add_include('INC2') - prj.add_include('INC3') - prj.add_param('PARAM1', 'VALUE1') - prj.add_param('PARAM2', 'VALUE2') - prj.add_param('PARAM3', 'VALUE3') - prj.add_define('DEF1', 'VALUE1') - prj.add_define('DEF2', 'VALUE2') - prj.add_define('DEF3', 'VALUE3') - prj.add_vhdl('path1', 'LIB1', 'OPT1') - prj.add_vlog('path2', 'OPT2') - prj.add_slog('path3', 'OPT3') - prj.add_cons('path4', 'OPT4') - prj.add_hook('precfg', 'HOOK1') - prj.add_hook('precfg', 'HOOK2') - prj.add_hook('postcfg', 'HOOK1') - prj.add_hook('postcfg', 'HOOK2') - prj.add_hook('presyn', 'HOOK1') - prj.add_hook('presyn', 'HOOK2') - prj.add_hook('postsyn', 'HOOK1') - prj.add_hook('postsyn', 'HOOK2') - prj.add_hook('prepar', 'HOOK1') - prj.add_hook('prepar', 'HOOK2') - prj.add_hook('postpar', 'HOOK1') - prj.add_hook('postpar', 'HOOK2') - prj.add_hook('prebit', 'HOOK1') - prj.add_hook('prebit', 'HOOK2') - prj.add_hook('postbit', 'HOOK1') - prj.add_hook('postbit', 'HOOK2') + prj.add_include('fakedata/dir1') + prj.add_include('fakedata/dir2') + prj.add_include('fakedata/dir3') + prj.add_slog('fakedata/**/*.sv') + prj.add_vhdl('fakedata/**/*.vhdl') + prj.add_vlog('fakedata/**/*.v') + prj.add_param('PAR1', 'VAL1') + prj.add_param('PAR2', 'VAL2') + prj.add_param('PAR3', 'VAL3') + prj.add_define('DEF1', 'VAL1') + prj.add_define('DEF2', 'VAL2') + prj.add_define('DEF3', 'VAL3') + prj.add_hook('precfg', 'CMD1') + prj.add_hook('precfg', 'CMD2') + prj.add_hook('postcfg', 'CMD1') + prj.add_hook('postcfg', 'CMD2') + prj.add_hook('presyn', 'CMD1') + prj.add_hook('presyn', 'CMD2') + prj.add_hook('postsyn', 'CMD1') + prj.add_hook('postsyn', 'CMD2') + prj.add_hook('prepar', 'CMD1') + prj.add_hook('prepar', 'CMD2') + prj.add_hook('postpar', 'CMD1') + prj.add_hook('postpar', 'CMD2') + prj.add_hook('prebit', 'CMD1') + prj.add_hook('prebit', 'CMD2') + prj.add_hook('postbit', 'CMD1') + prj.add_hook('postbit', 'CMD2') assert prj.data == pattern, 'ERROR: unexpected data' From 53bb363aa5c5cc2cf5ad5af846b019e2fd0bf0be Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Tue, 4 Jun 2024 21:25:39 -0300 Subject: [PATCH 067/248] Moved things from fpga to pyfpga --- pyfpga/factory.py | 2 +- {fpga/tool => pyfpga}/ise.py | 16 ++-------------- {fpga/tool => pyfpga}/libero.py | 16 ++-------------- {fpga/tool => pyfpga}/openflow.py | 16 ++-------------- {fpga/tool => pyfpga}/quartus.py | 16 ++-------------- .../templates/openflow-prog.jinja | 17 +++-------------- pyfpga/templates/vivado.jinja | 1 - fpga/tool/__init__.py => pyfpga/tool.py | 16 ++-------------- {fpga/tool => pyfpga}/vivado.py | 16 ++-------------- 9 files changed, 16 insertions(+), 100 deletions(-) rename {fpga/tool => pyfpga}/ise.py (88%) rename {fpga/tool => pyfpga}/libero.py (76%) rename {fpga/tool => pyfpga}/openflow.py (91%) rename {fpga/tool => pyfpga}/quartus.py (66%) rename fpga/tool/openprog.sh => pyfpga/templates/openflow-prog.jinja (57%) rename fpga/tool/__init__.py => pyfpga/tool.py (92%) rename {fpga/tool => pyfpga}/vivado.py (78%) diff --git a/pyfpga/factory.py b/pyfpga/factory.py index d7fc3502..d48547f6 100644 --- a/pyfpga/factory.py +++ b/pyfpga/factory.py @@ -1,5 +1,5 @@ # -# Copyright (C) 2019-2024 Rodrigo A. Melo +# Copyright (C) 2024 Rodrigo A. Melo # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/fpga/tool/ise.py b/pyfpga/ise.py similarity index 88% rename from fpga/tool/ise.py rename to pyfpga/ise.py index c6f5e83d..817ca433 100644 --- a/fpga/tool/ise.py +++ b/pyfpga/ise.py @@ -1,19 +1,7 @@ # -# Copyright (C) 2019-2020 INTI -# Copyright (C) 2019-2021 Rodrigo A. Melo +# Copyright (C) 2019-2024 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 <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # """fpga.tool.ise diff --git a/fpga/tool/libero.py b/pyfpga/libero.py similarity index 76% rename from fpga/tool/libero.py rename to pyfpga/libero.py index ee463993..c037284c 100644 --- a/fpga/tool/libero.py +++ b/pyfpga/libero.py @@ -1,19 +1,7 @@ # -# Copyright (C) 2019-2020 INTI -# Copyright (C) 2019-2021 Rodrigo A. Melo +# Copyright (C) 2019-2024 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 <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # """fpga.tool.libero diff --git a/fpga/tool/openflow.py b/pyfpga/openflow.py similarity index 91% rename from fpga/tool/openflow.py rename to pyfpga/openflow.py index 0eb6b905..b8207d58 100644 --- a/fpga/tool/openflow.py +++ b/pyfpga/openflow.py @@ -1,19 +1,7 @@ # -# Copyright (C) 2020 INTI -# Copyright (C) 2020-2021 Rodrigo A. Melo +# Copyright (C) 2020-2024 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 <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # """fpga.tool.openflow diff --git a/fpga/tool/quartus.py b/pyfpga/quartus.py similarity index 66% rename from fpga/tool/quartus.py rename to pyfpga/quartus.py index f7644618..94db6ad6 100644 --- a/fpga/tool/quartus.py +++ b/pyfpga/quartus.py @@ -1,19 +1,7 @@ # -# Copyright (C) 2019-2020 INTI -# Copyright (C) 2019-2020 Rodrigo A. Melo +# Copyright (C) 2019-2024 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 <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # """fpga.tool.quartus diff --git a/fpga/tool/openprog.sh b/pyfpga/templates/openflow-prog.jinja similarity index 57% rename from fpga/tool/openprog.sh rename to pyfpga/templates/openflow-prog.jinja index 2746d050..3be40f53 100644 --- a/fpga/tool/openprog.sh +++ b/pyfpga/templates/openflow-prog.jinja @@ -1,19 +1,8 @@ -#!/bin/bash # -# Copyright (C) 2020 Rodrigo A. Melo +# PyFPGA +# Copyright (C) 2020-2024 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 <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # set -e diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index c1805387..16bed243 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -85,7 +85,6 @@ close_project {% if SYN or PAR or BIT %} open_project $PROJECT -set_param general.maxThreads [expr {[exec nproc] < 32 ? [exec nproc] : 32}] {% if SYN %} {{ PRESYN }} diff --git a/fpga/tool/__init__.py b/pyfpga/tool.py similarity index 92% rename from fpga/tool/__init__.py rename to pyfpga/tool.py index 9fc09685..f02931a3 100644 --- a/fpga/tool/__init__.py +++ b/pyfpga/tool.py @@ -1,19 +1,7 @@ # -# Copyright (C) 2019-2020 INTI -# Copyright (C) 2019-2021 Rodrigo A. Melo +# Copyright (C) 2019-2024 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 <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # """fpga.tool diff --git a/fpga/tool/vivado.py b/pyfpga/vivado.py similarity index 78% rename from fpga/tool/vivado.py rename to pyfpga/vivado.py index 067644ed..24cd2ae9 100644 --- a/fpga/tool/vivado.py +++ b/pyfpga/vivado.py @@ -1,19 +1,7 @@ # -# Copyright (C) 2019-2020 INTI -# Copyright (C) 2019-2020 Rodrigo A. Melo +# Copyright (C) 2019-2024 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 <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # """fpga.tool.vivado From 29aae524114ed2b7f835cbf74220fda888b534de Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Tue, 4 Jun 2024 22:09:40 -0300 Subject: [PATCH 068/248] Removed unused things, commented others --- pyfpga/factory.py | 22 +-- pyfpga/ise.py | 243 +++++++++++++++-------------- pyfpga/libero.py | 127 ++++++++------- pyfpga/openflow.py | 381 +++++++++++++++++++++++---------------------- pyfpga/quartus.py | 94 +++++------ pyfpga/tool.py | 372 ++++++++++++++++++------------------------- pyfpga/vivado.py | 115 +++++++------- 7 files changed, 660 insertions(+), 694 deletions(-) diff --git a/pyfpga/factory.py b/pyfpga/factory.py index d48547f6..6236730e 100644 --- a/pyfpga/factory.py +++ b/pyfpga/factory.py @@ -8,11 +8,11 @@ A factory class to create FPGA projects. """ -from fpga.tool.ise import Ise -from fpga.tool.libero import Libero -from fpga.tool.openflow import Openflow -from fpga.tool.quartus import Quartus -from fpga.tool.vivado import Vivado +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 # pylint: disable=return-in-init # pylint: disable=no-else-return @@ -27,18 +27,18 @@ def __init__( self, tool='vivado', project=None): """Class constructor.""" if tool == 'ghdl': - return Openflow(project, frontend='ghdl', backend='vhdl') - elif tool in ['ise', 'yosys-ise']: - return Ise(project, 'yosys' if 'yosys' in tool else '') + return Openflow(project) # , frontend='ghdl', backend='vhdl') + elif tool == 'ise': + return Ise(project) elif tool == 'libero': return Libero(project) elif tool == 'openflow': return Openflow(project) elif tool == 'quartus': return Quartus(project) - elif tool in ['vivado', 'yosys-vivado']: - return Vivado(project, 'yosys' if 'yosys' in tool else '') + elif tool == 'vivado': + return Vivado(project) elif tool == 'yosys': - return Openflow(project, frontend='yosys', backend='verilog') + return Openflow(project) # , frontend='yosys', backend='verilog') else: raise NotImplementedError(tool) diff --git a/pyfpga/ise.py b/pyfpga/ise.py index 817ca433..329e6e48 100644 --- a/pyfpga/ise.py +++ b/pyfpga/ise.py @@ -4,14 +4,13 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -"""fpga.tool.ise - -Implements the support of ISE (Xilinx). +""" +Implements support for ISE. """ -import re +# import re -from fpga.tool import Tool, run +from pyfpga.project import Project _TEMPLATES = { 'fpga': """setMode -bs @@ -68,118 +67,126 @@ """ } +# pylint: disable=too-few-public-methods + -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' +class Ise(Project): + """Class to support ISE projects.""" + + tool = { + 'program': 'xtclsh', + 'command': 'xtclsh ise.tcl', } - for key, value in families.items(): - if re.match(key, part): - return value - return 'UNKNOWN' + +# _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/pyfpga/libero.py b/pyfpga/libero.py index c037284c..5513698e 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -4,14 +4,13 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -"""fpga.tool.libero - -Implements the support of Libero (Microchip/Microsemi). +""" +Implements support for Libero. """ -import re +# import re -from fpga.tool import Tool +from pyfpga.project import Project _TEMPLATES = { 'fpga': """\ @@ -28,64 +27,72 @@ # -clk_mode {free_running_clk} -programming_method {spi_slave} \ # -force_freq {OFF} -freq {4000000}" +# pylint: disable=too-few-public-methods + -class Libero(Tool): - """Implementation of the class to support Libero.""" +class Libero(Project): + """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' - ] + tool = { + 'program': 'libero', + 'command': 'libero SCRIPT: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 +# _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 transfer(self, devtype, position, part, width, capture): - super().transfer(devtype, position, part, width, capture) - raise NotImplementedError('transfer(libero)') +# 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' + +# 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/pyfpga/openflow.py b/pyfpga/openflow.py index b8207d58..b010ae40 100644 --- a/pyfpga/openflow.py +++ b/pyfpga/openflow.py @@ -4,203 +4,210 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -"""fpga.tool.openflow - -Implements the support of the open-source tools. """ +Implements support for an Open Source development flow. +""" + +# import os +from pyfpga.project import Project + +# pylint: disable=too-few-public-methods -import os -from fpga.tool import Tool, run +class Openflow(Project): + """Class to support Openflow.""" -class Openflow(Tool): - """Implementation of the class to support the open-source tools.""" + tool = { + 'program': 'docker', + 'command': 'bash openflow.sh', + } - _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' - ] +# _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 __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 _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 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 _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 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 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 <YOSYS>/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 <nextpnr>/ice40/main.cc - 'lp384', 'lp1k', 'lp4k', 'lp8k', 'hx1k', 'hx4k', 'hx8k', - 'up3k', 'up5k', 'u1k', 'u2k', 'u4k' - ] - if part.startswith(tuple(families)): - return 'ice40' - families = [ - # From <nextpnr>/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' +# def get_family(part): +# """Get the Family name from the specified part name.""" +# part = part.lower() +# families = [ +# # From <YOSYS>/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 <nextpnr>/ice40/main.cc +# 'lp384', 'lp1k', 'lp4k', 'lp8k', 'hx1k', 'hx4k', 'hx8k', +# 'up3k', 'up5k', 'u1k', 'u2k', 'u4k' +# ] +# if part.startswith(tuple(families)): +# return 'ice40' +# families = [ +# # From <nextpnr>/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/pyfpga/quartus.py b/pyfpga/quartus.py index 94db6ad6..9c6c861e 100644 --- a/pyfpga/quartus.py +++ b/pyfpga/quartus.py @@ -4,50 +4,54 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -"""fpga.tool.quartus - -Implements the support of Quartus (Intel/Altera). +""" +Implements support for Quartus. """ -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 +# import re +# import subprocess + +from pyfpga.project import Project + + +class Quartus(Project): + """Class to support Quartus.""" + + tool = { + 'program': 'quartus_sh', + 'command': 'quartus_sh --script quartus.tcl', + } + +# _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/pyfpga/tool.py b/pyfpga/tool.py index f02931a3..19a756d4 100644 --- a/pyfpga/tool.py +++ b/pyfpga/tool.py @@ -9,40 +9,17 @@ 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', 'postpar', 'postbit'] TASKS = ['prj', 'syn', 'par', 'bit'] +# def tcl_path(path): +# """Returns a Tcl suitable path.""" +# return path.replace(os.path.sep, "/") -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, "/") +# pylint: disable=too-few-public-methods class Tool: @@ -51,195 +28,152 @@ class Tool: 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': [], - 'postpar': [], - '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 add_param(self, name, value): - """Add 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_vlog_include(self, path): - """Add a Verilog include 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('#POSTPAR_CMDS#', '\n'.join(self.cmds['postpar'])) - 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) +# # 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': [], +# 'postpar': [], +# '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 _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('#POSTPAR_CMDS#', '\n'.join(self.cmds['postpar'])) +# 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 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/pyfpga/vivado.py b/pyfpga/vivado.py index 24cd2ae9..46008024 100644 --- a/pyfpga/vivado.py +++ b/pyfpga/vivado.py @@ -4,12 +4,11 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -"""fpga.tool.vivado - -Implements the support of Vivado (Xilinx). +""" +Implements support for Vivado. """ -from fpga.tool import Tool, run +from pyfpga.project import Project _TEMPLATES = { 'fpga': """\ @@ -28,58 +27,66 @@ """ } +# pylint: disable=too-few-public-methods + + +class Vivado(Project): + """Class to support Vivado.""" -class Vivado(Tool): - """Implementation of the class to support Vivado.""" + tool = { + 'program': 'vivado', + 'command': 'vivado -mode batch -notrace -quiet -source vivado.tcl', + } - _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' - ] +# _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 __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 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) +# 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) From 7d10d7b9acb7422776d07aad6253157c96799b70 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Tue, 4 Jun 2024 22:14:41 -0300 Subject: [PATCH 069/248] Added to check if the needed underlying tool is available --- pyfpga/project.py | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index f7d49537..5c054dd3 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -15,6 +15,7 @@ from datetime import datetime from pathlib import Path +from shutil import which from time import time @@ -25,9 +26,12 @@ class Project: :type name: str, optional :param odir: output directory :type odir: str, optional - :raises NotImplementedError: when a method is not implemented yet + :raises ValueError: when an invalid value is specified + :raises RuntimeError: when the needed underlying tool is not available """ + tool = {} + def __init__(self, name=None, odir='results'): """Class constructor.""" self.data = {} @@ -189,31 +193,41 @@ def add_hook(self, stage, hook): raise ValueError('Invalid stage.') self.data.setdefault('hooks', {}).setdefault(stage, []).append(hook) - def make(self, end='bit', start='prj'): + def make(self, last='bit', first='prj'): """Run the underlying tool. - :param end: last task - :type end: str, optional - :param start: first task - :type start: str, optional + :param last: last step + :type last: str, optional + :param first: first step + :type first: str, optional - .. note:: Valid values are ``cfg``, ``syn``, ``imp`` and ``bit``. + .. note:: valid steps are ``cfg``, ``syn``, ``imp`` and ``bit``. """ steps = ['cfg', 'syn', 'par', 'bit'] - if end not in steps or start not in steps: - raise ValueError('Invalid steps.') - _ = self - raise NotImplementedError('Method is not implemented yet.') - - def prog(self, position=1, bitstream=None): + if last not in steps: + raise ValueError('Invalid last step.') + if first not in steps: + raise ValueError('Invalid first step.') + if steps.index(first) > steps.index(last): + raise ValueError('Invalid steps combination.') + if not which(self.tool['program']): + raise RuntimeError(f'{self.tool["program"]} not found.') + self._run(self.tool['command']) + + def prog(self, bitstream=None, position=1): """Program the FPGA - :param position: position of the device in the JTAG chain - :type position: str, optional :param bitstream: bitstream to be programmed :type bitstream: str, optional + :param position: position of the device in the JTAG chain + :type position: str, optional """ - raise NotImplementedError('Method is not implemented yet.') + if position not in range(1, 9): + raise ValueError('Invalid position.') + _ = bitstream + if not which(self.tool['program']): + raise RuntimeError(f'{self.tool["program"]} not found.') + self._run(self.tool['command']) def _run(self, command): self.logger.info('Running the underlying tool (%s)', datetime.now()) From b1c26827eb18119e15019d3922127643dfad6cdc Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 5 Jun 2024 20:30:37 -0300 Subject: [PATCH 070/248] Removed unused things --- pyfpga/ise.py | 64 ++++++------------------------------- pyfpga/libero.py | 20 +++++------- pyfpga/openflow.py | 24 ++++++-------- pyfpga/quartus.py | 27 ++++++---------- pyfpga/tool.py | 78 ++-------------------------------------------- pyfpga/vivado.py | 62 +++++------------------------------- 6 files changed, 45 insertions(+), 230 deletions(-) diff --git a/pyfpga/ise.py b/pyfpga/ise.py index 329e6e48..17fb4706 100644 --- a/pyfpga/ise.py +++ b/pyfpga/ise.py @@ -78,48 +78,17 @@ class Ise(Project): 'command': 'xtclsh ise.tcl', } -# _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'] + tool = { + 'def-part': 'xc7k160t-3-fbg484', + 'proj-ext': 'xise', + 'make-app': 'xtclsh', + 'make-opt': 'ise.tcl', + 'prog-app': 'impact', + 'prog-opt': '-batch ise-prog.impact', + 'binaries': ['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: @@ -138,19 +107,6 @@ class Ise(Project): # 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] diff --git a/pyfpga/libero.py b/pyfpga/libero.py index 5513698e..966ac5d9 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -38,18 +38,14 @@ class Libero(Project): 'command': 'libero SCRIPT:libero.tcl', } -# _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' -# ] + tool = { + 'def-part': 'mpf100t-1-fcg484', + 'proj-ext': 'prjx', + 'make-app': 'libero', + 'make-opt': 'SCRIPT:libero.tcl', + 'prog-app': '', + 'prog-opt': '' + } # def set_part(self, part): # try: diff --git a/pyfpga/openflow.py b/pyfpga/openflow.py index b010ae40..f8a1150b 100644 --- a/pyfpga/openflow.py +++ b/pyfpga/openflow.py @@ -22,21 +22,15 @@ class Openflow(Project): 'command': 'bash openflow.sh', } -# _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' -# ] + tool = { + 'def-part': 'hx8k-ct256', + 'proj-ext': '', + 'make-app': 'docker', + 'make-cmd': 'bash openflow.sh', + 'prog-app': 'docker', + 'prog-cmd': 'bash openprog.sh', + 'binaries': ['bit'] + } # def __init__(self, project, frontend='yosys', backend='nextpnr'): # # The valid frontends are be ghdl and yosys diff --git a/pyfpga/quartus.py b/pyfpga/quartus.py index 9c6c861e..064af33b 100644 --- a/pyfpga/quartus.py +++ b/pyfpga/quartus.py @@ -22,24 +22,15 @@ class Quartus(Project): 'command': 'quartus_sh --script quartus.tcl', } -# _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' -# ] + tool = { + 'def-part': '10cl120zf780i8g', + 'proj-ext': 'qpf', + 'make-app': 'quartus_sh', + 'make-opt': '--script quartus.tcl', + 'prog-app': 'quartus_pgm', + 'prog-opt': '-c %s --mode jtag -o "p;%s@%s"', + 'binaries': ['sof', 'pof'] + } # def transfer(self, devtype, position, part, width, capture): # super().transfer(devtype, position, part, width, capture) diff --git a/pyfpga/tool.py b/pyfpga/tool.py index 19a756d4..30309ad0 100644 --- a/pyfpga/tool.py +++ b/pyfpga/tool.py @@ -4,17 +4,10 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -"""fpga.tool - +""" Defines the interface to be inherited to support a tool. """ - -FILETYPES = ['verilog', 'vhdl', 'constraint', 'design'] -MEMWIDTHS = [1, 2, 4, 8, 16, 32] -PHASES = ['prefile', 'project', 'preflow', 'postsyn', 'postpar', 'postbit'] -TASKS = ['prj', 'syn', 'par', 'bit'] - # def tcl_path(path): # """Returns a Tcl suitable path.""" # return path.replace(os.path.sep, "/") @@ -23,64 +16,7 @@ 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': [], -# 'postpar': [], -# '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] + """Tool interface.""" # def _create_gen_script(self, tasks): # """Create the script for generate execution.""" @@ -167,13 +103,3 @@ class Tool: # 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/pyfpga/vivado.py b/pyfpga/vivado.py index 46008024..c5b36cdb 100644 --- a/pyfpga/vivado.py +++ b/pyfpga/vivado.py @@ -34,59 +34,11 @@ class Vivado(Project): """Class to support Vivado.""" tool = { - 'program': 'vivado', - 'command': 'vivado -mode batch -notrace -quiet -source vivado.tcl', + 'def-part': 'xc7k160t-3-fbg484', + 'proj-ext': 'xpr', + 'make-app': 'vivado', + 'make-opt': '-mode batch -notrace -quiet -source vivado.tcl', + 'prog-app': 'vivado', + 'prog-opt': '-mode batch -notrace -quiet -source vivado-prog.tcl', + 'binaries': ['bit'] } - -# _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) From 6abcbd5d3a76cc2093f82388be3b19c06863d8fb Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 5 Jun 2024 21:21:09 -0300 Subject: [PATCH 071/248] Moved things from Python scripts to templates --- pyfpga/ise.py | 55 ------------------------- pyfpga/libero.py | 15 ------- pyfpga/templates/ise-prog.jinja | 60 ++++++++++++++++++++++++++++ pyfpga/templates/ise.jinja | 1 - pyfpga/templates/libero-prog.jinja | 20 ++++++++++ pyfpga/templates/libero.jinja | 1 - pyfpga/templates/openflow-prog.jinja | 1 - pyfpga/templates/openflow.jinja | 2 - pyfpga/templates/quartus-prog.jinja | 7 ++++ pyfpga/templates/quartus.jinja | 1 - pyfpga/templates/vivado-prog.jinja | 13 ++++++ pyfpga/templates/vivado.jinja | 1 - pyfpga/vivado.py | 17 -------- 13 files changed, 100 insertions(+), 94 deletions(-) create mode 100644 pyfpga/templates/ise-prog.jinja create mode 100644 pyfpga/templates/libero-prog.jinja create mode 100644 pyfpga/templates/quartus-prog.jinja create mode 100644 pyfpga/templates/vivado-prog.jinja diff --git a/pyfpga/ise.py b/pyfpga/ise.py index 17fb4706..cd39634e 100644 --- a/pyfpga/ise.py +++ b/pyfpga/ise.py @@ -12,61 +12,6 @@ from pyfpga.project import Project -_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 -""" -} - # pylint: disable=too-few-public-methods diff --git a/pyfpga/libero.py b/pyfpga/libero.py index 966ac5d9..7061f040 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -12,21 +12,6 @@ from pyfpga.project import Project -_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}" - # pylint: disable=too-few-public-methods diff --git a/pyfpga/templates/ise-prog.jinja b/pyfpga/templates/ise-prog.jinja new file mode 100644 index 00000000..4c18a072 --- /dev/null +++ b/pyfpga/templates/ise-prog.jinja @@ -0,0 +1,60 @@ +# +# Copyright (C) 2015-2024 Rodrigo A. Melo +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +_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 +""" +} diff --git a/pyfpga/templates/ise.jinja b/pyfpga/templates/ise.jinja index 08678558..c70efad6 100644 --- a/pyfpga/templates/ise.jinja +++ b/pyfpga/templates/ise.jinja @@ -1,5 +1,4 @@ # -# PyFPGA # Copyright (C) 2015-2024 Rodrigo A. Melo # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/pyfpga/templates/libero-prog.jinja b/pyfpga/templates/libero-prog.jinja new file mode 100644 index 00000000..ed9effe2 --- /dev/null +++ b/pyfpga/templates/libero-prog.jinja @@ -0,0 +1,20 @@ +# +# Copyright (C) 2015-2024 Rodrigo A. Melo +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +_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}" diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index 35ec3f52..1b230c2c 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -1,5 +1,4 @@ # -# PyFPGA # Copyright (C) 2015-2024 Rodrigo A. Melo # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/pyfpga/templates/openflow-prog.jinja b/pyfpga/templates/openflow-prog.jinja index 3be40f53..027eb388 100644 --- a/pyfpga/templates/openflow-prog.jinja +++ b/pyfpga/templates/openflow-prog.jinja @@ -1,5 +1,4 @@ # -# PyFPGA # Copyright (C) 2020-2024 Rodrigo A. Melo # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja index f50b0786..d3a0a703 100644 --- a/pyfpga/templates/openflow.jinja +++ b/pyfpga/templates/openflow.jinja @@ -1,6 +1,4 @@ -#!/bin/bash # -# PyFPGA # Copyright (C) 2020-2024 Rodrigo A. Melo # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/pyfpga/templates/quartus-prog.jinja b/pyfpga/templates/quartus-prog.jinja new file mode 100644 index 00000000..45ee765b --- /dev/null +++ b/pyfpga/templates/quartus-prog.jinja @@ -0,0 +1,7 @@ +# +# Copyright (C) 2015-2024 Rodrigo A. Melo +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +{{ COMMAND }} -c %s --mode jtag -o "p;{{ BITSTREAM }}@{{ POSITION }}" diff --git a/pyfpga/templates/quartus.jinja b/pyfpga/templates/quartus.jinja index 76c29fc5..cf660a8d 100644 --- a/pyfpga/templates/quartus.jinja +++ b/pyfpga/templates/quartus.jinja @@ -1,5 +1,4 @@ # -# PyFPGA # Copyright (C) 2015-2024 Rodrigo A. Melo # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/pyfpga/templates/vivado-prog.jinja b/pyfpga/templates/vivado-prog.jinja new file mode 100644 index 00000000..7b148b22 --- /dev/null +++ b/pyfpga/templates/vivado-prog.jinja @@ -0,0 +1,13 @@ +# +# Copyright (C) 2015-2024 Rodrigo A. Melo +# +# 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 index 16bed243..5c4e6049 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -1,5 +1,4 @@ # -# PyFPGA # Copyright (C) 2015-2024 Rodrigo A. Melo # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/pyfpga/vivado.py b/pyfpga/vivado.py index c5b36cdb..15dfa008 100644 --- a/pyfpga/vivado.py +++ b/pyfpga/vivado.py @@ -10,23 +10,6 @@ from pyfpga.project import Project -_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] -""" -} - # pylint: disable=too-few-public-methods From 713bbc75ecfd5589ec2fc1f6a01142ff1e653cfe Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 6 Jun 2024 23:18:17 -0300 Subject: [PATCH 072/248] Added a method to create files based on templates --- pyfpga/project.py | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index 5c054dd3..a6193683 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -17,6 +17,7 @@ from pathlib import Path from shutil import which from time import time +from jinja2 import Environment, FileSystemLoader class Project: @@ -37,7 +38,6 @@ def __init__(self, name=None, odir='results'): self.data = {} self.name = name self.odir = odir - # self.odir.mkdir(parents=True, exist_ok=True) # logging config self.logger = logging.getLogger(self.__class__.__name__) self.logger.setLevel(logging.INFO) @@ -210,9 +210,10 @@ def make(self, last='bit', first='prj'): raise ValueError('Invalid first step.') if steps.index(first) > steps.index(last): raise ValueError('Invalid steps combination.') - if not which(self.tool['program']): - raise RuntimeError(f'{self.tool["program"]} not found.') - self._run(self.tool['command']) + self._make_prepare() + if not which(self.tool['make-app']): + raise RuntimeError(f'{self.tool["make-app"]} not found.') + self._run(self.tool['make-cmd']) def prog(self, bitstream=None, position=1): """Program the FPGA @@ -225,9 +226,28 @@ def prog(self, bitstream=None, position=1): if position not in range(1, 9): raise ValueError('Invalid position.') _ = bitstream - if not which(self.tool['program']): - raise RuntimeError(f'{self.tool["program"]} not found.') - self._run(self.tool['command']) + self._prog_prepare() + if not which(self.tool['prog-app']): + raise RuntimeError(f'{self.tool["prog-app"]} not found.') + self._run(self.tool['prog-cmd']) + + def _make_prepare(self): + raise NotImplementedError('Tool-dependent') + + def _prog_prepare(self): + raise NotImplementedError('Tool-dependent') + + def _create_file(self, basename, extension, context): + 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(context) + 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): self.logger.info('Running the underlying tool (%s)', datetime.now()) @@ -237,14 +257,14 @@ def _run(self, command): start = time.time() try: os.chdir(new_dir) - with open('run.log', 'w', encoding='utf-8') as logfile: + with open('run.log', 'w', encoding='utf-8') as file: subprocess.run( command, shell=True, check=True, text=True, - stdout=logfile, stderr=subprocess.STDOUT + stdout=file, stderr=subprocess.STDOUT ) except subprocess.CalledProcessError: - with open('run.log', 'r', encoding='utf-8') as logfile: - lines = logfile.readlines() + with open('run.log', 'r', encoding='utf-8') as file: + lines = file.readlines() last_lines = lines[-10:] if len(lines) >= 10 else lines for line in last_lines: self.logger.error(line.strip()) From e506e2fb858186d7e54ac23009f486797885c6fc Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 6 Jun 2024 23:18:40 -0300 Subject: [PATCH 073/248] Tool-specific data reorganized --- pyfpga/ise.py | 27 +++++++++++---------------- pyfpga/libero.py | 26 +++++++++++--------------- pyfpga/openflow.py | 28 +++++++++++----------------- pyfpga/quartus.py | 31 ++++++++++++++----------------- pyfpga/vivado.py | 29 +++++++++++++++++------------ 5 files changed, 64 insertions(+), 77 deletions(-) diff --git a/pyfpga/ise.py b/pyfpga/ise.py index cd39634e..61fee4be 100644 --- a/pyfpga/ise.py +++ b/pyfpga/ise.py @@ -12,26 +12,22 @@ from pyfpga.project import Project -# pylint: disable=too-few-public-methods - class Ise(Project): """Class to support ISE projects.""" - tool = { - 'program': 'xtclsh', - 'command': 'xtclsh ise.tcl', - } + def __init__(self, name='ise', odir='results'): + super().__init__(name=name, odir=odir) + self.set_part('xc7k160t-3-fbg484') + + def _make_prepare(self): + self.tool['make-app'] = 'xtclsh' + self.tool['make-cmd'] = 'xtclsh ise.tcl' - tool = { - 'def-part': 'xc7k160t-3-fbg484', - 'proj-ext': 'xise', - 'make-app': 'xtclsh', - 'make-opt': 'ise.tcl', - 'prog-app': 'impact', - 'prog-opt': '-batch ise-prog.impact', - 'binaries': ['bit'] - } + def _prog_prepare(self): + # binaries = ['bit'] + self.tool['prog-app'] = 'impact' + self.tool['prog-cmd'] = 'impact -batch impact-prog' # _DEVTYPES = ['fpga', 'spi', 'bpi', 'detect', 'unlock'] @@ -64,7 +60,6 @@ class Ise(Project): # 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() diff --git a/pyfpga/libero.py b/pyfpga/libero.py index 7061f040..1c842fb9 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -12,25 +12,22 @@ from pyfpga.project import Project -# pylint: disable=too-few-public-methods - class Libero(Project): """Class to support Libero.""" - tool = { - 'program': 'libero', - 'command': 'libero SCRIPT:libero.tcl', - } + def __init__(self, name='libero', odir='results'): + super().__init__(name=name, odir=odir) + self.set_part('mpf100t-1-fcg484') + + def _make_prepare(self): + self.tool['make-app'] = 'libero' + self.tool['make-cmd'] = 'libero SCRIPT:libero.tcl' - tool = { - 'def-part': 'mpf100t-1-fcg484', - 'proj-ext': 'prjx', - 'make-app': 'libero', - 'make-opt': 'SCRIPT:libero.tcl', - 'prog-app': '', - 'prog-opt': '' - } + def _prog_prepare(self): + # binaries = ['bit'] + self.tool['prog-app'] = '' + self.tool['prog-cmd'] = '' # def set_part(self, part): # try: @@ -55,7 +52,6 @@ class Libero(Project): # 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() diff --git a/pyfpga/openflow.py b/pyfpga/openflow.py index f8a1150b..7ecbb9cd 100644 --- a/pyfpga/openflow.py +++ b/pyfpga/openflow.py @@ -8,29 +8,24 @@ Implements support for an Open Source development flow. """ -# import os from pyfpga.project import Project -# pylint: disable=too-few-public-methods - class Openflow(Project): """Class to support Openflow.""" - tool = { - 'program': 'docker', - 'command': 'bash openflow.sh', - } + def __init__(self, name='openflow', odir='results'): + super().__init__(name=name, odir=odir) + self.set_part('hx8k-ct256') + + def _make_prepare(self): + self.tool['make-app'] = 'docker' + self.tool['make-cmd'] = 'bash openflow.sh' - tool = { - 'def-part': 'hx8k-ct256', - 'proj-ext': '', - 'make-app': 'docker', - 'make-cmd': 'bash openflow.sh', - 'prog-app': 'docker', - 'prog-cmd': 'bash openprog.sh', - 'binaries': ['bit'] - } + def _prog_prepare(self): + # binaries = ['bit'] + self.tool['prog-app'] = 'docker' + self.tool['prog-cmd'] = 'bash openflow-prog.sh' # def __init__(self, project, frontend='yosys', backend='nextpnr'): # # The valid frontends are be ghdl and yosys @@ -178,7 +173,6 @@ class Openflow(Project): # 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() diff --git a/pyfpga/quartus.py b/pyfpga/quartus.py index 064af33b..55dd6434 100644 --- a/pyfpga/quartus.py +++ b/pyfpga/quartus.py @@ -9,28 +9,25 @@ """ # import re -# import subprocess from pyfpga.project import Project class Quartus(Project): - """Class to support Quartus.""" - - tool = { - 'program': 'quartus_sh', - 'command': 'quartus_sh --script quartus.tcl', - } - - tool = { - 'def-part': '10cl120zf780i8g', - 'proj-ext': 'qpf', - 'make-app': 'quartus_sh', - 'make-opt': '--script quartus.tcl', - 'prog-app': 'quartus_pgm', - 'prog-opt': '-c %s --mode jtag -o "p;%s@%s"', - 'binaries': ['sof', 'pof'] - } + """Class to support Quartus projects.""" + + def __init__(self, name='quartus', odir='results'): + super().__init__(name=name, odir=odir) + self.set_part('10cl120zf780i8g') + + def _make_prepare(self): + self.tool['make-app'] = 'quartus_sh' + self.tool['make-cmd'] = 'quartus_sh --script quartus.tcl' + + def _prog_prepare(self): + # binaries = ['sof', 'pof'] + self.tool['prog-app'] = 'quartus_pgm' + self.tool['prog-cmd'] = 'bash quartus-prog.sh' # def transfer(self, devtype, position, part, width, capture): # super().transfer(devtype, position, part, width, capture) diff --git a/pyfpga/vivado.py b/pyfpga/vivado.py index 15dfa008..4b53f7ca 100644 --- a/pyfpga/vivado.py +++ b/pyfpga/vivado.py @@ -10,18 +10,23 @@ from pyfpga.project import Project -# pylint: disable=too-few-public-methods - class Vivado(Project): - """Class to support Vivado.""" + """Class to support Vivado projects.""" + + def __init__(self, name='vivado', odir='results'): + super().__init__(name=name, odir=odir) + self.set_part('xc7k160t-3-fbg484') + + def _make_prepare(self): + self.tool['make-app'] = 'vivado' + self.tool['make-cmd'] = ( + 'vivado -mode batch -notrace -quiet -source vivado.tcl' + ) - tool = { - 'def-part': 'xc7k160t-3-fbg484', - 'proj-ext': 'xpr', - 'make-app': 'vivado', - 'make-opt': '-mode batch -notrace -quiet -source vivado.tcl', - 'prog-app': 'vivado', - 'prog-opt': '-mode batch -notrace -quiet -source vivado-prog.tcl', - 'binaries': ['bit'] - } + def _prog_prepare(self): + # binaries = ['bit'] + self.tool['prog-app'] = 'vivado' + self.tool['prog-cmd'] = ( + 'vivado -mode batch -notrace -quiet -source vivado-prog.tcl' + ) From bebd9e892e3011a468342a9ff7157f63f238f795 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 7 Jun 2024 00:36:30 -0300 Subject: [PATCH 074/248] Ignored results --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 From f496a2a0bb9a86be5d64c82d61734115430144ad Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 7 Jun 2024 00:37:08 -0300 Subject: [PATCH 075/248] Removed unused imports --- tests/test_data.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/test_data.py b/tests/test_data.py index 4c363de5..0994d9db 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -1,6 +1,3 @@ -import os -import pytest - from pathlib import Path from pyfpga.project import Project @@ -57,7 +54,7 @@ } -def test_names(): +def test_data(): prj = Project() prj.set_part('PARTNAME') prj.set_top('TOPNAME') From cf81ebfdf0d81a057b771da2f53039635d28bcd8 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 10 Jun 2024 20:25:11 -0300 Subject: [PATCH 076/248] Modified how to deal with steps; started the Vivado implementation --- pyfpga/ise.py | 2 +- pyfpga/libero.py | 2 +- pyfpga/openflow.py | 2 +- pyfpga/project.py | 15 ++++--- pyfpga/quartus.py | 2 +- pyfpga/templates/vivado.jinja | 73 +++++++++++++++-------------------- pyfpga/vivado.py | 22 ++++++++--- tests/test_part.py | 44 ++++++++++----------- tests/test_vivado.py | 22 +++++++++++ 9 files changed, 104 insertions(+), 80 deletions(-) create mode 100644 tests/test_vivado.py diff --git a/pyfpga/ise.py b/pyfpga/ise.py index 61fee4be..72e577bc 100644 --- a/pyfpga/ise.py +++ b/pyfpga/ise.py @@ -20,7 +20,7 @@ def __init__(self, name='ise', odir='results'): super().__init__(name=name, odir=odir) self.set_part('xc7k160t-3-fbg484') - def _make_prepare(self): + def _make_prepare(self, steps): self.tool['make-app'] = 'xtclsh' self.tool['make-cmd'] = 'xtclsh ise.tcl' diff --git a/pyfpga/libero.py b/pyfpga/libero.py index 1c842fb9..35d4d89f 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -20,7 +20,7 @@ def __init__(self, name='libero', odir='results'): super().__init__(name=name, odir=odir) self.set_part('mpf100t-1-fcg484') - def _make_prepare(self): + def _make_prepare(self, steps): self.tool['make-app'] = 'libero' self.tool['make-cmd'] = 'libero SCRIPT:libero.tcl' diff --git a/pyfpga/openflow.py b/pyfpga/openflow.py index 7ecbb9cd..cb756727 100644 --- a/pyfpga/openflow.py +++ b/pyfpga/openflow.py @@ -18,7 +18,7 @@ def __init__(self, name='openflow', odir='results'): super().__init__(name=name, odir=odir) self.set_part('hx8k-ct256') - def _make_prepare(self): + def _make_prepare(self, steps): self.tool['make-app'] = 'docker' self.tool['make-cmd'] = 'bash openflow.sh' diff --git a/pyfpga/project.py b/pyfpga/project.py index a6193683..e71b550a 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -193,7 +193,7 @@ def add_hook(self, stage, hook): raise ValueError('Invalid stage.') self.data.setdefault('hooks', {}).setdefault(stage, []).append(hook) - def make(self, last='bit', first='prj'): + def make(self, last='bit', first='cfg'): """Run the underlying tool. :param last: last step @@ -208,9 +208,12 @@ def make(self, last='bit', first='prj'): raise ValueError('Invalid last step.') if first not in steps: raise ValueError('Invalid first step.') - if steps.index(first) > steps.index(last): + first_index = steps.index(first) + last_index = steps.index(last) + if first_index > last_index: raise ValueError('Invalid steps combination.') - self._make_prepare() + selected_steps = steps[first_index:last_index + 1] + self._make_prepare([step.upper() for step in selected_steps]) if not which(self.tool['make-app']): raise RuntimeError(f'{self.tool["make-app"]} not found.') self._run(self.tool['make-cmd']) @@ -231,7 +234,7 @@ def prog(self, bitstream=None, position=1): raise RuntimeError(f'{self.tool["prog-app"]} not found.') self._run(self.tool['prog-cmd']) - def _make_prepare(self): + def _make_prepare(self, steps): raise NotImplementedError('Tool-dependent') def _prog_prepare(self): @@ -254,7 +257,7 @@ def _run(self, command): run_error = 0 old_dir = Path.cwd() new_dir = Path(self.odir) - start = time.time() + start = time() try: os.chdir(new_dir) with open('run.log', 'w', encoding='utf-8') as file: @@ -271,7 +274,7 @@ def _run(self, command): run_error = 1 finally: os.chdir(old_dir) - end = time.time() + end = time() self.logger.info('Done (%s)', datetime.now()) elapsed = end - start self.logger.info( diff --git a/pyfpga/quartus.py b/pyfpga/quartus.py index 55dd6434..c635ffa5 100644 --- a/pyfpga/quartus.py +++ b/pyfpga/quartus.py @@ -20,7 +20,7 @@ def __init__(self, name='quartus', odir='results'): super().__init__(name=name, odir=odir) self.set_part('10cl120zf780i8g') - def _make_prepare(self): + def _make_prepare(self, steps): self.tool['make-app'] = 'quartus_sh' self.tool['make-cmd'] = 'quartus_sh --script quartus.tcl' diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index 5c4e6049..cfc6734b 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -6,43 +6,37 @@ set PROJECT {{ PROJECT }} set PART {{ PART }} -set FAMILY {{ FAMILY }} -set DEVICE {{ DEVICE }} -set PACKAGE {{ PACKAGE }} -set SPEED {{ SPEED }} set TOP {{ TOP }} -set PARAMS [list {{ PARAMS }}] - -proc fpga_file {FILE {LIBRARY "work"}} { - set message "adding the file '$FILE'" - if { $LIBRARY != "work" } { append message " (into the VHDL library '$LIBRARY')" } - regexp -nocase {\.(\w*)$} $FILE -> ext - if { $ext == "tcl" } { - source $FILE - return - } - if { $LIBRARY != "work" } { - add_files $FILE - set_property library $LIBRARY [get_files $FILE] - } else { - add_files $FILE - } -} - -proc fpga_include {PATH} { - lappend INCLUDED $PATH - # Verilog Included Files are NOT added - set_property "include_dirs" $INCLUDED [current_fileset] -} - -proc fpga_params {} { - if { [llength $PARAMS] == 0 } { return } - 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_file {FILE {LIBRARY "work"}} { +# set message "adding the file '$FILE'" +# if { $LIBRARY != "work" } { append message " (into the VHDL library '$LIBRARY')" } +# regexp -nocase {\.(\w*)$} $FILE -> ext +# if { $ext == "tcl" } { +# source $FILE +# return +# } +# if { $LIBRARY != "work" } { +# add_files $FILE +# set_property library $LIBRARY [get_files $FILE] +# } else { +# add_files $FILE +# } +# } + +# proc fpga_include {PATH} { +# lappend INCLUDED $PATH +# # Verilog Included Files are NOT added +# set_property "include_dirs" $INCLUDED [current_fileset] +# } + +# proc fpga_params {} { +# if { [llength $PARAMS] == 0 } { return } +# set assigns [list] +# foreach PARAM $PARAMS { lappend assigns [join $PARAM "="] } +# set obj [get_filesets sources_1] +# set_property "generic" "[join $assigns]" -objects $obj +# } #--[ Project configuration ]--------------------------------------------------- @@ -88,13 +82,11 @@ open_project $PROJECT {% if SYN %} {{ PRESYN }} -{% if PRESYNTH %} -set_property design_mode GateLvl [current_fileset] -{% else %} +# PRESYNTH +# set_property design_mode GateLvl [current_fileset] reset_run synth_1 launch_runs synth_1 wait_on_run synth_1 -{% endif %} {{ POSTSYN }} {% endif %} @@ -102,9 +94,6 @@ wait_on_run synth_1 {% if PAR %} {{ PREPAR }} -{% if not PRESYNTH %} -open_run synth_1 -{% endif %} reset_run impl_1 launch_runs impl_1 wait_on_run impl_1 diff --git a/pyfpga/vivado.py b/pyfpga/vivado.py index 4b53f7ca..7c7c88b8 100644 --- a/pyfpga/vivado.py +++ b/pyfpga/vivado.py @@ -14,15 +14,27 @@ class Vivado(Project): """Class to support Vivado projects.""" - def __init__(self, name='vivado', odir='results'): - super().__init__(name=name, odir=odir) - self.set_part('xc7k160t-3-fbg484') - - def _make_prepare(self): + def _make_prepare(self, steps): self.tool['make-app'] = 'vivado' self.tool['make-cmd'] = ( 'vivado -mode batch -notrace -quiet -source vivado.tcl' ) + context = { + 'PROJECT': self.name or 'vivado', + 'PART': self.data.get('part', 'xc7k160t-3-fbg484'), + 'TOP': self.data.get('top', 'top') + } + for step in steps: + context[step] = 1 + if 'hooks' in self.data: + for stage in self.data['hooks']: + context[stage.upper()] = '\n'.join(self.data['hooks'][stage]) + # FILES + # DEFINES + # INCLUDES + # PARAMS + # ARCH + self._create_file('vivado', 'tcl', context) def _prog_prepare(self): # binaries = ['bit'] diff --git a/tests/test_part.py b/tests/test_part.py index a1c2861a..5cee3e71 100644 --- a/tests/test_part.py +++ b/tests/test_part.py @@ -1,29 +1,27 @@ -import pytest +from pyfpga.project import Project -from fpga.project import Project +# def get_part(prj): +# return prj.get_configs()['part'].lower() -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_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" +# 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/tests/test_vivado.py b/tests/test_vivado.py new file mode 100644 index 00000000..1aa74916 --- /dev/null +++ b/tests/test_vivado.py @@ -0,0 +1,22 @@ +from pyfpga.vivado import Vivado + + +def test_vivado(): + prj = Vivado() + prj.add_hook('precfg', 'HOOK1') + prj.add_hook('precfg', 'HOOK1') + prj.add_hook('postcfg', 'HOOK2') + prj.add_hook('postcfg', 'HOOK2') + prj.add_hook('presyn', 'HOOK3') + prj.add_hook('presyn', 'HOOK3') + prj.add_hook('postsyn', 'HOOK4') + prj.add_hook('postsyn', 'HOOK4') + prj.add_hook('prepar', 'HOOK5') + prj.add_hook('prepar', 'HOOK5') + prj.add_hook('postpar', 'HOOK6') + prj.add_hook('postpar', 'HOOK6') + prj.add_hook('prebit', 'HOOK7') + prj.add_hook('prebit', 'HOOK7') + prj.add_hook('postbit', 'HOOK8') + prj.add_hook('postbit', 'HOOK8') + prj.make() From d02b81ed17f74a99b44e1c1cc1f02d0a95cee786 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 10 Jun 2024 22:25:03 -0300 Subject: [PATCH 077/248] Implemented use of arch, defines, includes and params --- pyfpga/templates/vivado.jinja | 34 ---------------------------------- pyfpga/vivado.py | 27 ++++++++++++++++++++++----- tests/test_vivado.py | 8 ++++++++ 3 files changed, 30 insertions(+), 39 deletions(-) diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index cfc6734b..d12942e2 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -8,38 +8,6 @@ set PROJECT {{ PROJECT }} set PART {{ PART }} set TOP {{ TOP }} -# proc fpga_file {FILE {LIBRARY "work"}} { -# set message "adding the file '$FILE'" -# if { $LIBRARY != "work" } { append message " (into the VHDL library '$LIBRARY')" } -# regexp -nocase {\.(\w*)$} $FILE -> ext -# if { $ext == "tcl" } { -# source $FILE -# return -# } -# if { $LIBRARY != "work" } { -# add_files $FILE -# set_property library $LIBRARY [get_files $FILE] -# } else { -# add_files $FILE -# } -# } - -# proc fpga_include {PATH} { -# lappend INCLUDED $PATH -# # Verilog Included Files are NOT added -# set_property "include_dirs" $INCLUDED [current_fileset] -# } - -# proc fpga_params {} { -# if { [llength $PARAMS] == 0 } { return } -# set assigns [list] -# foreach PARAM $PARAMS { lappend assigns [join $PARAM "="] } -# set obj [get_filesets sources_1] -# set_property "generic" "[join $assigns]" -objects $obj -# } - -#--[ Project configuration ]--------------------------------------------------- - {% if CFG %} create_project -force $PROJECT set_property source_mgmt_mode None [current_project] @@ -74,8 +42,6 @@ set_property top_arch {{ ARCH }} [current_fileset] close_project {% endif %} -#--[ Design flow ]------------------------------------------------------------- - {% if SYN or PAR or BIT %} open_project $PROJECT diff --git a/pyfpga/vivado.py b/pyfpga/vivado.py index 7c7c88b8..60a7fcf8 100644 --- a/pyfpga/vivado.py +++ b/pyfpga/vivado.py @@ -29,11 +29,28 @@ def _make_prepare(self, steps): if 'hooks' in self.data: for stage in self.data['hooks']: context[stage.upper()] = '\n'.join(self.data['hooks'][stage]) - # FILES - # DEFINES - # INCLUDES - # PARAMS - # ARCH + # if { $LIBRARY != "work" } { + # add_files $FILE + # set_property library $LIBRARY [get_files $FILE] + # } else { + # add_files $FILE + if 'arch' in self.data: + context['ARCH'] = self.data['arch'] + if 'defines' in self.data: + defines = [] + for key,value in self.data['defines'].items(): + defines.append(f'{key}={value}') + context['DEFINES'] = ' '.join(defines) + if 'includes' in self.data: + includes = [] + for include in self.data['includes']: + includes.append(str(include)) + context['INCLUDES'] = ' '.join(includes) + if 'params' in self.data: + params = [] + for key,value in self.data['params'].items(): + params.append(f'{key}={value}') + context['PARAMS'] = ' '.join(params) self._create_file('vivado', 'tcl', context) def _prog_prepare(self): diff --git a/tests/test_vivado.py b/tests/test_vivado.py index 1aa74916..9d6a2e34 100644 --- a/tests/test_vivado.py +++ b/tests/test_vivado.py @@ -3,6 +3,14 @@ def test_vivado(): prj = Vivado() + + prj.set_arch('ARCHNAME') + prj.add_include('fakedata/dir1') + prj.add_include('fakedata/dir2') + prj.add_param('PARAM1', 'VALUE1') + prj.add_param('PARAM2', 'VALUE2') + prj.add_define('DEFINE1', 'VALUE1') + prj.add_define('DEFINE2', 'VALUE2') prj.add_hook('precfg', 'HOOK1') prj.add_hook('precfg', 'HOOK1') prj.add_hook('postcfg', 'HOOK2') From 6824d99b46861f4d55d6d6e561a0ee806112568e Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 17 Jun 2024 19:32:37 -0300 Subject: [PATCH 078/248] Fixed linter issues --- pyfpga/vivado.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyfpga/vivado.py b/pyfpga/vivado.py index 60a7fcf8..597afceb 100644 --- a/pyfpga/vivado.py +++ b/pyfpga/vivado.py @@ -38,7 +38,7 @@ def _make_prepare(self, steps): context['ARCH'] = self.data['arch'] if 'defines' in self.data: defines = [] - for key,value in self.data['defines'].items(): + for key, value in self.data['defines'].items(): defines.append(f'{key}={value}') context['DEFINES'] = ' '.join(defines) if 'includes' in self.data: @@ -48,7 +48,7 @@ def _make_prepare(self, steps): context['INCLUDES'] = ' '.join(includes) if 'params' in self.data: params = [] - for key,value in self.data['params'].items(): + for key, value in self.data['params'].items(): params.append(f'{key}={value}') context['PARAMS'] = ' '.join(params) self._create_file('vivado', 'tcl', context) From b178c922bce66bf27cfa0891cde9bb14dfe0d74d Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 17 Jun 2024 19:33:01 -0300 Subject: [PATCH 079/248] Added add_fileset, to be implemented --- pyfpga/project.py | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index e71b550a..8fcd75ec 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -58,6 +58,21 @@ def set_part(self, name): self.logger.debug('Executing set_part') 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') + path = Path(path).resolve() + if not path.is_dir(): + raise NotADirectoryError(path) + self.data.setdefault('includes', []).append(path) + def _add_file(self, pathname, filetype=None, library=None, options=None): files = glob.glob(pathname) if len(files) == 0: @@ -119,21 +134,6 @@ def add_vlog(self, pathname, options=None): self.logger.debug('Executing add_vlog') self._add_file(pathname, filetype='vlog', options=options) - 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') - path = Path(path).resolve() - if not path.is_dir(): - raise NotADirectoryError(path) - self.data.setdefault('includes', []).append(path) - def add_param(self, name, value): """Add a Parameter/Generic Value. @@ -165,6 +165,18 @@ def set_arch(self, name): self.logger.debug('Executing set_arch') self.data['arch'] = name + 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') + if not os.path.exists(pathname): + raise FileNotFoundError(pathname) + raise NotImplementedError() + def set_top(self, name): """Set the name of the top level component. From f77f21420e6bd4343b7388dfb5c511cc4f9e5433 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 17 Jun 2024 21:57:57 -0300 Subject: [PATCH 080/248] Slightly modified the internal data structure --- docs/internals.rst | 14 +++++--- pyfpga/project.py | 66 +++++++++++++++++++------------------ tests/fakedata/cons/all.xdc | 0 tests/fakedata/cons/par.xdc | 0 tests/fakedata/cons/syn.xdc | 0 tests/test_data.py | 40 +++++++++++----------- 6 files changed, 64 insertions(+), 56 deletions(-) create mode 100644 tests/fakedata/cons/all.xdc create mode 100644 tests/fakedata/cons/par.xdc create mode 100644 tests/fakedata/cons/syn.xdc diff --git a/docs/internals.rst b/docs/internals.rst index 0ec8ded5..6bf036ea 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -40,12 +40,15 @@ Internal data structure 'part': 'PARTNAME', 'includes': ['DIR1', 'DIR2', 'DIR3'], 'files': { - 'file1': {'type': 'vhdl', 'options': 'OPT1', 'library': 'LIB1'}, - 'file2': {'type': 'vlog', 'options': 'OPT2', 'library': None}, - 'file3': {'type': 'slog', 'options': 'OPT3', 'library': None}, - 'file4': {'type': 'cons', 'options': 'OPT4', 'library': None} + 'FILE1': ['vhdl', 'LIB1'], + 'FILE2': ['vlog'], + 'FILE3': ['slog'] + }, + 'constraints': { + 'FILE1': ['syn', 'par'], + 'FILE2': ['syn'], + 'FILE3': ['par'] }, - 'top': 'TOPNAME', 'params': { 'PAR1': 'VAL1', 'PAR2': 'VAL2', @@ -57,6 +60,7 @@ Internal data structure 'DEF3': 'VAL3' }, 'arch': 'ARCHNAME', + 'top': 'TOPNAME', 'hooks': { 'precfg': ['CMD1', 'CMD2'], 'postcfg': ['CMD1', 'CMD2'], diff --git a/pyfpga/project.py b/pyfpga/project.py index 8fcd75ec..b563ea38 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -73,66 +73,68 @@ def add_include(self, path): raise NotADirectoryError(path) self.data.setdefault('includes', []).append(path) - def _add_file(self, pathname, filetype=None, library=None, options=None): - files = glob.glob(pathname) + def _add_file(self, pathname, filetype=None, library=None): + files = glob.glob(pathname, recursive=True) if len(files) == 0: raise FileNotFoundError(pathname) for file in files: path = Path(file).resolve() - self.data.setdefault('files', {})[path] = { - 'type': filetype, 'options': options, 'library': library - } - - def add_cons(self, pathname): - """Add constraint file/s. - - :param pathname: path to a constraint file (glob compliant) - :type pathname: str - :raises FileNotFoundError: when pathname is not found - """ - self.logger.debug('Executing add_cons') - self._add_file(pathname, filetype='cons', options=None) - - def add_slog(self, pathname, options=None): + attr = [] + if filetype: + attr.append(filetype) + if library: + attr.append(library) + 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 - :param options: for the underlying tool - :type options: str, optional :raises FileNotFoundError: when pathname is not found """ self.logger.debug('Executing add_slog') - self._add_file(pathname, filetype='slog', options=options) + self._add_file(pathname, 'slog') - def add_vhdl(self, pathname, library=None, options=None): + def add_vhdl(self, pathname, library=None): """Add VHDL file/s. :param pathname: path to a SV file (glob compliant) :type pathname: str :param library: VHDL library name :type library: str, optional - :param options: for the underlying tool - :type options: str, optional :raises FileNotFoundError: when pathname is not found """ self.logger.debug('Executing add_vhdl') - self._add_file( - pathname, filetype='vhdl', - library=library, options=options - ) + self._add_file(pathname, 'vhdl', library) - def add_vlog(self, pathname, options=None): + def add_vlog(self, pathname): """Add Verilog file/s. :param pathname: path to a SV file (glob compliant) :type pathname: str - :param options: for the underlying tool - :type options: str, optional :raises FileNotFoundError: when pathname is not found """ self.logger.debug('Executing add_vlog') - self._add_file(pathname, filetype='vlog', options=options) + self._add_file(pathname, 'vlog') + + def add_constraint(self, path, syn=True, par=True): + """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_constraint') + path = Path(path).resolve() + if not path.is_file(): + raise FileNotFoundError(path) + attr = [] + if syn: + attr.append('syn') + if par: + attr.append('par') + self.data.setdefault('constraints', {})[path] = attr def add_param(self, name, value): """Add a Parameter/Generic Value. @@ -213,7 +215,7 @@ def make(self, last='bit', first='cfg'): :param first: first step :type first: str, optional - .. note:: valid steps are ``cfg``, ``syn``, ``imp`` and ``bit``. + .. note:: valid steps are ``cfg``, ``syn``, ``par`` and ``bit``. """ steps = ['cfg', 'syn', 'par', 'bit'] if last not in steps: 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/test_data.py b/tests/test_data.py index 0994d9db..2b2324a1 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -10,24 +10,23 @@ Path('fakedata/dir3').resolve() ], 'files': { - Path('fakedata/dir1/slog1.sv').resolve(): - {'type': 'slog', 'options': None, 'library': None}, - Path('fakedata/dir2/slog2.sv').resolve(): - {'type': 'slog', 'options': None, 'library': None}, - Path('fakedata/dir3/slog3.sv').resolve(): - {'type': 'slog', 'options': None, 'library': None}, - Path('fakedata/dir1/vhdl1.vhdl').resolve(): - {'type': 'vhdl', 'options': None, 'library': None}, - Path('fakedata/dir2/vhdl2.vhdl').resolve(): - {'type': 'vhdl', 'options': None, 'library': None}, - Path('fakedata/dir3/vhdl3.vhdl').resolve(): - {'type': 'vhdl', 'options': None, 'library': None}, - Path('fakedata/dir1/vlog1.v').resolve(): - {'type': 'vlog', 'options': None, 'library': None}, - Path('fakedata/dir2/vlog2.v').resolve(): - {'type': 'vlog', 'options': None, 'library': None}, - Path('fakedata/dir3/vlog3.v').resolve(): - {'type': 'vlog', 'options': None, 'library': None} + Path('fakedata/vhdl0.vhdl').resolve(): ['vhdl', 'LIB'], + Path('fakedata/dir1/vhdl1.vhdl').resolve(): ['vhdl', 'LIB'], + Path('fakedata/dir2/vhdl2.vhdl').resolve(): ['vhdl', 'LIB'], + Path('fakedata/dir3/vhdl3.vhdl').resolve(): ['vhdl', 'LIB'], + Path('fakedata/vlog0.v').resolve(): ['vlog'], + Path('fakedata/dir1/vlog1.v').resolve(): ['vlog'], + Path('fakedata/dir2/vlog2.v').resolve(): ['vlog'], + Path('fakedata/dir3/vlog3.v').resolve(): ['vlog'], + Path('fakedata/slog0.sv').resolve(): ['slog'], + Path('fakedata/dir1/slog1.sv').resolve(): ['slog'], + Path('fakedata/dir2/slog2.sv').resolve(): ['slog'], + Path('fakedata/dir3/slog3.sv').resolve(): ['slog'] + }, + 'constraints': { + Path('fakedata/cons/all.xdc').resolve(): ['syn', 'par'], + Path('fakedata/cons/syn.xdc').resolve(): ['syn'], + Path('fakedata/cons/par.xdc').resolve(): ['par'] }, 'top': 'TOPNAME', 'params': { @@ -63,8 +62,11 @@ def test_data(): prj.add_include('fakedata/dir2') prj.add_include('fakedata/dir3') prj.add_slog('fakedata/**/*.sv') - prj.add_vhdl('fakedata/**/*.vhdl') + prj.add_vhdl('fakedata/**/*.vhdl', 'LIB') prj.add_vlog('fakedata/**/*.v') + prj.add_constraint('fakedata/cons/all.xdc') + prj.add_constraint('fakedata/cons/syn.xdc', True, False) + prj.add_constraint('fakedata/cons/par.xdc', False, True) prj.add_param('PAR1', 'VAL1') prj.add_param('PAR2', 'VAL2') prj.add_param('PAR3', 'VAL3') From b17a2004318ee142590896c8e2794bfa2cc2a670 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 20 Jun 2024 17:52:37 -0300 Subject: [PATCH 081/248] More small adjustments in the internal data structure --- docs/internals.rst | 14 ++++++------- pyfpga/project.py | 50 +++++++++++++++++++++++++++------------------- tests/test_data.py | 42 +++++++++++++++++++++----------------- 3 files changed, 61 insertions(+), 45 deletions(-) diff --git a/docs/internals.rst b/docs/internals.rst index 6bf036ea..910df99a 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -40,14 +40,15 @@ Internal data structure 'part': 'PARTNAME', 'includes': ['DIR1', 'DIR2', 'DIR3'], 'files': { - 'FILE1': ['vhdl', 'LIB1'], - 'FILE2': ['vlog'], - 'FILE3': ['slog'] + 'FILE1': {'hdl': 'vhdl', 'lib': 'LIB1'} + 'FILE2': {'hdl': 'vlog'}, + 'FILE3': {'hdl': 'slog'} }, + 'top': 'TOPNAME', 'constraints': { - 'FILE1': ['syn', 'par'], - 'FILE2': ['syn'], - 'FILE3': ['par'] + 'FILE1': 'all', + 'FILE2': 'syn', + 'FILE3': 'par' }, 'params': { 'PAR1': 'VAL1', @@ -60,7 +61,6 @@ Internal data structure 'DEF3': 'VAL3' }, 'arch': 'ARCHNAME', - 'top': 'TOPNAME', 'hooks': { 'precfg': ['CMD1', 'CMD2'], 'postcfg': ['CMD1', 'CMD2'], diff --git a/pyfpga/project.py b/pyfpga/project.py index b563ea38..d7758ad0 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -23,12 +23,10 @@ class Project: """Base class to manage an FPGA project. - :param name: project name (tool name by default) + :param name: project name (tool name when nothing specified) :type name: str, optional :param odir: output directory :type odir: str, optional - :raises ValueError: when an invalid value is specified - :raises RuntimeError: when the needed underlying tool is not available """ tool = {} @@ -73,17 +71,17 @@ def add_include(self, path): raise NotADirectoryError(path) self.data.setdefault('includes', []).append(path) - def _add_file(self, pathname, filetype=None, library=None): + 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() - attr = [] - if filetype: - attr.append(filetype) - if library: - attr.append(library) + attr = {} + if hdl: + attr['hdl'] = hdl + if lib: + attr['lib'] = lib self.data.setdefault('files', {})[path] = attr def add_slog(self, pathname): @@ -96,17 +94,17 @@ def add_slog(self, pathname): self.logger.debug('Executing add_slog') self._add_file(pathname, 'slog') - def add_vhdl(self, pathname, library=None): + def add_vhdl(self, pathname, lib=None): """Add VHDL file/s. :param pathname: path to a SV file (glob compliant) :type pathname: str - :param library: VHDL library name - :type library: str, optional + :param lib: VHDL library name + :type lib: str, optional :raises FileNotFoundError: when pathname is not found """ self.logger.debug('Executing add_vhdl') - self._add_file(pathname, 'vhdl', library) + self._add_file(pathname, 'vhdl', lib) def add_vlog(self, pathname): """Add Verilog file/s. @@ -118,23 +116,22 @@ def add_vlog(self, pathname): self.logger.debug('Executing add_vlog') self._add_file(pathname, 'vlog') - def add_constraint(self, path, syn=True, par=True): + def add_constraint(self, path, when='all'): """Add a constraint file. :param pathname: path of a file :type pathname: str + :param when: always ('all'), synthesis ('syn') or P&R ('par') + :type only: str, optional :raises FileNotFoundError: if path is not found """ self.logger.debug('Executing add_constraint') path = Path(path).resolve() if not path.is_file(): raise FileNotFoundError(path) - attr = [] - if syn: - attr.append('syn') - if par: - attr.append('par') - self.data.setdefault('constraints', {})[path] = attr + if when not in ['all', 'syn', 'par']: + raise ValueError('Invalid only.') + self.data.setdefault('constraints', {})[path] = when def add_param(self, name, value): """Add a Parameter/Generic Value. @@ -199,6 +196,7 @@ def add_hook(self, stage, hook): :type hook: str :raises ValueError: when stage is invalid """ + self.logger.debug('Executing add_hook') stages = [ 'precfg', 'postcfg', 'presyn', 'postsyn', 'prepar', 'postpar', 'prebit', 'postbit' @@ -214,9 +212,18 @@ def make(self, last='bit', first='cfg'): :type last: str, optional :param first: first step :type first: str, optional + :raises ValueError: for missing or wrong values + :raises RuntimeError: when the needed underlying tool is not found .. note:: valid steps are ``cfg``, ``syn``, ``par`` and ``bit``. """ + self.logger.debug('Executing make') + if 'part' not in self.data: + self.logger.info('No part specified, using a default value') + if 'top' not in self.data: + self.logger.warning('No top specified') + if 'files' not in self.data: + self.logger.warning('No files specified') steps = ['cfg', 'syn', 'par', 'bit'] if last not in steps: raise ValueError('Invalid last step.') @@ -239,7 +246,10 @@ def prog(self, bitstream=None, position=1): :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 RuntimeError: when the needed underlying tool is not found """ + self.logger.debug('Executing prog') if position not in range(1, 9): raise ValueError('Invalid position.') _ = bitstream diff --git a/tests/test_data.py b/tests/test_data.py index 2b2324a1..c4a355c0 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -10,25 +10,31 @@ Path('fakedata/dir3').resolve() ], 'files': { - Path('fakedata/vhdl0.vhdl').resolve(): ['vhdl', 'LIB'], - Path('fakedata/dir1/vhdl1.vhdl').resolve(): ['vhdl', 'LIB'], - Path('fakedata/dir2/vhdl2.vhdl').resolve(): ['vhdl', 'LIB'], - Path('fakedata/dir3/vhdl3.vhdl').resolve(): ['vhdl', 'LIB'], - Path('fakedata/vlog0.v').resolve(): ['vlog'], - Path('fakedata/dir1/vlog1.v').resolve(): ['vlog'], - Path('fakedata/dir2/vlog2.v').resolve(): ['vlog'], - Path('fakedata/dir3/vlog3.v').resolve(): ['vlog'], - Path('fakedata/slog0.sv').resolve(): ['slog'], - Path('fakedata/dir1/slog1.sv').resolve(): ['slog'], - Path('fakedata/dir2/slog2.sv').resolve(): ['slog'], - Path('fakedata/dir3/slog3.sv').resolve(): ['slog'] + Path('fakedata/vhdl0.vhdl').resolve(): {'hdl': 'vhdl', 'lib': 'LIB'}, + Path('fakedata/dir1/vhdl1.vhdl').resolve(): { + 'hdl': 'vhdl', 'lib': 'LIB' + }, + Path('fakedata/dir2/vhdl2.vhdl').resolve(): { + 'hdl': 'vhdl', 'lib': 'LIB' + }, + Path('fakedata/dir3/vhdl3.vhdl').resolve(): { + 'hdl': 'vhdl', 'lib': 'LIB' + }, + Path('fakedata/vlog0.v').resolve(): {'hdl': 'vlog'}, + Path('fakedata/dir1/vlog1.v').resolve(): {'hdl': 'vlog'}, + Path('fakedata/dir2/vlog2.v').resolve(): {'hdl': 'vlog'}, + Path('fakedata/dir3/vlog3.v').resolve(): {'hdl': 'vlog'}, + Path('fakedata/slog0.sv').resolve(): {'hdl': 'slog'}, + Path('fakedata/dir1/slog1.sv').resolve(): {'hdl': 'slog'}, + Path('fakedata/dir2/slog2.sv').resolve(): {'hdl': 'slog'}, + Path('fakedata/dir3/slog3.sv').resolve(): {'hdl': 'slog'} }, + 'top': 'TOPNAME', 'constraints': { - Path('fakedata/cons/all.xdc').resolve(): ['syn', 'par'], - Path('fakedata/cons/syn.xdc').resolve(): ['syn'], - Path('fakedata/cons/par.xdc').resolve(): ['par'] + Path('fakedata/cons/all.xdc').resolve(): 'all', + Path('fakedata/cons/syn.xdc').resolve(): 'syn', + Path('fakedata/cons/par.xdc').resolve(): 'par' }, - 'top': 'TOPNAME', 'params': { 'PAR1': 'VAL1', 'PAR2': 'VAL2', @@ -65,8 +71,8 @@ def test_data(): prj.add_vhdl('fakedata/**/*.vhdl', 'LIB') prj.add_vlog('fakedata/**/*.v') prj.add_constraint('fakedata/cons/all.xdc') - prj.add_constraint('fakedata/cons/syn.xdc', True, False) - prj.add_constraint('fakedata/cons/par.xdc', False, True) + prj.add_constraint('fakedata/cons/syn.xdc', 'syn') + prj.add_constraint('fakedata/cons/par.xdc', 'par') prj.add_param('PAR1', 'VAL1') prj.add_param('PAR2', 'VAL2') prj.add_param('PAR3', 'VAL3') From 2c087d6a74a71252f39a3984801624a586c4d341 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 20 Jun 2024 17:54:15 -0300 Subject: [PATCH 082/248] Finished implementation of _make_prepare for Vivado --- pyfpga/templates/vivado.jinja | 33 ++++++++++---------- pyfpga/vivado.py | 58 +++++++++++++++++++++++++---------- tests/test_vivado.py | 41 ++++++++++++------------- 3 files changed, 78 insertions(+), 54 deletions(-) diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index d12942e2..05b1f0fa 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -4,37 +4,36 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -set PROJECT {{ PROJECT }} -set PART {{ PART }} -set TOP {{ TOP }} +#--[ Project configuration ]-------------------------------------------------- {% if CFG %} -create_project -force $PROJECT -set_property source_mgmt_mode None [current_project] +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] +set_property PART {{ PART }} [current_project] {{ PRECFG }} {{ FILES }} -set_property top $TOP [current_fileset] +{% if TOP %} +set_property TOP {{ TOP }} [current_fileset] +{% endif %} {% if DEFINES %} -set_property verilog_define { {{ DEFINES}} } [current_fileset] +set_property VERILOG_DEFINE { {{ DEFINES}} } [current_fileset] {% endif %} {% if INCLUDES %} -set_property include_dirs { {{ INCLUDES}} } [current_fileset] +set_property INCLUDE_DIRS { {{ INCLUDES}} } [current_fileset] {% endif %} {% if PARAMS %} -set_property generic { {{ PARAMS }} } -objects [get_filesets sources_1] +set_property GENERIC { {{ PARAMS }} } -objects [get_filesets sources_1] {% endif %} {% if ARCH %} -set_property top_arch {{ ARCH }} [current_fileset] +set_property TOP_ARCH {{ ARCH }} [current_fileset] {% endif %} {{ POSTCFG }} @@ -42,14 +41,16 @@ set_property top_arch {{ ARCH }} [current_fileset] close_project {% endif %} +#--[ Design flow ]------------------------------------------------------------- + {% if SYN or PAR or BIT %} -open_project $PROJECT +open_project {{ PROJECT }} {% if SYN %} {{ PRESYN }} # PRESYNTH -# set_property design_mode GateLvl [current_fileset] +# set_property DESIGN_MODE GateLvl [current_fileset] reset_run synth_1 launch_runs synth_1 wait_on_run synth_1 @@ -71,8 +72,8 @@ wait_on_run impl_1 {{ PREBIT }} open_run impl_1 -write_bitstream -force $PROJECT -write_debug_probes -force -quiet $PROJECT.ltx +write_bitstream -force {{ PROJECT }} +write_debug_probes -force -quiet {{ PROJECT }}.ltx {{ POSTBIT }} {% endif %} diff --git a/pyfpga/vivado.py b/pyfpga/vivado.py index 597afceb..d776b52a 100644 --- a/pyfpga/vivado.py +++ b/pyfpga/vivado.py @@ -8,6 +8,9 @@ Implements support for Vivado. """ +# pylint: disable=too-many-locals +# pylint: disable=too-many-branches + from pyfpga.project import Project @@ -21,36 +24,57 @@ def _make_prepare(self, steps): ) context = { 'PROJECT': self.name or 'vivado', - 'PART': self.data.get('part', 'xc7k160t-3-fbg484'), - 'TOP': self.data.get('top', 'top') + 'PART': self.data.get('part', 'xc7k160t-3-fbg484') } for step in steps: context[step] = 1 - if 'hooks' in self.data: - for stage in self.data['hooks']: - context[stage.upper()] = '\n'.join(self.data['hooks'][stage]) - # if { $LIBRARY != "work" } { - # add_files $FILE - # set_property library $LIBRARY [get_files $FILE] - # } else { - # add_files $FILE - if 'arch' in self.data: - context['ARCH'] = self.data['arch'] - if 'defines' in self.data: - defines = [] - for key, value in self.data['defines'].items(): - defines.append(f'{key}={value}') - context['DEFINES'] = ' '.join(defines) if 'includes' in self.data: includes = [] for include in self.data['includes']: includes.append(str(include)) context['INCLUDES'] = ' '.join(includes) + files = [] + if 'files' in self.data: + for file in self.data['files']: + files.append(f'add_file {file}') + for file in self.data['files']: + if 'lib' in self.data['files'][file]: + lib = self.data['files'][file]['lib'] + files.append( + f'set_property library {lib} [get_files {file}]' + ) + if 'constraints' in self.data: + for file in self.data['constraints']: + files.append(f'add_file -fileset constrs_1 {file}') + for file in self.data['constraints']: + if self.data['constraints'][file] == 'syn': + prop = 'USED_IN_IMPLEMENTATION FALSE' + if self.data['constraints'][file] == 'syn': + prop = 'USED_IN_SYNTHESIS FALSE' + if self.data['constraints'][file] != 'all': + files.append(f'set_property {prop} [get_files {file}]') + first = next(iter(self.data['constraints'])) + prop = f'TARGET_CONSTRS_FILE {first}' + files.append(f'set_property {prop} [current_fileset -constrset]') + if files: + context['FILES'] = '\n'.join(files) + if 'top' in self.data: + context['TOP'] = self.data['top'] + if 'defines' in self.data: + defines = [] + for key, value in self.data['defines'].items(): + defines.append(f'{key}={value}') + context['DEFINES'] = ' '.join(defines) if 'params' in self.data: params = [] for key, value in self.data['params'].items(): params.append(f'{key}={value}') context['PARAMS'] = ' '.join(params) + if 'arch' in self.data: + context['ARCH'] = self.data['arch'] + if 'hooks' in self.data: + for stage in self.data['hooks']: + context[stage.upper()] = '\n'.join(self.data['hooks'][stage]) self._create_file('vivado', 'tcl', context) def _prog_prepare(self): diff --git a/tests/test_vivado.py b/tests/test_vivado.py index 9d6a2e34..9afa353d 100644 --- a/tests/test_vivado.py +++ b/tests/test_vivado.py @@ -3,28 +3,27 @@ def test_vivado(): prj = Vivado() - + prj.set_part('PARTNAME') + prj.set_top('TOPNAME') prj.set_arch('ARCHNAME') prj.add_include('fakedata/dir1') prj.add_include('fakedata/dir2') - prj.add_param('PARAM1', 'VALUE1') - prj.add_param('PARAM2', 'VALUE2') - prj.add_define('DEFINE1', 'VALUE1') - prj.add_define('DEFINE2', 'VALUE2') - prj.add_hook('precfg', 'HOOK1') - prj.add_hook('precfg', 'HOOK1') - prj.add_hook('postcfg', 'HOOK2') - prj.add_hook('postcfg', 'HOOK2') - prj.add_hook('presyn', 'HOOK3') - prj.add_hook('presyn', 'HOOK3') - prj.add_hook('postsyn', 'HOOK4') - prj.add_hook('postsyn', 'HOOK4') - prj.add_hook('prepar', 'HOOK5') - prj.add_hook('prepar', 'HOOK5') - prj.add_hook('postpar', 'HOOK6') - prj.add_hook('postpar', 'HOOK6') - prj.add_hook('prebit', 'HOOK7') - prj.add_hook('prebit', 'HOOK7') - prj.add_hook('postbit', 'HOOK8') - prj.add_hook('postbit', 'HOOK8') + prj.add_slog('fakedata/**/*.sv') + prj.add_vhdl('fakedata/**/*.vhdl', 'LIB') + prj.add_vlog('fakedata/**/*.v') + prj.add_constraint('fakedata/cons/all.xdc') + prj.add_constraint('fakedata/cons/syn.xdc', 'syn') + prj.add_constraint('fakedata/cons/par.xdc', 'par') + prj.add_param('PAR1', 'VAL1') + prj.add_param('PAR2', 'VAL2') + prj.add_define('DEF1', 'VAL1') + prj.add_define('DEF2', 'VAL2') + prj.add_hook('precfg', 'PRECFG_HOOK') + prj.add_hook('postcfg', 'POSTCFG_HOOK') + prj.add_hook('presyn', 'PRESYN_HOOK') + prj.add_hook('postsyn', 'POSTSYN_HOOK') + prj.add_hook('prepar', 'PREPAR_HOOK') + prj.add_hook('postpar', 'POSTPAR_HOOK') + prj.add_hook('prebit', 'PREBIT_HOOK') + prj.add_hook('postbit', 'POSTBIT_HOOK') prj.make() From cf2011395d27146849a1ee392a74157d3fa79b35 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 20 Jun 2024 18:22:36 -0300 Subject: [PATCH 083/248] Moved submodule 'resources' to 'examples/resources' --- .gitmodules | 6 +++--- Makefile | 4 ++-- resources => examples/resources | 0 3 files changed, 5 insertions(+), 5 deletions(-) rename resources => examples/resources (100%) diff --git a/.gitmodules b/.gitmodules index 362bc8ae..cba0ef31 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "resources"] - path = resources - url = https://github.com/PyFPGA/resources +[submodule "examples/resources"] + path = examples/resources + url = git@github.com:PyFPGA/resources.git diff --git a/Makefile b/Makefile index 9c8032b2..f9eda4cb 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ clean: rm -fr build .pytest_cache submodule-init: - git submodule update --init + git submodule update --init --recursive submodule-update: - cd resources; git checkout main; git pull + cd examples/resources; git checkout main; git pull diff --git a/resources b/examples/resources similarity index 100% rename from resources rename to examples/resources From 0d6ad2acc173cca47374848a4d5b320cfaebb653 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 20 Jun 2024 18:36:11 -0300 Subject: [PATCH 084/248] Removed 'hdl', superseded by 'examples/resources' --- examples/resources | 2 +- hdl/blinking.v | 28 ------------------ hdl/blinking.vhdl | 33 --------------------- hdl/data/memory.dat | 64 ----------------------------------------- hdl/examples_pkg.vhdl | 17 ----------- hdl/fakes/generics.vhdl | 41 -------------------------- hdl/fakes/parameters.v | 51 -------------------------------- hdl/fakes/top.v | 10 ------- hdl/fakes/top.vhdl | 5 ---- hdl/headers1/freq.vh | 1 - hdl/headers2/secs.vh | 1 - hdl/ram.v | 21 -------------- hdl/ram.vhdl | 45 ----------------------------- hdl/top.v | 11 ------- hdl/top.vhdl | 21 -------------- 15 files changed, 1 insertion(+), 350 deletions(-) delete mode 100644 hdl/blinking.v delete mode 100644 hdl/blinking.vhdl delete mode 100644 hdl/data/memory.dat delete mode 100644 hdl/examples_pkg.vhdl delete mode 100644 hdl/fakes/generics.vhdl delete mode 100644 hdl/fakes/parameters.v delete mode 100644 hdl/fakes/top.v delete mode 100644 hdl/fakes/top.vhdl delete mode 100644 hdl/headers1/freq.vh delete mode 100644 hdl/headers2/secs.vh delete mode 100644 hdl/ram.v delete mode 100644 hdl/ram.vhdl delete mode 100644 hdl/top.v delete mode 100644 hdl/top.vhdl diff --git a/examples/resources b/examples/resources index 6a58bba3..53d36f1d 160000 --- a/examples/resources +++ b/examples/resources @@ -1 +1 @@ -Subproject commit 6a58bba3b1c36d238d1111e910db02f0b284aef7 +Subproject commit 53d36f1d9d49b26e42d09aaf1e0e6c6fac7736e2 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; From 293d1f02867a6569d379d70ed7aa6d7b83b78f7c Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 20 Jun 2024 19:27:40 -0300 Subject: [PATCH 085/248] Updated resources, removed zybo.xdc --- examples/resources | 2 +- examples/vivado/zybo.xdc | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) delete mode 100644 examples/vivado/zybo.xdc diff --git a/examples/resources b/examples/resources index 53d36f1d..7f34b14e 160000 --- a/examples/resources +++ b/examples/resources @@ -1 +1 @@ -Subproject commit 53d36f1d9d49b26e42d09aaf1e0e6c6fac7736e2 +Subproject commit 7f34b14e2cdcbcbd6f57053a033a2abdd3ff1900 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] From 07e53be5e8b22bd3dabafc6a0f650e9dfe1f78a6 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 20 Jun 2024 19:28:08 -0300 Subject: [PATCH 086/248] Updated Vivado example to use the new PyFPGA --- examples/vivado/vivado.py | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/examples/vivado/vivado.py b/examples/vivado/vivado.py index bf1830a1..497734d3 100644 --- a/examples/vivado/vivado.py +++ b/examples/vivado/vivado.py @@ -1,30 +1,33 @@ -"""Vivado example project.""" +"""Vivado VHDL example project.""" import argparse -import logging -from fpga.project import Project - -logging.basicConfig() +from pyfpga.vivado import Vivado parser = argparse.ArgumentParser() parser.add_argument( - '--action', choices=['generate', 'transfer', 'all'], default='generate', + '--action', choices=['make', 'prog', 'all'], default='make' +) +parser.add_argument( + '--source', choices=['vhdl', 'vlog', 'design'], default='vhdl' ) args = parser.parse_args() -prj = Project('vivado') +prj = Vivado(odir=f'../build/vivado-{args.source}') prj.set_part('xc7z010-1-clg400') -prj.set_outdir('../../build/vivado') - prj.add_param('FREQ', '125000000') -prj.add_files('../../hdl/blinking.vhdl') -prj.add_files('zybo.xdc') -prj.set_top('Blinking') - -if args.action in ['generate', 'all']: - prj.generate() - -if args.action in ['transfer', 'all']: - prj.transfer('fpga') +if args.source == 'vhdl': + prj.add_vhdl('../resources/vhdl/blink.vhdl') +if args.source == 'vlog': + prj.add_vlog('../resources/vlog/blink.v') +prj.add_constraint('../resources/constraints/zybo/timing.xdc', 'syn') +prj.add_constraint('../resources/constraints/zybo/clk.xdc', 'par') +prj.add_constraint('../resources/constraints/zybo/led.xdc', 'par') +prj.set_top('Blink') + +if args.action in ['make', 'all']: + prj.make() + +if args.action in ['prog', 'all']: + prj.prog() From d3bb077ea6f1327397730424804949fd71da1cb9 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 20 Jun 2024 19:28:56 -0300 Subject: [PATCH 087/248] Fixed double timestamp --- pyfpga/project.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index d7758ad0..7e381620 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -13,7 +13,6 @@ import os import subprocess -from datetime import datetime from pathlib import Path from shutil import which from time import time @@ -277,7 +276,9 @@ def _create_file(self, basename, extension, context): file.write(content) def _run(self, command): - self.logger.info('Running the underlying tool (%s)', datetime.now()) + self.logger.info( + 'Running the underlying tool (%s)', self.tool['make-app'] + ) run_error = 0 old_dir = Path.cwd() new_dir = Path(self.odir) @@ -299,7 +300,6 @@ def _run(self, command): finally: os.chdir(old_dir) end = time() - self.logger.info('Done (%s)', datetime.now()) elapsed = end - start self.logger.info( 'Elapsed time %dh %dm %.2fs', From 804bcc1dbd930c2fed0a7bb006ca64068cf250fc Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 23 Jun 2024 16:35:16 -0300 Subject: [PATCH 088/248] Added a script to enable mock-ups --- tests/mocks/source-me.sh | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tests/mocks/source-me.sh 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 From 33429d04e0c7c232fea25d7cedadd22eaa172ac9 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 23 Jun 2024 17:39:56 -0300 Subject: [PATCH 089/248] Improved some messages and how to indicate the command to run --- pyfpga/project.py | 57 ++++++++++++++++++++++------------------------- pyfpga/vivado.py | 10 ++------- 2 files changed, 29 insertions(+), 38 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index 7e381620..52106923 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -14,7 +14,6 @@ import subprocess from pathlib import Path -from shutil import which from time import time from jinja2 import Environment, FileSystemLoader @@ -204,39 +203,41 @@ def add_hook(self, stage, hook): raise ValueError('Invalid stage.') self.data.setdefault('hooks', {}).setdefault(stage, []).append(hook) - def make(self, last='bit', first='cfg'): + def make(self, first='cfg', last='bit'): """Run the underlying tool. - :param last: last step - :type last: str, optional :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: when the needed underlying tool is not found + :raises RuntimeError: error running the needed underlying tool .. note:: valid steps are ``cfg``, ``syn``, ``par`` and ``bit``. """ self.logger.debug('Executing make') if 'part' not in self.data: - self.logger.info('No part specified, using a default value') - if 'top' not in self.data: - self.logger.warning('No top specified') - if 'files' not in self.data: - self.logger.warning('No files specified') - steps = ['cfg', 'syn', 'par', 'bit'] + self.logger.info('Using the default PART') + steps = { + 'cfg': 'Project Creation', + 'syn': 'Synthesis', + 'par': 'Place and Route', + 'bit': 'Bitstream generation' + } if last not in steps: raise ValueError('Invalid last step.') if first not in steps: raise ValueError('Invalid first step.') - first_index = steps.index(first) - last_index = steps.index(last) - if first_index > last_index: + keys = list(steps.keys()) + index = [keys.index(first), keys.index(last)] + if index[0] > index[1]: raise ValueError('Invalid steps combination.') - selected_steps = steps[first_index:last_index + 1] - self._make_prepare([step.upper() for step in selected_steps]) - if not which(self.tool['make-app']): - raise RuntimeError(f'{self.tool["make-app"]} not found.') - self._run(self.tool['make-cmd']) + message = f'from {steps[first]} to {steps[last]}' + if first == last: + message = steps[first] + self.logger.info('Running %s', message) + selected = [step.upper() for step in keys[index[0]:index[1]+1]] + self._run(self._make_prepare(selected)) def prog(self, bitstream=None, position=1): """Program the FPGA @@ -246,16 +247,15 @@ def prog(self, bitstream=None, position=1): :param position: position of the device in the JTAG chain :type position: str, optional :raises ValueError: for missing or wrong values - :raises RuntimeError: when the needed underlying tool 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.') _ = bitstream self._prog_prepare() - if not which(self.tool['prog-app']): - raise RuntimeError(f'{self.tool["prog-app"]} not found.') - self._run(self.tool['prog-cmd']) + self.logger.info('Programming') + self._run(self._prog_prepare()) def _make_prepare(self, steps): raise NotImplementedError('Tool-dependent') @@ -276,10 +276,7 @@ def _create_file(self, basename, extension, context): file.write(content) def _run(self, command): - self.logger.info( - 'Running the underlying tool (%s)', self.tool['make-app'] - ) - run_error = 0 + error = 0 old_dir = Path.cwd() new_dir = Path(self.odir) start = time() @@ -296,7 +293,7 @@ def _run(self, command): last_lines = lines[-10:] if len(lines) >= 10 else lines for line in last_lines: self.logger.error(line.strip()) - run_error = 1 + error = 1 finally: os.chdir(old_dir) end = time() @@ -307,5 +304,5 @@ def _run(self, command): int((elapsed % 3600) // 60), elapsed % 60 ) - if run_error: - raise RuntimeError('Error running the underlying tool') + if error: + raise RuntimeError('Problem with the underlying tool') diff --git a/pyfpga/vivado.py b/pyfpga/vivado.py index d776b52a..9999f443 100644 --- a/pyfpga/vivado.py +++ b/pyfpga/vivado.py @@ -18,10 +18,6 @@ class Vivado(Project): """Class to support Vivado projects.""" def _make_prepare(self, steps): - self.tool['make-app'] = 'vivado' - self.tool['make-cmd'] = ( - 'vivado -mode batch -notrace -quiet -source vivado.tcl' - ) context = { 'PROJECT': self.name or 'vivado', 'PART': self.data.get('part', 'xc7k160t-3-fbg484') @@ -76,10 +72,8 @@ def _make_prepare(self, steps): for stage in self.data['hooks']: context[stage.upper()] = '\n'.join(self.data['hooks'][stage]) self._create_file('vivado', 'tcl', context) + return 'vivado -mode batch -notrace -quiet -source vivado.tcl' def _prog_prepare(self): # binaries = ['bit'] - self.tool['prog-app'] = 'vivado' - self.tool['prog-cmd'] = ( - 'vivado -mode batch -notrace -quiet -source vivado-prog.tcl' - ) + return 'vivado -mode batch -notrace -quiet -source vivado-prog.tcl' From a2cd4120112b41b33511b3f1a209b693b9f30a83 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 23 Jun 2024 19:55:36 -0300 Subject: [PATCH 090/248] Modified to print the last 20 lines of the log file --- pyfpga/project.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index 52106923..5008f3e6 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -276,6 +276,7 @@ def _create_file(self, basename, extension, context): file.write(content) def _run(self, command): + NUM = 20 error = 0 old_dir = Path.cwd() new_dir = Path(self.odir) @@ -290,9 +291,11 @@ def _run(self, command): except subprocess.CalledProcessError: with open('run.log', 'r', encoding='utf-8') as file: lines = file.readlines() - last_lines = lines[-10:] if len(lines) >= 10 else lines + last_lines = lines[-NUM:] if len(lines) >= NUM else lines for line in last_lines: - self.logger.error(line.strip()) + message = line.strip() + if len(message): + print(f'>> {message}') error = 1 finally: os.chdir(old_dir) From bbcd7a1808f3e345d723a4530c429bbe69325fac Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 23 Jun 2024 20:36:26 -0300 Subject: [PATCH 091/248] Added new sources, updated the Vivado example --- examples/sources/slog/blink.sv | 23 ++++++++++++++++ examples/sources/slog/include/header.svh | 1 + examples/sources/slog/top.sv | 24 +++++++++++++++++ examples/sources/vhdl/blink.vhdl | 34 ++++++++++++++++++++++++ examples/sources/vhdl/blink_pkg.vhdl | 16 +++++++++++ examples/sources/vhdl/top.vhdl | 25 +++++++++++++++++ examples/sources/vlog/blink.v | 23 ++++++++++++++++ examples/sources/vlog/include/header.vh | 1 + examples/sources/vlog/top.v | 24 +++++++++++++++++ examples/vivado/sources/zybo/clk.xdc | 2 ++ examples/vivado/sources/zybo/led.xdc | 2 ++ examples/vivado/sources/zybo/timing.xdc | 1 + examples/vivado/vivado.py | 20 +++++++++----- 13 files changed, 189 insertions(+), 7 deletions(-) create mode 100644 examples/sources/slog/blink.sv create mode 100644 examples/sources/slog/include/header.svh create mode 100644 examples/sources/slog/top.sv create mode 100644 examples/sources/vhdl/blink.vhdl create mode 100644 examples/sources/vhdl/blink_pkg.vhdl create mode 100644 examples/sources/vhdl/top.vhdl create mode 100644 examples/sources/vlog/blink.v create mode 100644 examples/sources/vlog/include/header.vh create mode 100644 examples/sources/vlog/top.v create mode 100644 examples/vivado/sources/zybo/clk.xdc create mode 100644 examples/vivado/sources/zybo/led.xdc create mode 100644 examples/vivado/sources/zybo/timing.xdc diff --git a/examples/sources/slog/blink.sv b/examples/sources/slog/blink.sv new file mode 100644 index 00000000..b74bfe08 --- /dev/null +++ b/examples/sources/slog/blink.sv @@ -0,0 +1,23 @@ +module Blink #( + parameter int FREQ = 25000000 +)( + input clk_i, + output led_o +); + + localparam int DIV = FREQ; + 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/include/header.svh b/examples/sources/slog/include/header.svh new file mode 100644 index 00000000..78aa9bfd --- /dev/null +++ b/examples/sources/slog/include/header.svh @@ -0,0 +1 @@ +`define INCLUDE diff --git a/examples/sources/slog/top.sv b/examples/sources/slog/top.sv new file mode 100644 index 00000000..24e780e5 --- /dev/null +++ b/examples/sources/slog/top.sv @@ -0,0 +1,24 @@ +`include "header.svh" + +module Top #( + parameter FREQ = 0 +)( + input clk_i, + output led_o +); + + initial begin + if (FREQ==0) begin + $stop("FREQ must be greater than 0"); + $error("FREQ must be greater than 0"); + $fatal("FREQ must be greater than 0"); + end + end + +`ifdef INCLUDE +`ifdef DEFINE + Blink #(.FREQ (FREQ)) dut (.clk_i (clk_i), .led_o (led_o)); +`endif +`endif + +endmodule diff --git a/examples/sources/vhdl/blink.vhdl b/examples/sources/vhdl/blink.vhdl new file mode 100644 index 00000000..9b9a4bc3 --- /dev/null +++ b/examples/sources/vhdl/blink.vhdl @@ -0,0 +1,34 @@ +library IEEE; +use IEEE.std_logic_1164.all; + +entity Blink is + generic ( + FREQ : positive := 25e6 + ); + port ( + clk_i : in std_logic; + led_o : out std_logic + ); +end entity Blink; + +architecture RTL of Blink is + constant DIV : positive := FREQ; + 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..115f0899 --- /dev/null +++ b/examples/sources/vhdl/blink_pkg.vhdl @@ -0,0 +1,16 @@ +library IEEE; +use IEEE.std_logic_1164.all; + +package blink_pkg is + + component Blink is + generic ( + FREQ : natural:=25e6 + ); + 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..0ff6d780 --- /dev/null +++ b/examples/sources/vhdl/top.vhdl @@ -0,0 +1,25 @@ +library IEEE; +use IEEE.std_logic_1164.all; +library blink_lib; +use blink_lib.blink_pkg.all; + +entity Top is + generic ( + FREQ : natural := 0 + ); + port ( + clk_i : in std_logic; + led_o : out std_logic + ); +end entity Top; + +architecture ARCH of Top is +begin + + assert FREQ > 0 report "FREQ must be greater than 0" severity failure; + + blink_i: Blink + generic map (FREQ => FREQ) + port map (clk_i => clk_i, led_o => led_o); + +end architecture ARCH; diff --git a/examples/sources/vlog/blink.v b/examples/sources/vlog/blink.v new file mode 100644 index 00000000..7522e0f7 --- /dev/null +++ b/examples/sources/vlog/blink.v @@ -0,0 +1,23 @@ +module Blink #( + parameter FREQ = 25000000 +)( + input clk_i, + output led_o +); + + localparam DIV = FREQ; + 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/include/header.vh b/examples/sources/vlog/include/header.vh new file mode 100644 index 00000000..78aa9bfd --- /dev/null +++ b/examples/sources/vlog/include/header.vh @@ -0,0 +1 @@ +`define INCLUDE diff --git a/examples/sources/vlog/top.v b/examples/sources/vlog/top.v new file mode 100644 index 00000000..6c63ebae --- /dev/null +++ b/examples/sources/vlog/top.v @@ -0,0 +1,24 @@ +`include "header.vh" + +module Top #( + parameter FREQ = 0 +)( + input clk_i, + output led_o +); + + initial begin + if (FREQ==0) begin + $stop("FREQ must be greater than 0"); + $error("FREQ must be greater than 0"); + $fatal("FREQ must be greater than 0"); + end + end + +`ifdef INCLUDE +`ifdef DEFINE + Blink #(.FREQ (FREQ)) dut (.clk_i (clk_i), .led_o (led_o)); +`endif +`endif + +endmodule diff --git a/examples/vivado/sources/zybo/clk.xdc b/examples/vivado/sources/zybo/clk.xdc new file mode 100644 index 00000000..877602c7 --- /dev/null +++ b/examples/vivado/sources/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/vivado/sources/zybo/led.xdc b/examples/vivado/sources/zybo/led.xdc new file mode 100644 index 00000000..8106c5cc --- /dev/null +++ b/examples/vivado/sources/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/vivado/sources/zybo/timing.xdc b/examples/vivado/sources/zybo/timing.xdc new file mode 100644 index 00000000..be3d63fc --- /dev/null +++ b/examples/vivado/sources/zybo/timing.xdc @@ -0,0 +1 @@ +create_clock -name clk_i -period 8 [get_ports clk_i] diff --git a/examples/vivado/vivado.py b/examples/vivado/vivado.py index 497734d3..9f9ed802 100644 --- a/examples/vivado/vivado.py +++ b/examples/vivado/vivado.py @@ -9,7 +9,7 @@ '--action', choices=['make', 'prog', 'all'], default='make' ) parser.add_argument( - '--source', choices=['vhdl', 'vlog', 'design'], default='vhdl' + '--source', choices=['vlog', 'vhdl', 'slog', 'design'], default='vlog' ) args = parser.parse_args() @@ -18,13 +18,19 @@ prj.add_param('FREQ', '125000000') if args.source == 'vhdl': - prj.add_vhdl('../resources/vhdl/blink.vhdl') + prj.add_vhdl('../sources/vhdl/*.vhdl', 'blink_lib') if args.source == 'vlog': - prj.add_vlog('../resources/vlog/blink.v') -prj.add_constraint('../resources/constraints/zybo/timing.xdc', 'syn') -prj.add_constraint('../resources/constraints/zybo/clk.xdc', 'par') -prj.add_constraint('../resources/constraints/zybo/led.xdc', 'par') -prj.set_top('Blink') + prj.add_include('../sources/vlog/include') + prj.add_vlog('../sources/vlog/*.v') +if args.source == 'slog': + prj.add_include('../sources/slog/include') + prj.add_vlog('../sources/slog/*.sv') +if args.source in ['vlog', 'slog']: + prj.add_define('DEFINE', '1') +prj.add_constraint('sources/zybo/timing.xdc', 'syn') +prj.add_constraint('sources/zybo/clk.xdc', 'par') +prj.add_constraint('sources/zybo/led.xdc', 'par') +prj.set_top('Top') if args.action in ['make', 'all']: prj.make() From 3a4d7173d4dde4fe6a489110ddc3f67205346c05 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 23 Jun 2024 21:14:01 -0300 Subject: [PATCH 092/248] Implemented programming with Vivado --- pyfpga/ise.py | 2 +- pyfpga/libero.py | 2 +- pyfpga/openflow.py | 2 +- pyfpga/project.py | 10 ++++------ pyfpga/quartus.py | 2 +- pyfpga/vivado.py | 10 ++++++++-- 6 files changed, 16 insertions(+), 12 deletions(-) diff --git a/pyfpga/ise.py b/pyfpga/ise.py index 72e577bc..d5289930 100644 --- a/pyfpga/ise.py +++ b/pyfpga/ise.py @@ -24,7 +24,7 @@ def _make_prepare(self, steps): self.tool['make-app'] = 'xtclsh' self.tool['make-cmd'] = 'xtclsh ise.tcl' - def _prog_prepare(self): + def _prog_prepare(self, bitstream, position): # binaries = ['bit'] self.tool['prog-app'] = 'impact' self.tool['prog-cmd'] = 'impact -batch impact-prog' diff --git a/pyfpga/libero.py b/pyfpga/libero.py index 35d4d89f..a717f1b7 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -24,7 +24,7 @@ def _make_prepare(self, steps): self.tool['make-app'] = 'libero' self.tool['make-cmd'] = 'libero SCRIPT:libero.tcl' - def _prog_prepare(self): + def _prog_prepare(self, bitstream, position): # binaries = ['bit'] self.tool['prog-app'] = '' self.tool['prog-cmd'] = '' diff --git a/pyfpga/openflow.py b/pyfpga/openflow.py index cb756727..0b7865ba 100644 --- a/pyfpga/openflow.py +++ b/pyfpga/openflow.py @@ -22,7 +22,7 @@ def _make_prepare(self, steps): self.tool['make-app'] = 'docker' self.tool['make-cmd'] = 'bash openflow.sh' - def _prog_prepare(self): + def _prog_prepare(self, bitstream, position): # binaries = ['bit'] self.tool['prog-app'] = 'docker' self.tool['prog-cmd'] = 'bash openflow-prog.sh' diff --git a/pyfpga/project.py b/pyfpga/project.py index 5008f3e6..951b1c3f 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -252,15 +252,13 @@ def prog(self, bitstream=None, position=1): self.logger.debug('Executing prog') if position not in range(1, 9): raise ValueError('Invalid position.') - _ = bitstream - self._prog_prepare() self.logger.info('Programming') - self._run(self._prog_prepare()) + self._run(self._prog_prepare(bitstream, position)) def _make_prepare(self, steps): raise NotImplementedError('Tool-dependent') - def _prog_prepare(self): + def _prog_prepare(self, bitstream, position): raise NotImplementedError('Tool-dependent') def _create_file(self, basename, extension, context): @@ -276,7 +274,7 @@ def _create_file(self, basename, extension, context): file.write(content) def _run(self, command): - NUM = 20 + num = 20 error = 0 old_dir = Path.cwd() new_dir = Path(self.odir) @@ -291,7 +289,7 @@ def _run(self, command): except subprocess.CalledProcessError: with open('run.log', 'r', encoding='utf-8') as file: lines = file.readlines() - last_lines = lines[-NUM:] if len(lines) >= NUM else lines + last_lines = lines[-num:] if len(lines) >= num else lines for line in last_lines: message = line.strip() if len(message): diff --git a/pyfpga/quartus.py b/pyfpga/quartus.py index c635ffa5..4ca2bf78 100644 --- a/pyfpga/quartus.py +++ b/pyfpga/quartus.py @@ -24,7 +24,7 @@ def _make_prepare(self, steps): self.tool['make-app'] = 'quartus_sh' self.tool['make-cmd'] = 'quartus_sh --script quartus.tcl' - def _prog_prepare(self): + def _prog_prepare(self, bitstream, position): # binaries = ['sof', 'pof'] self.tool['prog-app'] = 'quartus_pgm' self.tool['prog-cmd'] = 'bash quartus-prog.sh' diff --git a/pyfpga/vivado.py b/pyfpga/vivado.py index 9999f443..3470123e 100644 --- a/pyfpga/vivado.py +++ b/pyfpga/vivado.py @@ -11,6 +11,7 @@ # pylint: disable=too-many-locals # pylint: disable=too-many-branches +from pathlib import Path from pyfpga.project import Project @@ -74,6 +75,11 @@ def _make_prepare(self, steps): self._create_file('vivado', 'tcl', context) return 'vivado -mode batch -notrace -quiet -source vivado.tcl' - def _prog_prepare(self): - # binaries = ['bit'] + def _prog_prepare(self, bitstream, position): + _ = position # Not needed for Vivado + if not bitstream: + basename = self.name or 'vivado' + bitstream = Path(self.odir).resolve() / f'{basename}.bit' + context = {'BITSTREAM': bitstream} + self._create_file('vivado-prog', 'tcl', context) return 'vivado -mode batch -notrace -quiet -source vivado-prog.tcl' From 2bb960ce888a6f5fe43970e6a47281ed9cebf578 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 23 Jun 2024 21:17:32 -0300 Subject: [PATCH 093/248] ci: added to install pyfpga and prepare mock-ups --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 061381db..40699986 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,6 +22,6 @@ jobs: with: python-version: ${{ matrix.pyver }} - name: Install dependencies - run: pip install pytest + run: pip install . && pip install pytest - name: Run tests - run: make test + run: source tests/mocks/source-me.sh && make test From eb7c046f353094bd0242eaf199bf05d19c7ae8f3 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 23 Jun 2024 21:21:29 -0300 Subject: [PATCH 094/248] Fixed/updated package name --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e6b45d1b..9018d2d7 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup, find_packages -import fpga +import pyfpga with open('README.md', 'r') as fh: long_description = fh.read() From 4177a8b70e1afe98f2c17840b384d7b2c8a6561c Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 23 Jun 2024 21:31:11 -0300 Subject: [PATCH 095/248] Fixed/updated package name, added jinja2 as dependency, deprecated Python 3.7 --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 9018d2d7..a3eaab1d 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,6 @@ 'Intended Audience :: Developers', 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', @@ -39,5 +38,6 @@ 'Topic :: Utilities', 'Topic :: Software Development :: Build Tools', "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)" - ] + ], + install_requires=['jinja2'] ) From 80cfbce1aec28dfef14330ceb8dd63d312d2e13e Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 23 Jun 2024 22:43:25 -0300 Subject: [PATCH 096/248] Updated the header of the mocks --- tests/mocks/impact | 21 +++++---------- tests/mocks/libero | 21 ++++----------- tests/mocks/quartus_pgm | 21 +++++---------- tests/mocks/quartus_sh | 21 ++++----------- tests/mocks/vivado | 57 ++++++++++++++++------------------------- tests/mocks/xtclsh | 21 ++++----------- 6 files changed, 51 insertions(+), 111 deletions(-) diff --git a/tests/mocks/impact b/tests/mocks/impact index 17eee4a6..0fbbacbc 100755 --- a/tests/mocks/impact +++ b/tests/mocks/impact @@ -1,23 +1,14 @@ #!/usr/bin/env python3 + # -# Copyright (C) 2022 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. +# Copyright (C) 2022-2024 Rodrigo A. Melo # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # import argparse + parser = argparse.ArgumentParser() parser.add_argument('-batch', action='store_true', required=True) @@ -25,4 +16,6 @@ parser.add_argument('source') args = parser.parse_args() -print(f'INFO:the {parser.prog.upper()} mock has been executed') +tool = parser.prog + +print(f'INFO:the {tool.upper()} mock has been executed') diff --git a/tests/mocks/libero b/tests/mocks/libero index b0f0b691..a4663e9d 100755 --- a/tests/mocks/libero +++ b/tests/mocks/libero @@ -1,19 +1,9 @@ #!/usr/bin/env python3 + # -# Copyright (C) 2022 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. +# Copyright (C) 2022-2024 Rodrigo A. Melo # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # import argparse @@ -34,7 +24,7 @@ if not args.source.startswith("SCRIPT:", 0): sys.exit(1) tcl = f''' -proc unknown {{ cmmd args }} {{ }} +proc unknown args {{ }} source {args.source.replace('SCRIPT:', '')} ''' @@ -46,8 +36,7 @@ subprocess.run( f'tclsh {tool}-mock.tcl', shell=True, check=True, - universal_newlines=True, - #stdout=output, stderr=subprocess.STDOUT + universal_newlines=True ) print(f'INFO:the {tool.upper()} mock has been executed') diff --git a/tests/mocks/quartus_pgm b/tests/mocks/quartus_pgm index 3353ee46..8993ca10 100755 --- a/tests/mocks/quartus_pgm +++ b/tests/mocks/quartus_pgm @@ -1,23 +1,14 @@ #!/usr/bin/env python3 + # -# Copyright (C) 2022 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. +# Copyright (C) 2022-2024 Rodrigo A. Melo # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # import argparse + parser = argparse.ArgumentParser() parser.add_argument('-c', required=True) @@ -26,4 +17,6 @@ parser.add_argument('-o', required=True) args = parser.parse_args() -print(f'INFO:the {parser.prog.upper()} mock has been executed') +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 index a002e73d..64a8cf65 100755 --- a/tests/mocks/quartus_sh +++ b/tests/mocks/quartus_sh @@ -1,19 +1,9 @@ #!/usr/bin/env python3 + # -# Copyright (C) 2022 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. +# Copyright (C) 2022-2024 Rodrigo A. Melo # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # import argparse @@ -32,7 +22,7 @@ tool = parser.prog tcl = f''' lappend auto_path pkg -proc unknown {{ cmmd args }} {{ }} +proc unknown args {{ }} source {args.script} ''' @@ -60,8 +50,7 @@ subprocess.run( f'tclsh {tool}-mock.tcl', shell=True, check=True, - universal_newlines=True, - #stdout=output, stderr=subprocess.STDOUT + universal_newlines=True ) print(f'INFO:the {tool.upper()} mock has been executed') diff --git a/tests/mocks/vivado b/tests/mocks/vivado index af5614d1..f6a47eff 100755 --- a/tests/mocks/vivado +++ b/tests/mocks/vivado @@ -1,19 +1,9 @@ #!/usr/bin/env python3 + # -# Copyright (C) 2022 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. +# Copyright (C) 2022-2024 Rodrigo A. Melo # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # import argparse @@ -31,28 +21,26 @@ 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 }} {{ }} - +#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 {{ cmmd args }} {{ }} +proc unknown args {{ }} source {args.source} ''' @@ -64,8 +52,7 @@ subprocess.run( f'tclsh {tool}-mock.tcl', shell=True, check=True, - universal_newlines=True, - #stdout=output, stderr=subprocess.STDOUT + universal_newlines=True ) print(f'INFO:the {tool.upper()} mock has been executed') diff --git a/tests/mocks/xtclsh b/tests/mocks/xtclsh index 12046fa3..7621c4d9 100755 --- a/tests/mocks/xtclsh +++ b/tests/mocks/xtclsh @@ -1,19 +1,9 @@ #!/usr/bin/env python3 + # -# Copyright (C) 2022 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. +# Copyright (C) 2022-2024 Rodrigo A. Melo # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # import argparse @@ -29,7 +19,7 @@ args = parser.parse_args() tool = parser.prog tcl = f''' -proc unknown {{ cmmd args }} {{ }} +proc unknown args {{ }} source {args.source} ''' @@ -41,8 +31,7 @@ subprocess.run( f'tclsh {tool}-mock.tcl', shell=True, check=True, - universal_newlines=True, - #stdout=output, stderr=subprocess.STDOUT + universal_newlines=True ) print(f'INFO:the {tool.upper()} mock has been executed') From 57149f80b2f2ff8aeb30a68a3439721551427083 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 23 Jun 2024 23:00:04 -0300 Subject: [PATCH 097/248] Updated README.md --- README.md | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 3a60df40..1294d710 100644 --- a/README.md +++ b/README.md @@ -14,33 +14,22 @@ With PyFPGA, you can create your own FPGA development workflow tailored to your Some of its benefits are: * It provides a unified API between tools/devices. -* It's **Version Control Systems** and **Continuous Integration friendly**. +* It's **Version Control Systems** and **Continuous Integration** friendly. * It ensures reproducibility and repeatability. * It consumes fewer system resources than GUI-based workflows. ## 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_constraint('location3/example.xdc') prj.set_top('Top') - -# Generate the bitstream running the tool -prj.generate() +prj.make() ``` The next steps are to read the [docs](https://pyfpga.github.io/pyfpga) or take a look at [examples](examples). @@ -54,11 +43,11 @@ For a comprehensive list of supported tools, features and limitations, please re > **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). +> This includes having the tools installed, properly configured and licensed (when needed). ## Installation -PyFPGA requires Python>=3.7. +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: From 33ea70b889caa212d1f04fa5f888045709561df1 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 23 Jun 2024 23:04:55 -0300 Subject: [PATCH 098/248] Renamed add_constraint as add_cons --- README.md | 2 +- examples/vivado/vivado.py | 6 +++--- pyfpga/project.py | 4 ++-- tests/test_data.py | 6 +++--- tests/test_vivado.py | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 1294d710..aa17c849 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ prj = Vivado('example') prj.set_part('xc7z010-1-clg400') prj.add_vlog('location1/*.v') prj.add_vlog('location2/top.v') -prj.add_constraint('location3/example.xdc') +prj.add_cons('location3/example.xdc') prj.set_top('Top') prj.make() ``` diff --git a/examples/vivado/vivado.py b/examples/vivado/vivado.py index 9f9ed802..930e62cd 100644 --- a/examples/vivado/vivado.py +++ b/examples/vivado/vivado.py @@ -27,9 +27,9 @@ prj.add_vlog('../sources/slog/*.sv') if args.source in ['vlog', 'slog']: prj.add_define('DEFINE', '1') -prj.add_constraint('sources/zybo/timing.xdc', 'syn') -prj.add_constraint('sources/zybo/clk.xdc', 'par') -prj.add_constraint('sources/zybo/led.xdc', 'par') +prj.add_cons('sources/zybo/timing.xdc', 'syn') +prj.add_cons('sources/zybo/clk.xdc', 'par') +prj.add_cons('sources/zybo/led.xdc', 'par') prj.set_top('Top') if args.action in ['make', 'all']: diff --git a/pyfpga/project.py b/pyfpga/project.py index 951b1c3f..179ef179 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -114,7 +114,7 @@ def add_vlog(self, pathname): self.logger.debug('Executing add_vlog') self._add_file(pathname, 'vlog') - def add_constraint(self, path, when='all'): + def add_cons(self, path, when='all'): """Add a constraint file. :param pathname: path of a file @@ -123,7 +123,7 @@ def add_constraint(self, path, when='all'): :type only: str, optional :raises FileNotFoundError: if path is not found """ - self.logger.debug('Executing add_constraint') + self.logger.debug('Executing add_cons') path = Path(path).resolve() if not path.is_file(): raise FileNotFoundError(path) diff --git a/tests/test_data.py b/tests/test_data.py index c4a355c0..69446951 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -70,9 +70,9 @@ def test_data(): prj.add_slog('fakedata/**/*.sv') prj.add_vhdl('fakedata/**/*.vhdl', 'LIB') prj.add_vlog('fakedata/**/*.v') - prj.add_constraint('fakedata/cons/all.xdc') - prj.add_constraint('fakedata/cons/syn.xdc', 'syn') - prj.add_constraint('fakedata/cons/par.xdc', 'par') + prj.add_cons('fakedata/cons/all.xdc') + prj.add_cons('fakedata/cons/syn.xdc', 'syn') + prj.add_cons('fakedata/cons/par.xdc', 'par') prj.add_param('PAR1', 'VAL1') prj.add_param('PAR2', 'VAL2') prj.add_param('PAR3', 'VAL3') diff --git a/tests/test_vivado.py b/tests/test_vivado.py index 9afa353d..787e90cb 100644 --- a/tests/test_vivado.py +++ b/tests/test_vivado.py @@ -11,9 +11,9 @@ def test_vivado(): prj.add_slog('fakedata/**/*.sv') prj.add_vhdl('fakedata/**/*.vhdl', 'LIB') prj.add_vlog('fakedata/**/*.v') - prj.add_constraint('fakedata/cons/all.xdc') - prj.add_constraint('fakedata/cons/syn.xdc', 'syn') - prj.add_constraint('fakedata/cons/par.xdc', 'par') + prj.add_cons('fakedata/cons/all.xdc') + prj.add_cons('fakedata/cons/syn.xdc', 'syn') + prj.add_cons('fakedata/cons/par.xdc', 'par') prj.add_param('PAR1', 'VAL1') prj.add_param('PAR2', 'VAL2') prj.add_define('DEF1', 'VAL1') From cefcc9a18876bf7499d23f0bb07cbb94288a1108 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 24 Jun 2024 19:34:32 -0300 Subject: [PATCH 099/248] Removed support for VHDL architectures --- docs/internals.rst | 1 - pyfpga/project.py | 9 --------- pyfpga/templates/vivado.jinja | 4 ---- pyfpga/vivado.py | 2 -- tests/test_data.py | 2 -- tests/test_vivado.py | 1 - 6 files changed, 19 deletions(-) diff --git a/docs/internals.rst b/docs/internals.rst index 910df99a..bcaa371f 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -60,7 +60,6 @@ Internal data structure 'DEF2': 'VAL2', 'DEF3': 'VAL3' }, - 'arch': 'ARCHNAME', 'hooks': { 'precfg': ['CMD1', 'CMD2'], 'postcfg': ['CMD1', 'CMD2'], diff --git a/pyfpga/project.py b/pyfpga/project.py index 179ef179..faaacffe 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -153,15 +153,6 @@ def add_define(self, name, value): self.logger.debug('Executing add_define') self.data.setdefault('defines', {})[name] = value - def set_arch(self, name): - """Set the VHDL architecture. - - :param name: architecture name - :type name: str - """ - self.logger.debug('Executing set_arch') - self.data['arch'] = name - def add_fileset(self, pathname): """Add fileset file/s. diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index 05b1f0fa..2c593f69 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -32,10 +32,6 @@ set_property INCLUDE_DIRS { {{ INCLUDES}} } [current_fileset] set_property GENERIC { {{ PARAMS }} } -objects [get_filesets sources_1] {% endif %} -{% if ARCH %} -set_property TOP_ARCH {{ ARCH }} [current_fileset] -{% endif %} - {{ POSTCFG }} close_project diff --git a/pyfpga/vivado.py b/pyfpga/vivado.py index 3470123e..c0a625e6 100644 --- a/pyfpga/vivado.py +++ b/pyfpga/vivado.py @@ -67,8 +67,6 @@ def _make_prepare(self, steps): for key, value in self.data['params'].items(): params.append(f'{key}={value}') context['PARAMS'] = ' '.join(params) - if 'arch' in self.data: - context['ARCH'] = self.data['arch'] if 'hooks' in self.data: for stage in self.data['hooks']: context[stage.upper()] = '\n'.join(self.data['hooks'][stage]) diff --git a/tests/test_data.py b/tests/test_data.py index 69446951..250ea793 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -45,7 +45,6 @@ 'DEF2': 'VAL2', 'DEF3': 'VAL3' }, - 'arch': 'ARCHNAME', 'hooks': { 'precfg': ['CMD1', 'CMD2'], 'postcfg': ['CMD1', 'CMD2'], @@ -63,7 +62,6 @@ def test_data(): prj = Project() prj.set_part('PARTNAME') prj.set_top('TOPNAME') - prj.set_arch('ARCHNAME') prj.add_include('fakedata/dir1') prj.add_include('fakedata/dir2') prj.add_include('fakedata/dir3') diff --git a/tests/test_vivado.py b/tests/test_vivado.py index 787e90cb..57781a09 100644 --- a/tests/test_vivado.py +++ b/tests/test_vivado.py @@ -5,7 +5,6 @@ def test_vivado(): prj = Vivado() prj.set_part('PARTNAME') prj.set_top('TOPNAME') - prj.set_arch('ARCHNAME') prj.add_include('fakedata/dir1') prj.add_include('fakedata/dir2') prj.add_slog('fakedata/**/*.sv') From 9584c4936f4c40689e47acf9df24513696ff940f Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 24 Jun 2024 19:41:19 -0300 Subject: [PATCH 100/248] Removed unused files --- pyfpga/factory.py | 44 ------------------- pyfpga/tool.py | 105 ---------------------------------------------- 2 files changed, 149 deletions(-) delete mode 100644 pyfpga/factory.py delete mode 100644 pyfpga/tool.py diff --git a/pyfpga/factory.py b/pyfpga/factory.py deleted file mode 100644 index 6236730e..00000000 --- a/pyfpga/factory.py +++ /dev/null @@ -1,44 +0,0 @@ -# -# Copyright (C) 2024 Rodrigo A. Melo -# -# SPDX-License-Identifier: GPL-3.0-or-later -# - -""" -A factory class to create FPGA projects. -""" - -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 - -# pylint: disable=return-in-init -# pylint: disable=no-else-return -# pylint: disable=too-many-return-statements -# pylint: disable=too-few-public-methods - - -class Factory: - """A factory class to create FPGA projects.""" - - def __init__( - self, tool='vivado', project=None): - """Class constructor.""" - if tool == 'ghdl': - return Openflow(project) # , frontend='ghdl', backend='vhdl') - elif tool == 'ise': - return Ise(project) - elif tool == 'libero': - return Libero(project) - elif tool == 'openflow': - return Openflow(project) - elif tool == 'quartus': - return Quartus(project) - elif tool == 'vivado': - return Vivado(project) - elif tool == 'yosys': - return Openflow(project) # , frontend='yosys', backend='verilog') - else: - raise NotImplementedError(tool) diff --git a/pyfpga/tool.py b/pyfpga/tool.py deleted file mode 100644 index 30309ad0..00000000 --- a/pyfpga/tool.py +++ /dev/null @@ -1,105 +0,0 @@ -# -# Copyright (C) 2019-2024 Rodrigo A. Melo -# -# SPDX-License-Identifier: GPL-3.0-or-later -# - -""" -Defines the interface to be inherited to support a tool. -""" - -# def tcl_path(path): -# """Returns a Tcl suitable path.""" -# return path.replace(os.path.sep, "/") - -# pylint: disable=too-few-public-methods - - -class Tool: - """Tool interface.""" - -# 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('#POSTPAR_CMDS#', '\n'.join(self.cmds['postpar'])) -# 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 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] From b04a22290fc811f210b3416186bfbff9a714f5d9 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 24 Jun 2024 19:43:54 -0300 Subject: [PATCH 101/248] Moved the zybo constraints from the Vivado example to sources --- examples/{vivado => }/sources/zybo/clk.xdc | 0 examples/{vivado => }/sources/zybo/led.xdc | 0 examples/{vivado => }/sources/zybo/timing.xdc | 0 examples/vivado/vivado.py | 6 +++--- 4 files changed, 3 insertions(+), 3 deletions(-) rename examples/{vivado => }/sources/zybo/clk.xdc (100%) rename examples/{vivado => }/sources/zybo/led.xdc (100%) rename examples/{vivado => }/sources/zybo/timing.xdc (100%) diff --git a/examples/vivado/sources/zybo/clk.xdc b/examples/sources/zybo/clk.xdc similarity index 100% rename from examples/vivado/sources/zybo/clk.xdc rename to examples/sources/zybo/clk.xdc diff --git a/examples/vivado/sources/zybo/led.xdc b/examples/sources/zybo/led.xdc similarity index 100% rename from examples/vivado/sources/zybo/led.xdc rename to examples/sources/zybo/led.xdc diff --git a/examples/vivado/sources/zybo/timing.xdc b/examples/sources/zybo/timing.xdc similarity index 100% rename from examples/vivado/sources/zybo/timing.xdc rename to examples/sources/zybo/timing.xdc diff --git a/examples/vivado/vivado.py b/examples/vivado/vivado.py index 930e62cd..99ec2745 100644 --- a/examples/vivado/vivado.py +++ b/examples/vivado/vivado.py @@ -27,9 +27,9 @@ prj.add_vlog('../sources/slog/*.sv') if args.source in ['vlog', 'slog']: prj.add_define('DEFINE', '1') -prj.add_cons('sources/zybo/timing.xdc', 'syn') -prj.add_cons('sources/zybo/clk.xdc', 'par') -prj.add_cons('sources/zybo/led.xdc', 'par') +prj.add_cons('../sources/zybo/timing.xdc', 'syn') +prj.add_cons('../sources/zybo/clk.xdc', 'par') +prj.add_cons('../sources/zybo/led.xdc', 'par') prj.set_top('Top') if args.action in ['make', 'all']: From 8fcf3d700767f8713d18c051c2ad1d68ec687fc7 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 24 Jun 2024 20:28:50 -0300 Subject: [PATCH 102/248] Simplified Vivado example content --- examples/vivado/Makefile | 6 - examples/vivado/README.md | 31 -- examples/vivado/design.py | 33 -- examples/vivado/design.tcl | 608 -------------------------- examples/vivado/{vivado.py => run.py} | 0 examples/vivado/run.sh | 10 + 6 files changed, 10 insertions(+), 678 deletions(-) delete mode 100644 examples/vivado/Makefile delete mode 100644 examples/vivado/README.md delete mode 100644 examples/vivado/design.py delete mode 100644 examples/vivado/design.tcl rename examples/vivado/{vivado.py => run.py} (100%) create mode 100644 examples/vivado/run.sh diff --git a/examples/vivado/Makefile b/examples/vivado/Makefile deleted file mode 100644 index f166a9f9..00000000 --- a/examples/vivado/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/make - -SCRIPTS = $(wildcard *.py) - -all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT) || exit;) 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 7ac4eef3..00000000 --- a/examples/vivado/design.py +++ /dev/null @@ -1,33 +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') - -prj.generate() 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 <design_name> 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 <design_name> 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 <design_name> 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 <design_name> 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 <design_name> 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 <hier>."} - 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 {<Select>} \ - CONFIG.PCW_ENET0_GRP_MDIO_ENABLE {0} \ - CONFIG.PCW_ENET0_GRP_MDIO_IO {<Select>} \ - 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 {<Select>} \ - CONFIG.PCW_EN_CLK0_PORT {0} \ - CONFIG.PCW_EN_EMIO_TTC0 {0} \ - CONFIG.PCW_EN_EMIO_WP_SDIO0 {1} \ - CONFIG.PCW_EN_ENET0 {0} \ - CONFIG.PCW_EN_GPIO {0} \ - CONFIG.PCW_EN_QSPI {1} \ - CONFIG.PCW_EN_RST0_PORT {0} \ - CONFIG.PCW_EN_SDIO0 {1} \ - CONFIG.PCW_EN_TTC0 {0} \ - CONFIG.PCW_EN_UART1 {1} \ - CONFIG.PCW_EN_USB0 {0} \ - CONFIG.PCW_FCLK0_PERIPHERAL_DIVISOR0 {1} \ - CONFIG.PCW_FCLK0_PERIPHERAL_DIVISOR1 {1} \ - CONFIG.PCW_FCLK1_PERIPHERAL_DIVISOR0 {1} \ - CONFIG.PCW_FCLK1_PERIPHERAL_DIVISOR1 {1} \ - CONFIG.PCW_FCLK2_PERIPHERAL_DIVISOR0 {1} \ - CONFIG.PCW_FCLK2_PERIPHERAL_DIVISOR1 {1} \ - CONFIG.PCW_FCLK3_PERIPHERAL_DIVISOR0 {1} \ - CONFIG.PCW_FCLK3_PERIPHERAL_DIVISOR1 {1} \ - CONFIG.PCW_FCLK_CLK0_BUF {FALSE} \ - CONFIG.PCW_FPGA0_PERIPHERAL_FREQMHZ {100} \ - CONFIG.PCW_FPGA_FCLK0_ENABLE {0} \ - CONFIG.PCW_FPGA_FCLK1_ENABLE {0} \ - CONFIG.PCW_FPGA_FCLK2_ENABLE {0} \ - CONFIG.PCW_FPGA_FCLK3_ENABLE {0} \ - CONFIG.PCW_GPIO_MIO_GPIO_ENABLE {0} \ - CONFIG.PCW_GPIO_MIO_GPIO_IO {<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 {<Select>} \ - CONFIG.PCW_MIO_0_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_0_PULLUP {<Select>} \ - CONFIG.PCW_MIO_0_SLEW {<Select>} \ - CONFIG.PCW_MIO_10_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_10_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_10_PULLUP {<Select>} \ - CONFIG.PCW_MIO_10_SLEW {<Select>} \ - CONFIG.PCW_MIO_11_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_11_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_11_PULLUP {<Select>} \ - CONFIG.PCW_MIO_11_SLEW {<Select>} \ - CONFIG.PCW_MIO_12_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_12_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_12_PULLUP {<Select>} \ - CONFIG.PCW_MIO_12_SLEW {<Select>} \ - CONFIG.PCW_MIO_13_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_13_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_13_PULLUP {<Select>} \ - CONFIG.PCW_MIO_13_SLEW {<Select>} \ - CONFIG.PCW_MIO_14_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_14_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_14_PULLUP {<Select>} \ - CONFIG.PCW_MIO_14_SLEW {<Select>} \ - CONFIG.PCW_MIO_15_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_15_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_15_PULLUP {<Select>} \ - CONFIG.PCW_MIO_15_SLEW {<Select>} \ - CONFIG.PCW_MIO_16_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_16_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_16_PULLUP {<Select>} \ - CONFIG.PCW_MIO_16_SLEW {<Select>} \ - CONFIG.PCW_MIO_17_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_17_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_17_PULLUP {<Select>} \ - CONFIG.PCW_MIO_17_SLEW {<Select>} \ - CONFIG.PCW_MIO_18_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_18_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_18_PULLUP {<Select>} \ - CONFIG.PCW_MIO_18_SLEW {<Select>} \ - CONFIG.PCW_MIO_19_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_19_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_19_PULLUP {<Select>} \ - CONFIG.PCW_MIO_19_SLEW {<Select>} \ - 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 {<Select>} \ - CONFIG.PCW_MIO_20_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_20_PULLUP {<Select>} \ - CONFIG.PCW_MIO_20_SLEW {<Select>} \ - CONFIG.PCW_MIO_21_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_21_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_21_PULLUP {<Select>} \ - CONFIG.PCW_MIO_21_SLEW {<Select>} \ - CONFIG.PCW_MIO_22_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_22_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_22_PULLUP {<Select>} \ - CONFIG.PCW_MIO_22_SLEW {<Select>} \ - CONFIG.PCW_MIO_23_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_23_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_23_PULLUP {<Select>} \ - CONFIG.PCW_MIO_23_SLEW {<Select>} \ - CONFIG.PCW_MIO_24_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_24_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_24_PULLUP {<Select>} \ - CONFIG.PCW_MIO_24_SLEW {<Select>} \ - CONFIG.PCW_MIO_25_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_25_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_25_PULLUP {<Select>} \ - CONFIG.PCW_MIO_25_SLEW {<Select>} \ - CONFIG.PCW_MIO_26_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_26_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_26_PULLUP {<Select>} \ - CONFIG.PCW_MIO_26_SLEW {<Select>} \ - CONFIG.PCW_MIO_27_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_27_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_27_PULLUP {<Select>} \ - CONFIG.PCW_MIO_27_SLEW {<Select>} \ - CONFIG.PCW_MIO_28_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_28_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_28_PULLUP {<Select>} \ - CONFIG.PCW_MIO_28_SLEW {<Select>} \ - CONFIG.PCW_MIO_29_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_29_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_29_PULLUP {<Select>} \ - CONFIG.PCW_MIO_29_SLEW {<Select>} \ - 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 {<Select>} \ - CONFIG.PCW_MIO_30_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_30_PULLUP {<Select>} \ - CONFIG.PCW_MIO_30_SLEW {<Select>} \ - CONFIG.PCW_MIO_31_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_31_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_31_PULLUP {<Select>} \ - CONFIG.PCW_MIO_31_SLEW {<Select>} \ - CONFIG.PCW_MIO_32_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_32_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_32_PULLUP {<Select>} \ - CONFIG.PCW_MIO_32_SLEW {<Select>} \ - CONFIG.PCW_MIO_33_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_33_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_33_PULLUP {<Select>} \ - CONFIG.PCW_MIO_33_SLEW {<Select>} \ - CONFIG.PCW_MIO_34_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_34_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_34_PULLUP {<Select>} \ - CONFIG.PCW_MIO_34_SLEW {<Select>} \ - CONFIG.PCW_MIO_35_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_35_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_35_PULLUP {<Select>} \ - CONFIG.PCW_MIO_35_SLEW {<Select>} \ - CONFIG.PCW_MIO_36_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_36_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_36_PULLUP {<Select>} \ - CONFIG.PCW_MIO_36_SLEW {<Select>} \ - CONFIG.PCW_MIO_37_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_37_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_37_PULLUP {<Select>} \ - CONFIG.PCW_MIO_37_SLEW {<Select>} \ - CONFIG.PCW_MIO_38_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_38_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_38_PULLUP {<Select>} \ - CONFIG.PCW_MIO_38_SLEW {<Select>} \ - CONFIG.PCW_MIO_39_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_39_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_39_PULLUP {<Select>} \ - CONFIG.PCW_MIO_39_SLEW {<Select>} \ - 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 {<Select>} \ - CONFIG.PCW_MIO_46_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_46_PULLUP {<Select>} \ - CONFIG.PCW_MIO_46_SLEW {<Select>} \ - 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 {<Select>} \ - CONFIG.PCW_MIO_50_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_50_PULLUP {<Select>} \ - CONFIG.PCW_MIO_50_SLEW {<Select>} \ - CONFIG.PCW_MIO_51_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_51_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_51_PULLUP {<Select>} \ - CONFIG.PCW_MIO_51_SLEW {<Select>} \ - CONFIG.PCW_MIO_52_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_52_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_52_PULLUP {<Select>} \ - CONFIG.PCW_MIO_52_SLEW {<Select>} \ - CONFIG.PCW_MIO_53_DIRECTION {<Select>} \ - CONFIG.PCW_MIO_53_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_53_PULLUP {<Select>} \ - CONFIG.PCW_MIO_53_SLEW {<Select>} \ - 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 {<Select>} \ - CONFIG.PCW_MIO_7_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_7_PULLUP {<Select>} \ - CONFIG.PCW_MIO_7_SLEW {<Select>} \ - 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 {<Select>} \ - CONFIG.PCW_MIO_9_IOTYPE {<Select>} \ - CONFIG.PCW_MIO_9_PULLUP {<Select>} \ - CONFIG.PCW_MIO_9_SLEW {<Select>} \ - 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 {<Select>} \ - CONFIG.PCW_TTC_PERIPHERAL_FREQMHZ {50} \ - CONFIG.PCW_UART1_GRP_FULL_ENABLE {0} \ - CONFIG.PCW_UART1_PERIPHERAL_ENABLE {1} \ - CONFIG.PCW_UART1_UART1_IO {MIO 48 .. 49} \ - CONFIG.PCW_UART_PERIPHERAL_DIVISOR0 {16} \ - CONFIG.PCW_UART_PERIPHERAL_FREQMHZ {100} \ - CONFIG.PCW_UART_PERIPHERAL_VALID {1} \ - CONFIG.PCW_UIPARAM_ACT_DDR_FREQ_MHZ {525} \ - CONFIG.PCW_UIPARAM_DDR_BANK_ADDR_COUNT {3} \ - CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY0 {0.176} \ - CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY1 {0.159} \ - CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY2 {0.162} \ - CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY3 {0.187} \ - CONFIG.PCW_UIPARAM_DDR_CL {7} \ - CONFIG.PCW_UIPARAM_DDR_COL_ADDR_COUNT {10} \ - CONFIG.PCW_UIPARAM_DDR_CWL {6} \ - CONFIG.PCW_UIPARAM_DDR_DEVICE_CAPACITY {2048 MBits} \ - CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_0 {-0.073} \ - CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_1 {-0.034} \ - CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_2 {-0.03} \ - CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_3 {-0.082} \ - CONFIG.PCW_UIPARAM_DDR_DRAM_WIDTH {16 Bits} \ - CONFIG.PCW_UIPARAM_DDR_FREQ_MHZ {525} \ - CONFIG.PCW_UIPARAM_DDR_PARTNO {MT41K128M16 JT-125} \ - CONFIG.PCW_UIPARAM_DDR_ROW_ADDR_COUNT {14} \ - CONFIG.PCW_UIPARAM_DDR_SPEED_BIN {DDR3_1066F} \ - CONFIG.PCW_UIPARAM_DDR_TRAIN_DATA_EYE {1} \ - CONFIG.PCW_UIPARAM_DDR_TRAIN_READ_GATE {1} \ - CONFIG.PCW_UIPARAM_DDR_TRAIN_WRITE_LEVEL {1} \ - CONFIG.PCW_UIPARAM_DDR_T_FAW {40.0} \ - CONFIG.PCW_UIPARAM_DDR_T_RAS_MIN {35.0} \ - CONFIG.PCW_UIPARAM_DDR_T_RC {48.75} \ - CONFIG.PCW_UIPARAM_DDR_T_RCD {7} \ - CONFIG.PCW_UIPARAM_DDR_T_RP {7} \ - CONFIG.PCW_USB0_PERIPHERAL_ENABLE {0} \ - CONFIG.PCW_USB0_PERIPHERAL_FREQMHZ {60} \ - CONFIG.PCW_USB0_RESET_ENABLE {0} \ - CONFIG.PCW_USB0_RESET_IO {<Select>} \ - CONFIG.PCW_USB0_USB0_IO {<Select>} \ - CONFIG.PCW_USB1_RESET_ENABLE {0} \ - CONFIG.PCW_USB_RESET_ENABLE {0} \ - CONFIG.PCW_USB_RESET_SELECT {<Select>} \ - 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/run.py similarity index 100% rename from examples/vivado/vivado.py rename to examples/vivado/run.py diff --git a/examples/vivado/run.sh b/examples/vivado/run.sh new file mode 100644 index 00000000..f85dcc03 --- /dev/null +++ b/examples/vivado/run.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +ACTIONS=("make" "prog" "all") +SOURCES=("vlog" "vhdl" "slog" "design") + +for ACTION in "${ACTIONS[@]}"; do + for SOURCE in "${SOURCES[@]}"; do + python3 run.py --action $ACTION --source $SOURCE + done +done From 6c68cafcb668e2bf921b0dbc0abd2ec79a9fedde Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 24 Jun 2024 21:19:17 -0300 Subject: [PATCH 103/248] Added support for the Arty A7 35T in the Vivado example --- examples/sources/arty/a7-35t/clk.xdc | 2 ++ examples/sources/arty/a7-35t/led.xdc | 2 ++ examples/sources/arty/a7-35t/timing.xdc | 1 + examples/sources/zybo/{ => ZYBO}/clk.xdc | 0 examples/sources/zybo/{ => ZYBO}/led.xdc | 0 examples/sources/zybo/{ => ZYBO}/timing.xdc | 0 examples/vivado/run.py | 31 +++++++++++++++------ examples/vivado/run.sh | 12 ++++++-- 8 files changed, 36 insertions(+), 12 deletions(-) create mode 100644 examples/sources/arty/a7-35t/clk.xdc create mode 100644 examples/sources/arty/a7-35t/led.xdc create mode 100644 examples/sources/arty/a7-35t/timing.xdc rename examples/sources/zybo/{ => ZYBO}/clk.xdc (100%) rename examples/sources/zybo/{ => ZYBO}/led.xdc (100%) rename examples/sources/zybo/{ => ZYBO}/timing.xdc (100%) diff --git a/examples/sources/arty/a7-35t/clk.xdc b/examples/sources/arty/a7-35t/clk.xdc new file mode 100644 index 00000000..ee01dcb3 --- /dev/null +++ b/examples/sources/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/arty/a7-35t/led.xdc b/examples/sources/arty/a7-35t/led.xdc new file mode 100644 index 00000000..bf6d2304 --- /dev/null +++ b/examples/sources/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/arty/a7-35t/timing.xdc b/examples/sources/arty/a7-35t/timing.xdc new file mode 100644 index 00000000..4bfaa72f --- /dev/null +++ b/examples/sources/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/zybo/clk.xdc b/examples/sources/zybo/ZYBO/clk.xdc similarity index 100% rename from examples/sources/zybo/clk.xdc rename to examples/sources/zybo/ZYBO/clk.xdc diff --git a/examples/sources/zybo/led.xdc b/examples/sources/zybo/ZYBO/led.xdc similarity index 100% rename from examples/sources/zybo/led.xdc rename to examples/sources/zybo/ZYBO/led.xdc diff --git a/examples/sources/zybo/timing.xdc b/examples/sources/zybo/ZYBO/timing.xdc similarity index 100% rename from examples/sources/zybo/timing.xdc rename to examples/sources/zybo/ZYBO/timing.xdc diff --git a/examples/vivado/run.py b/examples/vivado/run.py index 99ec2745..2431466e 100644 --- a/examples/vivado/run.py +++ b/examples/vivado/run.py @@ -1,22 +1,37 @@ -"""Vivado VHDL example project.""" +"""Vivado examples.""" import argparse from pyfpga.vivado import Vivado + parser = argparse.ArgumentParser() parser.add_argument( - '--action', choices=['make', 'prog', 'all'], default='make' + '--board', choices=['zybo', 'arty'], default='zybo' ) parser.add_argument( - '--source', choices=['vlog', 'vhdl', 'slog', 'design'], default='vlog' + '--source', choices=['vlog', 'vhdl', 'slog'], default='vlog' +) +parser.add_argument( + '--action', choices=['make', 'prog', 'all'], default='make' ) args = parser.parse_args() -prj = Vivado(odir=f'../build/vivado-{args.source}') -prj.set_part('xc7z010-1-clg400') +prj = Vivado(odir=f'../build/vivado') + +if args.board == 'zybo': + prj.set_part('xc7z010-1-clg400') + prj.add_param('FREQ', '125000000') + prj.add_cons('../sources/zybo/ZYBO/timing.xdc', 'syn') + prj.add_cons('../sources/zybo/ZYBO/clk.xdc', 'par') + prj.add_cons('../sources/zybo/ZYBO/led.xdc', 'par') +if args.board == 'arty': + prj.set_part('xc7a35ticsg324-1L') + prj.add_param('FREQ', '100000000') + prj.add_cons('../sources/arty/a7-35t/timing.xdc', 'syn') + prj.add_cons('../sources/arty/a7-35t/clk.xdc', 'par') + prj.add_cons('../sources/arty/a7-35t/led.xdc', 'par') -prj.add_param('FREQ', '125000000') if args.source == 'vhdl': prj.add_vhdl('../sources/vhdl/*.vhdl', 'blink_lib') if args.source == 'vlog': @@ -27,9 +42,7 @@ prj.add_vlog('../sources/slog/*.sv') if args.source in ['vlog', 'slog']: prj.add_define('DEFINE', '1') -prj.add_cons('../sources/zybo/timing.xdc', 'syn') -prj.add_cons('../sources/zybo/clk.xdc', 'par') -prj.add_cons('../sources/zybo/led.xdc', 'par') + prj.set_top('Top') if args.action in ['make', 'all']: diff --git a/examples/vivado/run.sh b/examples/vivado/run.sh index f85dcc03..bd894b91 100644 --- a/examples/vivado/run.sh +++ b/examples/vivado/run.sh @@ -1,10 +1,16 @@ #!/bin/bash +set -e + +BOARDS=("zybo" "arty") +SOURCES=("vlog" "vhdl" "slog") ACTIONS=("make" "prog" "all") -SOURCES=("vlog" "vhdl" "slog" "design") -for ACTION in "${ACTIONS[@]}"; do +for BOARD in "${BOARDS[@]}"; do for SOURCE in "${SOURCES[@]}"; do - python3 run.py --action $ACTION --source $SOURCE + for ACTION in "${ACTIONS[@]}"; do + echo "> $BOARD - $SOURCE - $ACTION" + python3 run.py --board $BOARD --source $SOURCE --action $ACTION + done done done From c0b02c6680811185da203e470d4c7a5712565302 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Tue, 25 Jun 2024 22:08:00 -0300 Subject: [PATCH 104/248] Added initial (very early stage) support for Openflow --- examples/openflow/run.py | 37 ++++++ pyfpga/openflow.py | 76 +++++++++++-- pyfpga/templates/openflow.jinja | 194 ++++++-------------------------- 3 files changed, 141 insertions(+), 166 deletions(-) create mode 100644 examples/openflow/run.py diff --git a/examples/openflow/run.py b/examples/openflow/run.py new file mode 100644 index 00000000..8677a8a4 --- /dev/null +++ b/examples/openflow/run.py @@ -0,0 +1,37 @@ +"""Vivado 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' +) +args = parser.parse_args() + +prj = Openflow(odir='../build/openflow') + +prj.add_param('FREQ', '100000000') + +if args.source == 'vlog': + prj.add_include('../sources/vlog/include') + prj.add_vlog('../sources/vlog/*.v') +if args.source in ['vlog', 'slog']: + prj.add_define('DEFINE', '1') + +prj.set_top('Top') + +if args.action in ['make', 'all']: + prj.make() + +if args.action in ['prog', 'all']: + prj.prog() diff --git a/pyfpga/openflow.py b/pyfpga/openflow.py index 0b7865ba..2849b4ba 100644 --- a/pyfpga/openflow.py +++ b/pyfpga/openflow.py @@ -8,19 +8,81 @@ Implements support for an Open Source development flow. """ +# pylint: disable=too-many-locals +# pylint: disable=too-many-branches +# pylint: disable=duplicate-code + from pyfpga.project import Project class Openflow(Project): - """Class to support Openflow.""" - - def __init__(self, name='openflow', odir='results'): - super().__init__(name=name, odir=odir) - self.set_part('hx8k-ct256') + """Class to support Open Source tools.""" def _make_prepare(self, steps): - self.tool['make-app'] = 'docker' - self.tool['make-cmd'] = 'bash openflow.sh' + context = { + 'PROJECT': self.name or 'openflow', + 'PART': self.data.get('part', 'hx8k-ct256') + } + for step in steps: + context[step] = 1 + if 'includes' in self.data: + includes = [] + for include in self.data['includes']: + includes.append(f'-I{str(include)}') + context['INCLUDES'] = ' '.join(includes) + files = [] + if 'files' in self.data: + for file in self.data['files']: + files.append(f'read_verilog -defer {file}') + if files: + context['VLOGS'] = '\n'.join(files) +# for file in self.data['files']: +# if 'lib' in self.data['files'][file]: +# lib = self.data['files'][file]['lib'] +# files.append( +# f'set_property library {lib} [get_files {file}]' +# ) +# if 'constraints' in self.data: +# for file in self.data['constraints']: +# files.append(f'add_file -fileset constrs_1 {file}') +# for file in self.data['constraints']: +# if self.data['constraints'][file] == 'syn': +# prop = 'USED_IN_IMPLEMENTATION FALSE' +# if self.data['constraints'][file] == 'syn': +# prop = 'USED_IN_SYNTHESIS FALSE' +# if self.data['constraints'][file] != 'all': +# files.append(f'set_property {prop} [get_files {file}]') +# first = next(iter(self.data['constraints'])) +# prop = f'TARGET_CONSTRS_FILE {first}' +# files.append(f'set_property {prop} [current_fileset -constrset]') +# if files: +# context['FILES'] = '\n'.join(files) + if 'top' in self.data: + context['TOP'] = self.data['top'] + if 'defines' in self.data: + defines = [] + for key, value in self.data['defines'].items(): + defines.append(f'-D{key}={value}') + context['DEFINES'] = ' '.join(defines) + if 'params' in self.data: + params = [] + for key, value in self.data['params'].items(): + params.append(f'-set {key} {value}') + context['PARAMS'] = ' '.join(params) + if 'hooks' in self.data: + for stage in self.data['hooks']: + context[stage.upper()] = '\n'.join(self.data['hooks'][stage]) + self._create_file('openflow', 'sh', context) + return 'bash openflow.sh' + +# def _prog_prepare(self, bitstream, position): +# _ = position # Not needed for Vivado +# if not bitstream: +# basename = self.name or 'vivado' +# bitstream = Path(self.odir).resolve() / f'{basename}.bit' +# context = {'BITSTREAM': bitstream} +# self._create_file('vivado-prog', 'tcl', context) +# return 'vivado -mode batch -notrace -quiet -source vivado-prog.tcl' def _prog_prepare(self, bitstream, position): # binaries = ['bit'] diff --git a/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja index d3a0a703..6b4580c2 100644 --- a/pyfpga/templates/openflow.jinja +++ b/pyfpga/templates/openflow.jinja @@ -6,162 +6,38 @@ 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 par 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; - echo ">>> PyFPGA ($1): $2" -}} - -############################################################################### -# 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 == *"par"* ]]; then - -print "nextpnr-$FAMILY" "running 'place and route'" - -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 +DOCKER="docker run --rm -v $HOME:$HOME -w $PWD" + +{% if SYN %} +$DOCKER hdlc/ghdl:yosys /bin/bash -c " +{{ PRESYN }} +{{ VHDLS }} +yosys -Q -m ghdl -p ' +{% if INCLUDES %} +verilog_defaults -add {{ INCLUDES }} +{% endif %} +{% if DEFINES %} +verilog_defines {{ DEFINES }} +{% endif %} +{{ VLOGS }} +{{ SLOGS }} +{% if PARAMS %} +chparam {{ PARAMS }} {{ TOP }} +{% endif %} +synth -top {{ TOP }} +' +{{ POSTSYN }} +" +{% endif %} + +{% if PAR %} +{{ PREPAR }} + +{{ POSTPAR }} +{% endif %} + +{% if BIT %} +{{ PREBIT }} + +{{ POSTBIT }} +{% endif %} From aaef6c1b2b63369152bdbd51fa54c9e40490c6e3 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Tue, 25 Jun 2024 22:22:44 -0300 Subject: [PATCH 105/248] Removed resources submodule --- .gitmodules | 3 --- examples/resources | 1 - examples/sources/de10nano/clk.sdc | 1 + examples/sources/de10nano/clk.tcl | 1 + examples/sources/de10nano/led.tcl | 1 + examples/sources/ecp5evn/clk.lpf | 2 ++ examples/sources/ecp5evn/led.lpf | 2 ++ examples/sources/edu-ciaa/clk.pcf | 1 + examples/sources/edu-ciaa/led.pcf | 1 + examples/sources/icestick/clk.pcf | 1 + examples/sources/icestick/led.pcf | 1 + examples/sources/maker-board/clk.pdc | 1 + examples/sources/maker-board/clk.sdc | 1 + examples/sources/maker-board/led.pdc | 1 + examples/sources/nexys3/clk.ucf | 1 + examples/sources/nexys3/clk.xcf | 2 ++ examples/sources/nexys3/led.ucf | 1 + examples/sources/orangecrab/clk.lpf | 2 ++ examples/sources/orangecrab/led.lpf | 2 ++ examples/sources/s6micro/clk.ucf | 1 + examples/sources/s6micro/clk.xcf | 2 ++ examples/sources/s6micro/led.ucf | 1 + 22 files changed, 26 insertions(+), 4 deletions(-) delete mode 100644 .gitmodules delete mode 160000 examples/resources create mode 100644 examples/sources/de10nano/clk.sdc create mode 100644 examples/sources/de10nano/clk.tcl create mode 100644 examples/sources/de10nano/led.tcl create mode 100644 examples/sources/ecp5evn/clk.lpf create mode 100644 examples/sources/ecp5evn/led.lpf create mode 100644 examples/sources/edu-ciaa/clk.pcf create mode 100644 examples/sources/edu-ciaa/led.pcf create mode 100644 examples/sources/icestick/clk.pcf create mode 100644 examples/sources/icestick/led.pcf create mode 100644 examples/sources/maker-board/clk.pdc create mode 100644 examples/sources/maker-board/clk.sdc create mode 100644 examples/sources/maker-board/led.pdc create mode 100644 examples/sources/nexys3/clk.ucf create mode 100644 examples/sources/nexys3/clk.xcf create mode 100644 examples/sources/nexys3/led.ucf create mode 100644 examples/sources/orangecrab/clk.lpf create mode 100644 examples/sources/orangecrab/led.lpf create mode 100644 examples/sources/s6micro/clk.ucf create mode 100644 examples/sources/s6micro/clk.xcf create mode 100644 examples/sources/s6micro/led.ucf diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index cba0ef31..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "examples/resources"] - path = examples/resources - url = git@github.com:PyFPGA/resources.git diff --git a/examples/resources b/examples/resources deleted file mode 160000 index 7f34b14e..00000000 --- a/examples/resources +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7f34b14e2cdcbcbd6f57053a033a2abdd3ff1900 diff --git a/examples/sources/de10nano/clk.sdc b/examples/sources/de10nano/clk.sdc new file mode 100644 index 00000000..00363616 --- /dev/null +++ b/examples/sources/de10nano/clk.sdc @@ -0,0 +1 @@ +create_clock -period 20 [get_ports clk_i] diff --git a/examples/sources/de10nano/clk.tcl b/examples/sources/de10nano/clk.tcl new file mode 100644 index 00000000..2d72bdd4 --- /dev/null +++ b/examples/sources/de10nano/clk.tcl @@ -0,0 +1 @@ +set_location_assignment PIN_V11 -to clk_i diff --git a/examples/sources/de10nano/led.tcl b/examples/sources/de10nano/led.tcl new file mode 100644 index 00000000..4159ac36 --- /dev/null +++ b/examples/sources/de10nano/led.tcl @@ -0,0 +1 @@ +set_location_assignment PIN_W15 -to led_o diff --git a/examples/sources/ecp5evn/clk.lpf b/examples/sources/ecp5evn/clk.lpf new file mode 100644 index 00000000..c6c70332 --- /dev/null +++ b/examples/sources/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/ecp5evn/led.lpf b/examples/sources/ecp5evn/led.lpf new file mode 100644 index 00000000..529c0b89 --- /dev/null +++ b/examples/sources/ecp5evn/led.lpf @@ -0,0 +1,2 @@ +LOCATE COMP "led_o" SITE "A13"; +IOBUF PORT "led_o" IO_TYPE=LVCMOS25; diff --git a/examples/sources/edu-ciaa/clk.pcf b/examples/sources/edu-ciaa/clk.pcf new file mode 100644 index 00000000..cfd62a35 --- /dev/null +++ b/examples/sources/edu-ciaa/clk.pcf @@ -0,0 +1 @@ +set_io clk_i 94 diff --git a/examples/sources/edu-ciaa/led.pcf b/examples/sources/edu-ciaa/led.pcf new file mode 100644 index 00000000..92e61517 --- /dev/null +++ b/examples/sources/edu-ciaa/led.pcf @@ -0,0 +1 @@ +set_io led_o 4 diff --git a/examples/sources/icestick/clk.pcf b/examples/sources/icestick/clk.pcf new file mode 100644 index 00000000..8b246ac7 --- /dev/null +++ b/examples/sources/icestick/clk.pcf @@ -0,0 +1 @@ +set_io clk_i 21 diff --git a/examples/sources/icestick/led.pcf b/examples/sources/icestick/led.pcf new file mode 100644 index 00000000..9a56ae6f --- /dev/null +++ b/examples/sources/icestick/led.pcf @@ -0,0 +1 @@ +set_io led_o 95 diff --git a/examples/sources/maker-board/clk.pdc b/examples/sources/maker-board/clk.pdc new file mode 100644 index 00000000..c0070c73 --- /dev/null +++ b/examples/sources/maker-board/clk.pdc @@ -0,0 +1 @@ +set_io clk_i -DIRECTION INPUT -pinname 23 -fixed yes diff --git a/examples/sources/maker-board/clk.sdc b/examples/sources/maker-board/clk.sdc new file mode 100644 index 00000000..00363616 --- /dev/null +++ b/examples/sources/maker-board/clk.sdc @@ -0,0 +1 @@ +create_clock -period 20 [get_ports clk_i] diff --git a/examples/sources/maker-board/led.pdc b/examples/sources/maker-board/led.pdc new file mode 100644 index 00000000..703bd95a --- /dev/null +++ b/examples/sources/maker-board/led.pdc @@ -0,0 +1 @@ +set_io led_o -DIRECTION OUTPUT -pinname 117 -fixed yes diff --git a/examples/sources/nexys3/clk.ucf b/examples/sources/nexys3/clk.ucf new file mode 100644 index 00000000..c0230e2d --- /dev/null +++ b/examples/sources/nexys3/clk.ucf @@ -0,0 +1 @@ +NET "clk_i" LOC = "V10"; diff --git a/examples/sources/nexys3/clk.xcf b/examples/sources/nexys3/clk.xcf new file mode 100644 index 00000000..a200fdeb --- /dev/null +++ b/examples/sources/nexys3/clk.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/nexys3/led.ucf b/examples/sources/nexys3/led.ucf new file mode 100644 index 00000000..c210831b --- /dev/null +++ b/examples/sources/nexys3/led.ucf @@ -0,0 +1 @@ +NET "led_o" LOC = "U16"; diff --git a/examples/sources/orangecrab/clk.lpf b/examples/sources/orangecrab/clk.lpf new file mode 100644 index 00000000..ed801503 --- /dev/null +++ b/examples/sources/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/orangecrab/led.lpf b/examples/sources/orangecrab/led.lpf new file mode 100644 index 00000000..cad54d2b --- /dev/null +++ b/examples/sources/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/s6micro/clk.ucf b/examples/sources/s6micro/clk.ucf new file mode 100644 index 00000000..c0230e2d --- /dev/null +++ b/examples/sources/s6micro/clk.ucf @@ -0,0 +1 @@ +NET "clk_i" LOC = "V10"; diff --git a/examples/sources/s6micro/clk.xcf b/examples/sources/s6micro/clk.xcf new file mode 100644 index 00000000..2f24ee45 --- /dev/null +++ b/examples/sources/s6micro/clk.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/s6micro/led.ucf b/examples/sources/s6micro/led.ucf new file mode 100644 index 00000000..c557e8c5 --- /dev/null +++ b/examples/sources/s6micro/led.ucf @@ -0,0 +1 @@ +NET "led_o" LOC = "C2"; From ed239f9526d88ffeccdeaf7f15dd1fc9b993f3bb Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Tue, 25 Jun 2024 23:57:28 -0300 Subject: [PATCH 106/248] Modified HDL to depends on more than one param/include/define --- examples/openflow/run.py | 9 ++-- examples/sources/slog/blink.sv | 5 ++- examples/sources/slog/include/header.svh | 1 - examples/sources/slog/include1/header1.svh | 1 + examples/sources/slog/include2/header2.svh | 1 + examples/sources/slog/top.sv | 49 ++++++++++++++++++---- examples/sources/vhdl/blink.vhdl | 5 ++- examples/sources/vhdl/blink_pkg.vhdl | 3 +- examples/sources/vhdl/top.vhdl | 8 ++-- examples/sources/vlog/blink.v | 5 ++- examples/sources/vlog/include/header.vh | 1 - examples/sources/vlog/include1/header1.vh | 1 + examples/sources/vlog/include2/header2.vh | 1 + examples/sources/vlog/top.v | 49 ++++++++++++++++++---- examples/vivado/run.py | 10 +++-- 15 files changed, 113 insertions(+), 36 deletions(-) delete mode 100644 examples/sources/slog/include/header.svh create mode 100644 examples/sources/slog/include1/header1.svh create mode 100644 examples/sources/slog/include2/header2.svh delete mode 100644 examples/sources/vlog/include/header.vh create mode 100644 examples/sources/vlog/include1/header1.vh create mode 100644 examples/sources/vlog/include2/header2.vh diff --git a/examples/openflow/run.py b/examples/openflow/run.py index 8677a8a4..d591b195 100644 --- a/examples/openflow/run.py +++ b/examples/openflow/run.py @@ -1,4 +1,4 @@ -"""Vivado examples.""" +"""Openflow examples.""" import argparse @@ -21,12 +21,15 @@ prj = Openflow(odir='../build/openflow') prj.add_param('FREQ', '100000000') +prj.add_param('SECS', '1') if args.source == 'vlog': - prj.add_include('../sources/vlog/include') + prj.add_include('../sources/vlog/include1') + prj.add_include('../sources/vlog/include2') prj.add_vlog('../sources/vlog/*.v') if args.source in ['vlog', 'slog']: - prj.add_define('DEFINE', '1') + prj.add_define('DEFINE1', '1') + prj.add_define('DEFINE2', '1') prj.set_top('Top') diff --git a/examples/sources/slog/blink.sv b/examples/sources/slog/blink.sv index b74bfe08..4e2679e9 100644 --- a/examples/sources/slog/blink.sv +++ b/examples/sources/slog/blink.sv @@ -1,11 +1,12 @@ module Blink #( - parameter int FREQ = 25000000 + parameter int FREQ = 25000000, + parameter int SECS = 1 )( input clk_i, output led_o ); - localparam int DIV = FREQ; + localparam int DIV = FREQ*SECS; logic led = 0; logic [$clog2(DIV)-1:0] cnt = 0; diff --git a/examples/sources/slog/include/header.svh b/examples/sources/slog/include/header.svh deleted file mode 100644 index 78aa9bfd..00000000 --- a/examples/sources/slog/include/header.svh +++ /dev/null @@ -1 +0,0 @@ -`define INCLUDE 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 index 24e780e5..4665011a 100644 --- a/examples/sources/slog/top.sv +++ b/examples/sources/slog/top.sv @@ -1,23 +1,54 @@ -`include "header.svh" +`include "header1.svh" +`include "header2.svh" module Top #( - parameter FREQ = 0 + parameter int FREQ = 0, + parameter int SECS = 0 )( input clk_i, output led_o ); initial begin - if (FREQ==0) begin - $stop("FREQ must be greater than 0"); - $error("FREQ must be greater than 0"); - $fatal("FREQ must be greater than 0"); + if (!FREQ) begin + $stop("FREQ not set"); + $error("FREQ not set"); + $fatal("FREQ not set"); end + if (!SECS) begin + $stop("SECS not set"); + $error("SECS not set"); + $fatal("SECS not set"); + end + `ifndef INCLUDE1 + $stop("INCLUDE1 not defined"); + $error("INCLUDE1 not defined"); + $fatal("INCLUDE1 not defined"); + `endif + `ifndef INCLUDE2 + $stop("INCLUDE2 not defined"); + $error("INCLUDE2 not defined"); + $fatal("INCLUDE2 not defined"); + `endif + `ifndef DEFINE1 + $stop("DEFINE1 not defined"); + $error("DEFINE1 not defined"); + $fatal("DEFINE1 not defined"); + `endif + `ifndef DEFINE2 + $stop("DEFINE2 not defined"); + $error("DEFINE2 not defined"); + $fatal("DEFINE2 not defined"); + `endif end -`ifdef INCLUDE -`ifdef DEFINE - Blink #(.FREQ (FREQ)) dut (.clk_i (clk_i), .led_o (led_o)); +`ifdef INCLUDE1 +`ifdef INCLUDE2 +`ifdef DEFINE1 +`ifdef DEFINE2 + Blink #(.FREQ (FREQ), .SECS (SECS)) dut (.clk_i (clk_i), .led_o (led_o)); +`endif +`endif `endif `endif diff --git a/examples/sources/vhdl/blink.vhdl b/examples/sources/vhdl/blink.vhdl index 9b9a4bc3..156f553c 100644 --- a/examples/sources/vhdl/blink.vhdl +++ b/examples/sources/vhdl/blink.vhdl @@ -3,7 +3,8 @@ use IEEE.std_logic_1164.all; entity Blink is generic ( - FREQ : positive := 25e6 + FREQ : positive := 25e6; + SECS : positive := 1 ); port ( clk_i : in std_logic; @@ -12,7 +13,7 @@ entity Blink is end entity Blink; architecture RTL of Blink is - constant DIV : positive := FREQ; + constant DIV : positive := FREQ*SECS; signal led : std_logic := '0'; signal cnt : natural range 0 to DIV-1 := 0; begin diff --git a/examples/sources/vhdl/blink_pkg.vhdl b/examples/sources/vhdl/blink_pkg.vhdl index 115f0899..3f6ee86a 100644 --- a/examples/sources/vhdl/blink_pkg.vhdl +++ b/examples/sources/vhdl/blink_pkg.vhdl @@ -5,7 +5,8 @@ package blink_pkg is component Blink is generic ( - FREQ : natural:=25e6 + FREQ : positive := 25e6; + SECS : positive := 1 ); port ( clk_i : in std_logic; diff --git a/examples/sources/vhdl/top.vhdl b/examples/sources/vhdl/top.vhdl index 0ff6d780..24977d6c 100644 --- a/examples/sources/vhdl/top.vhdl +++ b/examples/sources/vhdl/top.vhdl @@ -5,7 +5,8 @@ use blink_lib.blink_pkg.all; entity Top is generic ( - FREQ : natural := 0 + FREQ : natural := 0; + SECS : natural := 0 ); port ( clk_i : in std_logic; @@ -16,10 +17,11 @@ end entity Top; architecture ARCH of Top is begin - assert FREQ > 0 report "FREQ must be greater than 0" severity failure; + assert FREQ > 0 report "FREQ not set" severity failure; + assert SECS > 0 report "SECS not set" severity failure; blink_i: Blink - generic map (FREQ => FREQ) + generic map (FREQ => FREQ, SECS => SECS) port map (clk_i => clk_i, led_o => led_o); end architecture ARCH; diff --git a/examples/sources/vlog/blink.v b/examples/sources/vlog/blink.v index 7522e0f7..120b8c9f 100644 --- a/examples/sources/vlog/blink.v +++ b/examples/sources/vlog/blink.v @@ -1,11 +1,12 @@ module Blink #( - parameter FREQ = 25000000 + parameter FREQ = 25000000, + parameter SECS = 1 )( input clk_i, output led_o ); - localparam DIV = FREQ; + localparam DIV = FREQ*SECS; reg led = 0; reg [$clog2(DIV)-1:0] cnt = 0; diff --git a/examples/sources/vlog/include/header.vh b/examples/sources/vlog/include/header.vh deleted file mode 100644 index 78aa9bfd..00000000 --- a/examples/sources/vlog/include/header.vh +++ /dev/null @@ -1 +0,0 @@ -`define INCLUDE 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 index 6c63ebae..fb6dff0b 100644 --- a/examples/sources/vlog/top.v +++ b/examples/sources/vlog/top.v @@ -1,23 +1,54 @@ -`include "header.vh" +`include "header1.vh" +`include "header2.vh" module Top #( - parameter FREQ = 0 + parameter FREQ = 0, + parameter SECS = 0 )( input clk_i, output led_o ); initial begin - if (FREQ==0) begin - $stop("FREQ must be greater than 0"); - $error("FREQ must be greater than 0"); - $fatal("FREQ must be greater than 0"); + if (!FREQ) begin + $stop("FREQ not set"); + $error("FREQ not set"); + $fatal("FREQ not set"); end + if (!SECS) begin + $stop("SECS not set"); + $error("SECS not set"); + $fatal("SECS not set"); + end + `ifndef INCLUDE1 + $stop("INCLUDE1 not defined"); + $error("INCLUDE1 not defined"); + $fatal("INCLUDE1 not defined"); + `endif + `ifndef INCLUDE2 + $stop("INCLUDE2 not defined"); + $error("INCLUDE2 not defined"); + $fatal("INCLUDE2 not defined"); + `endif + `ifndef DEFINE1 + $stop("DEFINE1 not defined"); + $error("DEFINE1 not defined"); + $fatal("DEFINE1 not defined"); + `endif + `ifndef DEFINE2 + $stop("DEFINE2 not defined"); + $error("DEFINE2 not defined"); + $fatal("DEFINE2 not defined"); + `endif end -`ifdef INCLUDE -`ifdef DEFINE - Blink #(.FREQ (FREQ)) dut (.clk_i (clk_i), .led_o (led_o)); +`ifdef INCLUDE1 +`ifdef INCLUDE2 +`ifdef DEFINE1 +`ifdef DEFINE2 + Blink #(.FREQ (FREQ), .SECS (SECS)) dut (.clk_i (clk_i), .led_o (led_o)); +`endif +`endif `endif `endif diff --git a/examples/vivado/run.py b/examples/vivado/run.py index 2431466e..cf110c2f 100644 --- a/examples/vivado/run.py +++ b/examples/vivado/run.py @@ -31,17 +31,21 @@ prj.add_cons('../sources/arty/a7-35t/timing.xdc', 'syn') prj.add_cons('../sources/arty/a7-35t/clk.xdc', 'par') prj.add_cons('../sources/arty/a7-35t/led.xdc', 'par') +prj.add_param('SECS', '1') if args.source == 'vhdl': prj.add_vhdl('../sources/vhdl/*.vhdl', 'blink_lib') if args.source == 'vlog': - prj.add_include('../sources/vlog/include') + 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/include') + 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('DEFINE', '1') + prj.add_define('DEFINE1', '1') + prj.add_define('DEFINE2', '1') prj.set_top('Top') From 12ab0a46edf30dac01bee4916675b37e00d11d5d Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 26 Jun 2024 00:16:30 -0300 Subject: [PATCH 107/248] Small fix --- examples/vivado/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/vivado/run.py b/examples/vivado/run.py index cf110c2f..8ae97d6f 100644 --- a/examples/vivado/run.py +++ b/examples/vivado/run.py @@ -17,7 +17,7 @@ ) args = parser.parse_args() -prj = Vivado(odir=f'../build/vivado') +prj = Vivado(odir='../build/vivado') if args.board == 'zybo': prj.set_part('xc7z010-1-clg400') From e61cf5b979197350c8d3e509ea29b4a304e83543 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 26 Jun 2024 00:17:07 -0300 Subject: [PATCH 108/248] Simplified Openflow example content --- examples/openflow/Makefile | 7 ---- examples/openflow/ecp5evn.lpf | 5 --- examples/openflow/edu-ciaa-fpga.pcf | 2 -- examples/openflow/icestick.pcf | 2 -- examples/openflow/icestorm.py | 50 --------------------------- examples/openflow/orangecrab_r0.2.lpf | 5 --- examples/openflow/prjtrellis.py | 50 --------------------------- examples/openflow/run.py | 27 +++++++++++++++ examples/openflow/run.sh | 16 +++++++++ 9 files changed, 43 insertions(+), 121 deletions(-) delete mode 100644 examples/openflow/Makefile delete mode 100644 examples/openflow/ecp5evn.lpf delete mode 100644 examples/openflow/edu-ciaa-fpga.pcf delete mode 100644 examples/openflow/icestick.pcf delete mode 100644 examples/openflow/icestorm.py delete mode 100644 examples/openflow/orangecrab_r0.2.lpf delete mode 100644 examples/openflow/prjtrellis.py create mode 100644 examples/openflow/run.sh 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/edu-ciaa-fpga.pcf b/examples/openflow/edu-ciaa-fpga.pcf deleted file mode 100644 index 17fb1134..00000000 --- a/examples/openflow/edu-ciaa-fpga.pcf +++ /dev/null @@ -1,2 +0,0 @@ -set_io clk_i 94 -set_io led_o 4 diff --git a/examples/openflow/icestick.pcf b/examples/openflow/icestick.pcf deleted file mode 100644 index caa6e413..00000000 --- a/examples/openflow/icestick.pcf +++ /dev/null @@ -1,2 +0,0 @@ -set_io clk_i 21 -set_io led_o 95 diff --git a/examples/openflow/icestorm.py b/examples/openflow/icestorm.py deleted file mode 100644 index a71bec78..00000000 --- a/examples/openflow/icestorm.py +++ /dev/null @@ -1,50 +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_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' - 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']: - prj.generate() - -if args.action in ['transfer', 'all']: - prj.transfer() 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 bece88a9..00000000 --- a/examples/openflow/prjtrellis.py +++ /dev/null @@ -1,50 +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_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' - 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']: - prj.generate() - -if args.action in ['transfer', 'all']: - prj.transfer() diff --git a/examples/openflow/run.py b/examples/openflow/run.py index d591b195..b01153c3 100644 --- a/examples/openflow/run.py +++ b/examples/openflow/run.py @@ -20,13 +20,40 @@ prj = Openflow(odir='../build/openflow') +if args.board == 'icestick': + prj.set_part('hx1k-tq144') + prj.add_param('FREQ', '100000000') + prj.add_cons('../sources/icestick/clk.pcf', 'par') + prj.add_cons('../sources/icestick/led.pcf', 'par') +if args.board == 'edu-ciaa': + prj.set_part('hx1k-tq144') + prj.add_param('FREQ', '100000000') + prj.add_cons('../sources/edu-ciaa/clk.pcf', 'par') + prj.add_cons('../sources/edu-ciaa/led.pcf', 'par') +if args.board == 'orangecrab': + prj.set_part('25k-CSFBGA285') + prj.add_param('FREQ', '100000000') + prj.add_cons('../sources/orangecrab/clk.lpf', 'par') + prj.add_cons('../sources/orangecrab/led.lpf', 'par') +if args.board == 'ecp5evn': + prj.set_part('um5g-85k-CABGA381') + prj.add_param('FREQ', '100000000') + prj.add_cons('../sources/ecp5evn/clk.lpf', 'par') + prj.add_cons('../sources/ecp5evn/led.lpf', 'par') + prj.add_param('FREQ', '100000000') prj.add_param('SECS', '1') +if args.source == 'vhdl': + prj.add_vhdl('../sources/vhdl/*.vhdl', 'blink_lib') 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') diff --git a/examples/openflow/run.sh b/examples/openflow/run.sh new file mode 100644 index 00000000..4e213f40 --- /dev/null +++ b/examples/openflow/run.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e + +BOARDS=("icestick" "edu-ciaa" "orangecrab" "ecp5evn") +SOURCES=("vlog" "vhdl" "slog") +ACTIONS=("make" "prog" "all") + +for BOARD in "${BOARDS[@]}"; do + for SOURCE in "${SOURCES[@]}"; do + for ACTION in "${ACTIONS[@]}"; do + echo "> $BOARD - $SOURCE - $ACTION" + python3 run.py --board $BOARD --source $SOURCE --action $ACTION + done + done +done From b52435e41b0d7bed910c2c0344697519db57972a Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 26 Jun 2024 00:29:30 -0300 Subject: [PATCH 109/248] Removed deprecated files --- examples/ghdl/Makefile | 6 ------ examples/ghdl/ghdl.py | 17 ----------------- examples/hooks/Makefile | 6 ------ examples/ise/Makefile | 7 ------- examples/ise/nexys3.ucf | 7 ------- examples/ise/nexys3.xcf | 6 ------ examples/ise/s6micro.ucf | 7 ------- examples/ise/s6micro.xcf | 6 ------ examples/libero/Makefile | 6 ------ examples/libero/mkr.pdc | 4 ---- examples/libero/mkr.sdc | 6 ------ examples/misc/Makefile | 6 ------ examples/misc/capture.py | 23 ----------------------- examples/misc/logger.py | 25 ------------------------- examples/multi/Makefile | 6 ------ examples/quartus/Makefile | 6 ------ examples/quartus/de10nano.sdc | 6 ------ examples/quartus/de10nano.tcl | 2 -- examples/yosys/Makefile | 6 ------ examples/yosys/README.md | 26 -------------------------- examples/yosys/yosys.py | 31 ------------------------------- 21 files changed, 215 deletions(-) delete mode 100644 examples/ghdl/Makefile delete mode 100644 examples/ghdl/ghdl.py delete mode 100644 examples/hooks/Makefile delete mode 100644 examples/ise/Makefile delete mode 100644 examples/ise/nexys3.ucf delete mode 100644 examples/ise/nexys3.xcf delete mode 100644 examples/ise/s6micro.ucf delete mode 100644 examples/ise/s6micro.xcf delete mode 100644 examples/libero/Makefile delete mode 100644 examples/libero/mkr.pdc delete mode 100644 examples/libero/mkr.sdc delete mode 100644 examples/misc/Makefile delete mode 100644 examples/misc/capture.py delete mode 100644 examples/misc/logger.py delete mode 100644 examples/multi/Makefile delete mode 100644 examples/quartus/Makefile delete mode 100644 examples/quartus/de10nano.sdc delete mode 100644 examples/quartus/de10nano.tcl delete mode 100644 examples/yosys/Makefile delete mode 100644 examples/yosys/README.md delete mode 100644 examples/yosys/yosys.py diff --git a/examples/ghdl/Makefile b/examples/ghdl/Makefile deleted file mode 100644 index f166a9f9..00000000 --- a/examples/ghdl/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/make - -SCRIPTS = $(wildcard *.py) - -all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT) || exit;) diff --git a/examples/ghdl/ghdl.py b/examples/ghdl/ghdl.py deleted file mode 100644 index da2ef275..00000000 --- a/examples/ghdl/ghdl.py +++ /dev/null @@ -1,17 +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') - -prj.generate() diff --git a/examples/hooks/Makefile b/examples/hooks/Makefile deleted file mode 100644 index f166a9f9..00000000 --- a/examples/hooks/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/make - -SCRIPTS = $(wildcard *.py) - -all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT) || exit;) 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/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 f166a9f9..00000000 --- a/examples/libero/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/make - -SCRIPTS = $(wildcard *.py) - -all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT) || exit;) 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 f166a9f9..00000000 --- a/examples/misc/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/make - -SCRIPTS = $(wildcard *.py) - -all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT) || exit;) diff --git a/examples/misc/capture.py b/examples/misc/capture.py deleted file mode 100644 index 4284b212..00000000 --- a/examples/misc/capture.py +++ /dev/null @@ -1,23 +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') - -output = PRJ.generate(to_task='syn', capture=True) -print(output) - -output = PRJ.transfer(devtype='detect', capture=True) -print(output) diff --git a/examples/misc/logger.py b/examples/misc/logger.py deleted file mode 100644 index 7f34ca1c..00000000 --- a/examples/misc/logger.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -This script demonstrates how to utilize the logging functionality within the -pyfpga package. The following steps are covered: - -1. Creating an instance of the Project class. -2. Testing logging with the default INFO level. -3. Setting the logging level to DEBUG to capture more detailed information. -4. Disabling logging by removing all handlers. - -Usage: -- By default, the logger captures messages with level INFO and higher. -- To see more detailed debug information, set the logger level to DEBUG. -- To disable logging, remove all handlers from the logger. -""" - -import logging - -from pyfpga.project import Project - -prj = Project() -prj.set_part('EXAMPLE') -prj.logger.setLevel(logging.DEBUG) -prj.set_part('EXAMPLE') -prj.logger.handlers = [] -prj.set_part('EXAMPLE') diff --git a/examples/multi/Makefile b/examples/multi/Makefile deleted file mode 100644 index f166a9f9..00000000 --- a/examples/multi/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/make - -SCRIPTS = $(wildcard *.py) - -all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT) || exit;) diff --git a/examples/quartus/Makefile b/examples/quartus/Makefile deleted file mode 100644 index f166a9f9..00000000 --- a/examples/quartus/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/make - -SCRIPTS = $(wildcard *.py) - -all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT) || exit;) 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/de10nano.tcl b/examples/quartus/de10nano.tcl deleted file mode 100644 index 19494e68..00000000 --- a/examples/quartus/de10nano.tcl +++ /dev/null @@ -1,2 +0,0 @@ -set_location_assignment PIN_V11 -to clk_i -set_location_assignment PIN_W15 -to led_o diff --git a/examples/yosys/Makefile b/examples/yosys/Makefile deleted file mode 100644 index f07825c7..00000000 --- a/examples/yosys/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/make - -SCRIPTS = $(wildcard *.py) - -all: - @$(foreach SCRIPT, $(SCRIPTS), python3 $(SCRIPT) || exit; python3 $(SCRIPT) --lang vhdl || exit; ) 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 3553d515..00000000 --- a/examples/yosys/yosys.py +++ /dev/null @@ -1,31 +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_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' - 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.generate() From 548d7ab4a9a4e972d1c8e1b30260dd9c8430f695 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 27 Jun 2024 22:58:09 -0300 Subject: [PATCH 110/248] Implemented par and bit for Openflow --- pyfpga/openflow.py | 120 +++++--------------------------- pyfpga/templates/openflow.jinja | 67 +++++++++++++++++- 2 files changed, 83 insertions(+), 104 deletions(-) diff --git a/pyfpga/openflow.py b/pyfpga/openflow.py index 2849b4ba..10321b4c 100644 --- a/pyfpga/openflow.py +++ b/pyfpga/openflow.py @@ -12,6 +12,7 @@ # pylint: disable=too-many-branches # pylint: disable=duplicate-code +from pathlib import Path from pyfpga.project import Project @@ -21,7 +22,10 @@ class Openflow(Project): def _make_prepare(self, steps): context = { 'PROJECT': self.name or 'openflow', - 'PART': self.data.get('part', 'hx8k-ct256') + 'PART': self.data.get('part', 'hx8k-ct256'), + 'FAMILY': 'ice40', + 'DEVICE': 'hx8k', + 'PACKAGE': 'tq144:4k' } for step in steps: context[step] = 1 @@ -42,21 +46,11 @@ def _make_prepare(self, steps): # files.append( # f'set_property library {lib} [get_files {file}]' # ) -# if 'constraints' in self.data: -# for file in self.data['constraints']: -# files.append(f'add_file -fileset constrs_1 {file}') -# for file in self.data['constraints']: -# if self.data['constraints'][file] == 'syn': -# prop = 'USED_IN_IMPLEMENTATION FALSE' -# if self.data['constraints'][file] == 'syn': -# prop = 'USED_IN_SYNTHESIS FALSE' -# if self.data['constraints'][file] != 'all': -# files.append(f'set_property {prop} [get_files {file}]') -# first = next(iter(self.data['constraints'])) -# prop = f'TARGET_CONSTRS_FILE {first}' -# files.append(f'set_property {prop} [current_fileset -constrset]') -# if files: -# context['FILES'] = '\n'.join(files) + if 'constraints' in self.data: + constraints = [] + for constraint in self.data['constraints']: + constraints.append(str(constraint)) + context['CONSTRAINTS'] = ' '.join(constraints) if 'top' in self.data: context['TOP'] = self.data['top'] if 'defines' in self.data: @@ -75,57 +69,14 @@ def _make_prepare(self, steps): self._create_file('openflow', 'sh', context) return 'bash openflow.sh' -# def _prog_prepare(self, bitstream, position): -# _ = position # Not needed for Vivado -# if not bitstream: -# basename = self.name or 'vivado' -# bitstream = Path(self.odir).resolve() / f'{basename}.bit' -# context = {'BITSTREAM': bitstream} -# self._create_file('vivado-prog', 'tcl', context) -# return 'vivado -mode batch -notrace -quiet -source vivado-prog.tcl' - def _prog_prepare(self, bitstream, position): - # binaries = ['bit'] - self.tool['prog-app'] = 'docker' - self.tool['prog-cmd'] = 'bash openflow-prog.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) + _ = position + if not bitstream: + basename = self.name or 'openflow' + bitstream = Path(self.odir).resolve() / f'{basename}.bit' + context = {'BITSTREAM': bitstream} + self._create_file('openflow-prog', 'sh', context) + return 'bash openflow-prog.sh' # def set_part(self, part): # self.part['name'] = part @@ -172,43 +123,6 @@ def _prog_prepare(self, bitstream, position): # 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: diff --git a/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja index 6b4580c2..971149be 100644 --- a/pyfpga/templates/openflow.jinja +++ b/pyfpga/templates/openflow.jinja @@ -6,7 +6,9 @@ set -e -DOCKER="docker run --rm -v $HOME:$HOME -w $PWD" +DOCKER="docker run --user $(id -u):$(id -g) --rm -v $HOME:$HOME -w $PWD" + +# Synthesis ------------------------------------------------------------------- {% if SYN %} $DOCKER hdlc/ghdl:yosys /bin/bash -c " @@ -25,19 +27,82 @@ verilog_defines {{ DEFINES }} chparam {{ PARAMS }} {{ TOP }} {% endif %} synth -top {{ TOP }} +synth_{{ FAMILY }} -top {{ TOP }} -json {{ PROJECT }}.json ' {{ POSTSYN }} " {% endif %} +#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 + +# Place and Route ------------------------------------------------------------- + {% if PAR %} + +CONSTRAINTS="{{ CONSTRAINTS }}" + +{% if FAMILY == 'ice40' %} +if [ -n "$CONSTRAINTS" ]; then + cat $CONSTRAINTS > constraints.pcf + CONSTRAINT="--pcf constraints.pcf" +fi +$DOCKER hdlc/nextpnr:ice40 /bin/bash -c " {{ PREPAR }} +nextpnr-ice40 --{{ DEVICE }} --package {{ PACKAGE }} $CONSTRAINT --json {{ PROJECT }}.json --asc {{ PROJECT }}.asc +{{ POSTPAR }} +" +$DOCKER hdlc/icestorm /bin/bash -c " +icetime -d {{ DEVICE }} -mtr {{ PROJECT }}.rpt {{ PROJECT }}.asc +" +{% endif %} +{% if FAMILY == 'ecp5' %} +if [ -n "$CONSTRAINTS" ]; then + cat $CONSTRAINTS > constraints.lpf + CONSTRAINT="--lpf constraints.lpf" +fi +$DOCKER hdlc/nextpnr:ecp5 /bin/bash -c " +{{ PREPAR }} +nextpnr-ecp5 --{{ DEVICE }} --package {{ PACKAGE }} $CONSTRAINT --json {{ PROJECT }}.json --textcfg {{ PROJECT }}.config {{ POSTPAR }} +" {% endif %} +{% endif %} + +# Bitstream ------------------------------------------------------------------- + {% if BIT %} + +{% if FAMILY == 'ice40' %} +$DOCKER hdlc/icestorm /bin/bash -c " {{ PREBIT }} +icepack {{ PROJECT }}.asc {{ PROJECT }}.bit +{{ POSTBIT }} +" +{% endif %} +{% if FAMILY == 'ecp5' %} +$DOCKER hdlc/icestorm /bin/bash -c " +{{ PREBIT }} +ecppack --svf {{ PROJECT }}.svf {{ PROJECT }}.config {{ PROJECT }}.bit {{ POSTBIT }} +" +{% endif %} + {% endif %} From 69ae00b474feaa58abd78ad6f16c5910323e460f Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 27 Jun 2024 23:22:29 -0300 Subject: [PATCH 111/248] Prepared template for Openflow programming --- pyfpga/templates/openflow-prog.jinja | 37 ++++++---------------------- 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/pyfpga/templates/openflow-prog.jinja b/pyfpga/templates/openflow-prog.jinja index 027eb388..fc1c1105 100644 --- a/pyfpga/templates/openflow-prog.jinja +++ b/pyfpga/templates/openflow-prog.jinja @@ -6,35 +6,12 @@ set -e -############################################################################### -# Things to tuneup -############################################################################### +DOCKER="docker run --user $(id -u):$(id -g) --rm -v $HOME:$HOME -w $PWD" -FAMILY={family} -PROJECT={project} +{% if FAMILY == 'ice40' %} +$DOCKER --device /dev/bus/usb hdlc/prog iceprog {{ PROJECT }}.bit +{% endif %} -# -# 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 +{% 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 {{ PROJECT }}.svf; exit" +{% endif %} From 1d1c157276f12aba111aabc53d932113760bb28e Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 28 Jun 2024 20:33:14 -0300 Subject: [PATCH 112/248] Added get_info to obtain extra information based on the PART --- pyfpga/ise.py | 103 ++++++++++++++++++++--------------- pyfpga/libero.py | 99 ++++++++++++++++++--------------- pyfpga/openflow.py | 133 ++++++++++++++++++--------------------------- tests/test_part.py | 53 +++++++++++------- 4 files changed, 201 insertions(+), 187 deletions(-) diff --git a/pyfpga/ise.py b/pyfpga/ise.py index d5289930..22dab08f 100644 --- a/pyfpga/ise.py +++ b/pyfpga/ise.py @@ -8,7 +8,11 @@ Implements support for ISE. """ -# import re +# pylint: disable=too-many-locals +# pylint: disable=too-many-branches +# pylint: disable=duplicate-code + +import re from pyfpga.project import Project @@ -31,23 +35,6 @@ def _prog_prepare(self, bitstream, position): # _DEVTYPES = ['fpga', 'spi', 'bpi', 'detect', 'unlock'] -# 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 transfer(self, devtype, position, part, width, capture): # super().transfer(devtype, position, part, width, capture) # temp = _TEMPLATES[devtype] @@ -60,29 +47,57 @@ def _prog_prepare(self, bitstream, position): # 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' + +def get_info(part): + """Get info about the FPGA part. + + :param part: the FPGA part as specified by the tool + :returns: a dictionary with the keys family, device, speed and package + """ + part = part.lower() + # 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 device, package and speed + device = None + speed = None + package = None + aux = part.split('-') + if len(aux) == 3: + device = aux[0] + if len(aux[1]) < len(aux[2]): + speed = aux[1] + package = aux[2] + else: + speed = aux[2] + package = aux[1] + else: + raise ValueError( + 'Part must be DEVICE-SPEED-PACKAGE or DEVICE-PACKAGE-SPEED' + ) + # Finish + return { + 'family': family, 'device': device, 'speed': speed, 'package': package + } diff --git a/pyfpga/libero.py b/pyfpga/libero.py index a717f1b7..7f807906 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -8,7 +8,11 @@ Implements support for Libero. """ -# import re +# pylint: disable=too-many-locals +# pylint: disable=too-many-branches +# pylint: disable=duplicate-code + +import re from pyfpga.project import Project @@ -29,47 +33,56 @@ def _prog_prepare(self, bitstream, position): self.tool['prog-app'] = '' self.tool['prog-cmd'] = '' -# 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_info(part): + """Get info about the FPGA part. -# 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' + :param part: the FPGA part as specified by the tool + :returns: a dictionary with the keys family, device, speed and package + """ + part = part.lower() + # Looking for the family + family = None + 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): + family = value + break + # Looking for the device and package + device = None + speed = None + package = None + aux = part.split('-') + if len(aux) == 2: + device = aux[0] + speed = 'STD' + package = aux[1] + elif len(aux) == 3: + device = aux[0] + if len(aux[1]) < len(aux[2]): + speed = aux[1] + package = aux[2] + else: + speed = aux[2] + package = aux[1] + else: + raise ValueError( + 'Part must be DEVICE-SPEED-PACKAGE or DEVICE-PACKAGE' + ) + # Finish + return { + 'family': family, 'device': device, 'speed': speed, 'package': package + } diff --git a/pyfpga/openflow.py b/pyfpga/openflow.py index 10321b4c..8b40b259 100644 --- a/pyfpga/openflow.py +++ b/pyfpga/openflow.py @@ -20,12 +20,12 @@ class Openflow(Project): """Class to support Open Source tools.""" def _make_prepare(self, steps): + info = get_info(self.data.get('part', 'hx8k-ct256')) context = { 'PROJECT': self.name or 'openflow', - 'PART': self.data.get('part', 'hx8k-ct256'), - 'FAMILY': 'ice40', - 'DEVICE': 'hx8k', - 'PACKAGE': 'tq144:4k' + 'FAMILY': info['family'], + 'DEVICE': info['device'], + 'PACKAGE': info['package'] } for step in steps: context[step] = 1 @@ -78,29 +78,7 @@ def _prog_prepare(self, bitstream, position): self._create_file('openflow-prog', 'sh', context) return 'bash openflow-prog.sh' -# 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 = [] @@ -119,59 +97,56 @@ def _prog_prepare(self, bitstream, position): # 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}') -# 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_info(part): + """Get info about the FPGA part. -# def get_family(part): -# """Get the Family name from the specified part name.""" -# part = part.lower() -# families = [ -# # From <YOSYS>/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 <nextpnr>/ice40/main.cc -# 'lp384', 'lp1k', 'lp4k', 'lp8k', 'hx1k', 'hx4k', 'hx8k', -# 'up3k', 'up5k', 'u1k', 'u2k', 'u4k' -# ] -# if part.startswith(tuple(families)): -# return 'ice40' -# families = [ -# # From <nextpnr>/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' + :param part: the FPGA part as specified by the tool + :returns: a dictionary with the keys family, device and package + """ + part = part.lower() + # Looking for the family + family = None + families = [ + # From <YOSYS>/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 <nextpnr>/ice40/main.cc + 'lp384', 'lp1k', 'lp4k', 'lp8k', 'hx1k', 'hx4k', 'hx8k', + 'up3k', 'up5k', 'u1k', 'u2k', 'u4k' + ] + if part.startswith(tuple(families)): + family = 'ice40' + families = [ + # From <nextpnr>/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 device and package + 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: + raise ValueError('Part must be DEVICE-PACKAGE') + 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/tests/test_part.py b/tests/test_part.py index 5cee3e71..7fd24ec7 100644 --- a/tests/test_part.py +++ b/tests/test_part.py @@ -1,27 +1,38 @@ -from pyfpga.project import Project +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 get_part(prj): -# return prj.get_configs()['part'].lower() +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 -# 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(): + info = { + 'family': 'SmartFusion2', + 'device': 'm2s010', + 'speed': '1', + 'package': 'tq144' + } + assert get_info_libero('m2s010-1-tq144') == info + assert get_info_libero('m2s010-tq144-1') == info + info['speed'] = 'STD' + assert get_info_libero('m2s010-tq144') == info -# 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" +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 From 7031a189d0b0432b0002f4f6783bb97c5b97d729 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 29 Jun 2024 00:54:19 -0300 Subject: [PATCH 113/248] Re-added ISE support --- examples/ise/ise.py | 46 ---------------- examples/ise/run.py | 50 +++++++++++++++++ pyfpga/ise.py | 65 ++++++++++++++++++---- pyfpga/templates/ise.jinja | 108 ++++++++++++++++--------------------- 4 files changed, 151 insertions(+), 118 deletions(-) delete mode 100644 examples/ise/ise.py create mode 100644 examples/ise/run.py diff --git a/examples/ise/ise.py b/examples/ise/ise.py deleted file mode 100644 index c4f90b41..00000000 --- a/examples/ise/ise.py +++ /dev/null @@ -1,46 +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']: - prj.generate() - -if args.action in ['transfer', 'all']: - prj.transfer('fpga') - # prj.transfer('detect') - # prj.transfer('unlock') - # prj.transfer('spi', 1, 'N25Q128', 4) diff --git a/examples/ise/run.py b/examples/ise/run.py new file mode 100644 index 00000000..93a35b13 --- /dev/null +++ b/examples/ise/run.py @@ -0,0 +1,50 @@ +"""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' +) +args = parser.parse_args() + +prj = Ise(odir='../build/ise') + +if args.board == 's6micro': + prj.set_part('xc6slx9-2-csg324') + prj.add_param('FREQ', '125000000') + prj.add_cons('../sources/s6micro/clk.xcf', 'syn') + prj.add_cons('../sources/s6micro/clk.ucf', 'par') + prj.add_cons('../sources/s6micro/led.ucf', 'par') +if args.board == 'nexys3': + prj.set_part('xc6slx16-3-csg32') + prj.add_param('FREQ', '100000000') + prj.add_cons('../sources/nexys3/clk.xcf', 'syn') + prj.add_cons('../sources/nexys3/clk.ucf', 'par') + prj.add_cons('../sources/nexys3/led.ucf', 'par') +prj.add_param('SECS', '1') + +if args.source == 'vhdl': + prj.add_vhdl('../sources/vhdl/*.vhdl', 'blink_lib') +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') + +if args.action in ['make', 'all']: + prj.make() + +if args.action in ['prog', 'all']: + prj.prog() diff --git a/pyfpga/ise.py b/pyfpga/ise.py index 22dab08f..1777539b 100644 --- a/pyfpga/ise.py +++ b/pyfpga/ise.py @@ -20,18 +20,65 @@ class Ise(Project): """Class to support ISE projects.""" - def __init__(self, name='ise', odir='results'): - super().__init__(name=name, odir=odir) - self.set_part('xc7k160t-3-fbg484') - def _make_prepare(self, steps): - self.tool['make-app'] = 'xtclsh' - self.tool['make-cmd'] = 'xtclsh ise.tcl' + info = get_info(self.data.get('part', 'xc7k160t-3-fbg484')) + context = { + 'PROJECT': self.name or 'ise', + 'FAMILY': info['family'], + 'DEVICE': info['device'], + 'SPEED': info['speed'], + 'PACKAGE': info['package'] + } + for step in steps: + context[step] = 1 + if 'includes' in self.data: + includes = [] + for include in self.data['includes']: + includes.append(str(include)) + context['INCLUDES'] = '|'.join(includes) + files = [] + if 'files' in self.data: + for file in self.data['files']: + if 'lib' in self.data['files']: + lib = self.data['files'][file]['lib'] + files.append(f'lib_vhdl new {lib}') + files.append(f'xfile add {file} -lib_vhdl {lib}') + else: + files.append(f'xfile add {file}') + if 'constraints' in self.data: + for file in self.data['constraints']: + files.append(f'xfile add {file}') + if files: + context['FILES'] = '\n'.join(files) + if 'top' in self.data: + context['TOP'] = self.data['top'] + if 'defines' in self.data: + defines = [] + for key, value in self.data['defines'].items(): + defines.append(f'{key}={value}') + context['DEFINES'] = ' | '.join(defines) + if 'params' in self.data: + params = [] + for key, value in self.data['params'].items(): + params.append(f'{key}={value}') + context['PARAMS'] = ' '.join(params) + if 'hooks' in self.data: + for stage in self.data['hooks']: + context[stage.upper()] = '\n'.join(self.data['hooks'][stage]) + self._create_file('ise', 'tcl', context) + return 'xtclsh ise.tcl' def _prog_prepare(self, bitstream, position): - # binaries = ['bit'] - self.tool['prog-app'] = 'impact' - self.tool['prog-cmd'] = 'impact -batch impact-prog' + if not bitstream: + basename = self.name or 'ise' + bitstream = Path(self.odir).resolve() / f'{basename}.bit' + context = {'BITSTREAM': bitstream, 'POSITION': position} + self._create_file('vivado-prog', 'tcl', context) + return 'impact -batch impact-prog' + + def add_slog(self, pathname): + """Add System Verilog file/s.""" + raise NotImplementedError('ISE does not support SystemVerilog') # _DEVTYPES = ['fpga', 'spi', 'bpi', 'detect', 'unlock'] diff --git a/pyfpga/templates/ise.jinja b/pyfpga/templates/ise.jinja index c70efad6..79b3f9de 100644 --- a/pyfpga/templates/ise.jinja +++ b/pyfpga/templates/ise.jinja @@ -4,66 +4,49 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -set PROJECT {{ PROJECT }} -set PART {{ PART }} -set FAMILY {{ FAMILY }} -set DEVICE {{ DEVICE }} -set PACKAGE {{ PACKAGE }} -set SPEED {{ SPEED }} -set TOP {{ TOP }} - -set PARAMS [list {{ PARAMS }}] - -proc fpga_file {FILE {LIBRARY "work"}} { - set message "adding the file '$FILE'" - if { $LIBRARY != "work" } { append message " (into the VHDL library '$LIBRARY')" } - regexp -nocase {\.(\w*)$} $FILE -> ext - if { $ext == "tcl" } { - source $FILE - return - } - 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 - } -} - -proc fpga_include {PATH} { - lappend INCLUDED $PATH - # Verilog Included Files are NOT added - project set "Verilog Include Directories" \ - [join $INCLUDED "|"] -process "Synthesize - XST" -} - -proc fpga_params {} { - if { [llength $PARAMS] == 0 } { return } - set assigns [list] - foreach PARAM $PARAMS { lappend assigns [join $PARAM "="] } - project set "Generics, Parameters" "[join $assigns]" -process "Synthesize - XST" -} - -#--[ Project configuration ]--------------------------------------------------- +# 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 +# } +#} + +#--[ Project configuration ]-------------------------------------------------- {% if CFG %} -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 { [ 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 }} {{ PRECFG }} {{ FILES }} +{% if CONSTRAINTS %} +project set "Synthesis Constraints File" "{{ CONSTRAINTS }}" -process "Synthesize - XST" +{% endif %} -project set top $TOP +{% if TOP %} +project set top {{ TOP }} +{% endif %} -fpga_params +{% if DEFINES %} +project set "Verilog Macros" "{{ DEFINES }}" -process "Synthesize - XST" +{% endif %} + +{% if INCLUDES %} +project set "Verilog Include Directories" "{{ INCLUDES }}" -process "Synthesize - XST" +# [join $INCLUDED "|"] +{% endif %} + +{% if PARAMS %} +project set "Generics, Parameters" "{{ PARAMS }}" -process "Synthesize - XST" +{% endif %} {{ POSTCFG }} @@ -73,18 +56,16 @@ project close #--[ Design flow ]------------------------------------------------------------- {% if SYN or PAR or BIT %} -project open $PROJECT.xise +project open {{ PROJECT }}.xise {% if SYN %} {{ PRESYN }} -{% if PRESYNTH %} -project set top_level_module_type "EDIF" -{% else %} +# PRESYNTH +#project set top_level_module_type "EDIF" project clean process run "Synthesize" -if { [process get "Synthesize" status] == "errors" } { exit 2 } -{% endif %} +if { [process get "Synthesize" status] == "errors" } { exit 1 } {{ POSTSYN }} {% endif %} @@ -93,11 +74,11 @@ if { [process get "Synthesize" status] == "errors" } { exit 2 } {{ PREPAR }} process run "Translate" -if { [process get "Translate" status] == "errors" } { exit 2 } +if { [process get "Translate" status] == "errors" } { exit 1 } process run "Map" -if { [process get "Map" status] == "errors" } { exit 2 } +if { [process get "Map" status] == "errors" } { exit 1 } process run "Place & Route" -if { [process get "Place & Route" status] == "errors" } { exit 2 } +if { [process get "Place & Route" status] == "errors" } { exit 1 } {{ POSTPAR }} {% endif %} @@ -106,8 +87,9 @@ if { [process get "Place & Route" status] == "errors" } { exit 2 } {{ PREBIT }} process run "Generate Programming File" -if { [process get "Generate Programming File" status] == "errors" } { exit 2 } -catch { file rename -force $TOP.bit $PROJECT.bit } +if { [process get "Generate Programming File" status] == "errors" } { exit 1 } +# catch { file rename -force {{ TOP }}.bit {{ PROJECT }}.bit } +file rename -force {{ TOP }}.bit {{ PROJECT }}.bit {{ POSTBIT }} {% endif %} From f0044941e1c7535d9e93c7bc159e3ed8f7bcd2e4 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 29 Jun 2024 14:00:55 -0300 Subject: [PATCH 114/248] Fixed to include ISE XST constraints --- pyfpga/ise.py | 5 +++++ pyfpga/templates/ise.jinja | 10 ---------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/pyfpga/ise.py b/pyfpga/ise.py index 1777539b..b3c86739 100644 --- a/pyfpga/ise.py +++ b/pyfpga/ise.py @@ -46,8 +46,13 @@ def _make_prepare(self, steps): else: files.append(f'xfile add {file}') if 'constraints' in self.data: + constraints = [] for file in self.data['constraints']: files.append(f'xfile add {file}') + if file.suffix == '.xcf': + constraints.append(str(file)) + if constraints: + context['CONSTRAINTS'] = " ".join(constraints) if files: context['FILES'] = '\n'.join(files) if 'top' in self.data: diff --git a/pyfpga/templates/ise.jinja b/pyfpga/templates/ise.jinja index 79b3f9de..024e0edb 100644 --- a/pyfpga/templates/ise.jinja +++ b/pyfpga/templates/ise.jinja @@ -4,16 +4,6 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -# 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 -# } -#} - #--[ Project configuration ]-------------------------------------------------- {% if CFG %} From 349af14700764a5f3a5956b787f2c4f19c185da3 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 29 Jun 2024 15:13:05 -0300 Subject: [PATCH 115/248] Modified to have different log files for make and prog --- pyfpga/project.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index faaacffe..d33dbd93 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -228,7 +228,7 @@ def make(self, first='cfg', last='bit'): message = steps[first] self.logger.info('Running %s', message) selected = [step.upper() for step in keys[index[0]:index[1]+1]] - self._run(self._make_prepare(selected)) + self._run(self._make_prepare(selected), 'make.log') def prog(self, bitstream=None, position=1): """Program the FPGA @@ -244,7 +244,7 @@ def prog(self, bitstream=None, position=1): if position not in range(1, 9): raise ValueError('Invalid position.') self.logger.info('Programming') - self._run(self._prog_prepare(bitstream, position)) + self._run(self._prog_prepare(bitstream, position), 'prog.log') def _make_prepare(self, steps): raise NotImplementedError('Tool-dependent') @@ -264,7 +264,7 @@ def _create_file(self, basename, extension, context): with open(directory / filename, 'w', encoding='utf-8') as file: file.write(content) - def _run(self, command): + def _run(self, command, logname): num = 20 error = 0 old_dir = Path.cwd() @@ -272,13 +272,13 @@ def _run(self, command): start = time() try: os.chdir(new_dir) - with open('run.log', 'w', encoding='utf-8') as file: + 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('run.log', 'r', encoding='utf-8') as file: + 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: From 47cb94286a5f6189ddcf71d876cdeb41e9fd3371 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 29 Jun 2024 15:25:34 -0300 Subject: [PATCH 116/248] Small fix on quartus-prog and prepared ise-prog --- pyfpga/templates/ise-prog.jinja | 56 +++++++++++++---------------- pyfpga/templates/quartus-prog.jinja | 2 +- 2 files changed, 25 insertions(+), 33 deletions(-) diff --git a/pyfpga/templates/ise-prog.jinja b/pyfpga/templates/ise-prog.jinja index 4c18a072..2ba0c193 100644 --- a/pyfpga/templates/ise-prog.jinja +++ b/pyfpga/templates/ise-prog.jinja @@ -4,57 +4,49 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -_TEMPLATES = { - 'fpga': """setMode -bs +cleancablelock + +{% if FPGA %} +setMode -bs setCable -port auto Identify -inferir -assignFile -p #POSITION# -file #BITSTREAM# -Program -p #POSITION# +assignFile -p {{ POSITION }} -file {{ BITSTREAM }} +Program -p {{ POSITION }} +{% endif %} -quit -""", - 'spi': """setMode -pff -addConfigDevice -name #NAME# -path . +{% if SPI %} +setMode -pff +addConfigDevice -name {{ NAME }} -path . setSubmode -pffspi addDesign -version 0 -name 0 addDeviceChain -index 0 -addDevice -p 1 -file #BITSTREAM# +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 +attachflash -position {{ POSITION }} -spi {{ NAME }} +assignfiletoattachedflash -position {{ POSITION }} -file ./{{ NAME }}.mcs +Program -p {{ POSITION }} -dataWidth {{ WIDTH }} -spionly -e -v -loadfpga +{% endif %} -quit -""", - 'bpi': """setMode -pff -addConfigDevice -name #NAME# -path . +{% 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# +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 +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 -""", - 'detect': """setMode -bs -setCable -port auto -Identify -inferir -quit -""", - 'unlock': """cleancablelock -quit -""" -} diff --git a/pyfpga/templates/quartus-prog.jinja b/pyfpga/templates/quartus-prog.jinja index 45ee765b..d754fee5 100644 --- a/pyfpga/templates/quartus-prog.jinja +++ b/pyfpga/templates/quartus-prog.jinja @@ -4,4 +4,4 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -{{ COMMAND }} -c %s --mode jtag -o "p;{{ BITSTREAM }}@{{ POSITION }}" +quartus_pgm -c {{ CABLE }} --mode jtag -o "p;{{ BITSTREAM }}@{{ POSITION }}" From 7e13dc7767a88351a5782b84e0ecd88b2155c813 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 29 Jun 2024 15:27:55 -0300 Subject: [PATCH 117/248] Added missing import of Path --- pyfpga/ise.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyfpga/ise.py b/pyfpga/ise.py index b3c86739..370c80ce 100644 --- a/pyfpga/ise.py +++ b/pyfpga/ise.py @@ -14,6 +14,7 @@ import re +from pathlib import Path from pyfpga.project import Project From f2fa17f73b887af724051adb8dc9779455146a7d Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 29 Jun 2024 16:11:42 -0300 Subject: [PATCH 118/248] Added to raise an exception for Libero programming --- pyfpga/libero.py | 4 +--- pyfpga/templates/libero-prog.jinja | 7 ------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/pyfpga/libero.py b/pyfpga/libero.py index 7f807906..5cf8efe4 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -29,9 +29,7 @@ def _make_prepare(self, steps): self.tool['make-cmd'] = 'libero SCRIPT:libero.tcl' def _prog_prepare(self, bitstream, position): - # binaries = ['bit'] - self.tool['prog-app'] = '' - self.tool['prog-cmd'] = '' + raise NotImplementedError('Libero programming not supported') def get_info(part): diff --git a/pyfpga/templates/libero-prog.jinja b/pyfpga/templates/libero-prog.jinja index ed9effe2..95b740f0 100644 --- a/pyfpga/templates/libero-prog.jinja +++ b/pyfpga/templates/libero-prog.jinja @@ -4,13 +4,6 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -_TEMPLATES = { - 'fpga': """\ -""", - 'detect': """\ -""" -} - # open_project -file {$TEMPDIR/libero.prjx} # run_tool -name {CONFIGURE_CHAIN} -script {$TEMPDIR/flashpro.tcl} # run_tool -name {PROGRAMDEVICE} From fa66cfac6355b968e72c23e9c7601835afebbd0d Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 29 Jun 2024 18:17:47 -0300 Subject: [PATCH 119/248] Re-added Quartus support (WIP) --- examples/ise/run.py | 1 + examples/quartus/quartus.py | 32 -------------- examples/quartus/run.py | 50 +++++++++++++++++++++ pyfpga/quartus.py | 71 +++++++++++++++++++++++++---- pyfpga/templates/quartus.jinja | 81 +++++++++------------------------- 5 files changed, 135 insertions(+), 100 deletions(-) delete mode 100644 examples/quartus/quartus.py create mode 100644 examples/quartus/run.py diff --git a/examples/ise/run.py b/examples/ise/run.py index 93a35b13..3bd89df9 100644 --- a/examples/ise/run.py +++ b/examples/ise/run.py @@ -4,6 +4,7 @@ from pyfpga.ise import Ise + parser = argparse.ArgumentParser() parser.add_argument( '--board', choices=['s6micro', 'nexys3'], default='s6micro' diff --git a/examples/quartus/quartus.py b/examples/quartus/quartus.py deleted file mode 100644 index 6516fcd0..00000000 --- a/examples/quartus/quartus.py +++ /dev/null @@ -1,32 +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']: - prj.generate() - -if args.action in ['transfer', 'all']: - prj.transfer('fpga', 2) diff --git a/examples/quartus/run.py b/examples/quartus/run.py new file mode 100644 index 00000000..0453ac8e --- /dev/null +++ b/examples/quartus/run.py @@ -0,0 +1,50 @@ +"""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' +) +args = parser.parse_args() + +prj = Quartus(odir='../build/quartus') + +if args.board == 'de10nano': + prj.set_part('5CSEBA6U23I7') + prj.add_param('FREQ', '125000000') + prj.add_cons('../sources/de10nano/clk.sdc', 'syn') + prj.add_cons('../sources/de10nano/clk.tcl', 'par') + prj.add_cons('../sources/de10nano/led.tcl', 'par') +prj.add_param('SECS', '1') + +if args.source == 'vhdl': + prj.add_vhdl('../sources/vhdl/*.vhdl', 'blink_lib') +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') + +if args.action in ['make', 'all']: + prj.make() + +if args.action in ['prog', 'all']: + prj.prog() diff --git a/pyfpga/quartus.py b/pyfpga/quartus.py index 4ca2bf78..fda50647 100644 --- a/pyfpga/quartus.py +++ b/pyfpga/quartus.py @@ -8,7 +8,9 @@ Implements support for Quartus. """ -# import re +# pylint: disable=too-many-locals +# pylint: disable=too-many-branches +# pylint: disable=duplicate-code from pyfpga.project import Project @@ -16,18 +18,69 @@ class Quartus(Project): """Class to support Quartus projects.""" - def __init__(self, name='quartus', odir='results'): - super().__init__(name=name, odir=odir) - self.set_part('10cl120zf780i8g') - def _make_prepare(self, steps): - self.tool['make-app'] = 'quartus_sh' - self.tool['make-cmd'] = 'quartus_sh --script quartus.tcl' + context = { + 'PROJECT': self.name or 'quartus', + 'PART': self.data.get('part', '10cl120zf780i8g') + } + for step in steps: + context[step] = 1 + if 'includes' in self.data: + includes = [] + for include in self.data['includes']: + includes.append(str(include)) + context['INCLUDES'] = ' '.join(includes) + files = [] + if 'files' in self.data: + types = { + 'slog': 'SYSTEMVERILOG_FILE', + 'vhdl': 'VHDL_FILE', + 'vlog': 'VERILOG_FILE' + } + for file in self.data['files']: + hdl = self.data['files'][file]['hdl'] + lib = self.data['files'][file].get('lib', None) + typ = types[hdl] + line = f'set_global_assignment -name {typ} {file}' + if lib: + line += f' -library {lib}' + files.append(line) + if 'constraints' in self.data: + for file in self.data['constraints']: + if file.suffix == '.sdc': + line = f'set_global_assignment -name SDC_FILE {file}' + else: + line = f'source {file}' + files.append(line) + if files: + context['FILES'] = '\n'.join(files) + if 'top' in self.data: + context['TOP'] = self.data['top'] + if 'defines' in self.data: + defines = [] + for key, value in self.data['defines'].items(): + defines.append(f'{key} {value}') + context['DEFINES'] = ' '.join(defines) + if 'params' in self.data: + params = [] + for key, value in self.data['params'].items(): + params.append(f'{key} {value}') + context['PARAMS'] = ' '.join(params) + if 'hooks' in self.data: + for stage in self.data['hooks']: + context[stage.upper()] = '\n'.join(self.data['hooks'][stage]) + self._create_file('quartus', 'tcl', context) + return 'quartus_sh --script quartus.tcl' def _prog_prepare(self, bitstream, position): # binaries = ['sof', 'pof'] - self.tool['prog-app'] = 'quartus_pgm' - self.tool['prog-cmd'] = 'bash quartus-prog.sh' + _ = position # Not needed for Vivado + # if not bitstream: + # basename = self.name or 'quartus' + # bitstream = Path(self.odir).resolve() / f'{basename}.bit' + context = {'BITSTREAM': bitstream} + self._create_file('quartus-prog', 'tcl', context) + return 'bash quartus-prog.sh' # def transfer(self, devtype, position, part, width, capture): # super().transfer(devtype, position, part, width, capture) diff --git a/pyfpga/templates/quartus.jinja b/pyfpga/templates/quartus.jinja index cf660a8d..4b98174a 100644 --- a/pyfpga/templates/quartus.jinja +++ b/pyfpga/templates/quartus.jinja @@ -4,72 +4,35 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -set PROJECT {{ PROJECT }} -set PART {{ PART }} -set FAMILY {{ FAMILY }} -set DEVICE {{ DEVICE }} -set PACKAGE {{ PACKAGE }} -set SPEED {{ SPEED }} -set TOP {{ TOP }} - -set PARAMS [list {{ PARAMS }}] - -proc fpga_file {FILE {LIBRARY "work"}} { - set message "adding the file '$FILE'" - if { $LIBRARY != "work" } { append message " (into the VHDL library '$LIBRARY')" } - regexp -nocase {\.(\w*)$} $FILE -> ext - if { $ext == "tcl" } { - source $FILE - return - } - 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 - } -} - -proc fpga_include {PATH} { - lappend INCLUDED $PATH - # Verilog Included Files are NOT added - foreach INCLUDE $INCLUDED { - set_global_assignment -name SEARCH_PATH $INCLUDE - } -} - -proc fpga_params {} { - if { [llength $PARAMS] == 0 } { return } - foreach PARAM $PARAMS { - eval "set_parameter -name $PARAM" - } -} - #--[ Project configuration ]--------------------------------------------------- {% if CFG %} package require ::quartus::project -project_new $PROJECT -overwrite - -set_global_assignment -name DEVICE $PART +project_new {{ PROJECT }} -overwrite +set_global_assignment -name DEVICE {{ PART }} {{ PRECFG }} -fpga_files +{{ FILES }} + +{% if TOP %} +set_global_assignment -name TOP_LEVEL_ENTITY {{ TOP }} +{% endif %} + +{% if DEFINES %} +set defines [dict create {{ DEFINES }}] +foreach {key value} $defines {set_global_assignment -name VERILOG_MACRO "$key=$value"} +{% endif %} -set_global_assignment -name TOP_LEVEL_ENTITY $TOP +{% if INCLUDES %} +set includes { {{ INCLUDES}} } +foreach value $includes {set_global_assignment -name SEARCH_PATH $value} +{% endif %} -fpga_params +{% if PARAMS %} +set params [dict create {{ PARAMS }}] +foreach {key value} $params {set_parameter -name $key $value} +{% endif %} {{ POSTCFG }} @@ -80,8 +43,8 @@ project_close {% if SYN or PAR or BIT %} package require ::quartus::flow -project_open -force $PROJECT.qpf -set_global_assignment -name NUM_PARALLEL_PROCESSORS ALL +project_open -force {{ PROJECT }}.qpf +# set_global_assignment -name NUM_PARALLEL_PROCESSORS ALL {% if SYN %} {{ PRESYN }} From 2b34189694b5a5107e3379b8d9198542008337c5 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 29 Jun 2024 20:54:48 -0300 Subject: [PATCH 120/248] Fix duplicate logging issue when creating new instances --- pyfpga/project.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index d33dbd93..b9192c03 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -36,14 +36,15 @@ def __init__(self, name=None, odir='results'): self.odir = odir # logging config self.logger = logging.getLogger(self.__class__.__name__) - 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) + 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. From c8841d5ddd933e15f98d9363806b7ad0c3893cbf Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 29 Jun 2024 20:58:53 -0300 Subject: [PATCH 121/248] Add a test to verify support of the specified tool --- tests/projects/support.py | 126 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 tests/projects/support.py diff --git a/tests/projects/support.py b/tests/projects/support.py new file mode 100644 index 00000000..3b529f14 --- /dev/null +++ b/tests/projects/support.py @@ -0,0 +1,126 @@ +"""Vivado examples.""" + +import sys + +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 + + +classes = { + 'ise': Ise, + 'libero': Libero, + 'openflow': Openflow, + 'quartus': Quartus, + 'vivado': Vivado +} + +tool = sys.argv[1] if len(sys.argv) > 1 else 'openflow' + +Class = classes.get(tool) + +if Class is None: + sys.exit('Unsupported tool') + +print(f'* Class Under Test: {Class.__name__}') + +try: + print('* Verilog Includes') + prj = Class() + 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() + sys.exit('* FAIL') +except SystemExit: + raise +except: + print('* PASS') + +try: + print('* Verilog Defines') + prj = Class() + 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() + sys.exit('* FAIL') +except SystemExit: + raise +except: + print('* PASS') + +try: + print('* Verilog Parameters') + prj = Class() + 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() + sys.exit('* FAIL') +except SystemExit: + raise +except: + print('* PASS') + +print('* Verilog Support') +prj = Class() +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() +print('* PASS') + +if tool not in ['ise', 'openflow']: + print('* System Verilog Support') + prj = Class() + prj.add_vlog('../../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() + print('* PASS') + +if tool not in ['openflow']: + try: + print('* VHDL Generics') + prj = Class() + prj.add_vhdl('../../examples/sources/vhdl/*.vhdl', 'blink_lib') + prj.set_top('Top') + prj.make() + sys.exit('* FAIL') + except SystemExit: + raise + except: + print('* PASS') + + print('* VHDL Support') + prj = Class() + prj.add_vhdl('../../examples/sources/vhdl/*.vhdl', 'blink_lib') + prj.set_top('Top') + prj.add_param('FREQ', '1') + prj.add_param('SECS', '1') + prj.make() + print('* PASS') + +print(f'* Class Under Test works as expected') From d8eebcfd2d8b2d64956ed64a86d1dcc4a54b4bde Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 29 Jun 2024 22:33:41 -0300 Subject: [PATCH 122/248] Improve/simplify the generation of an intentional error --- examples/sources/slog/top.sv | 55 +++++++++++----------------------- examples/sources/vhdl/top.vhdl | 8 +++-- examples/sources/vlog/top.v | 55 +++++++++++----------------------- 3 files changed, 41 insertions(+), 77 deletions(-) diff --git a/examples/sources/slog/top.sv b/examples/sources/slog/top.sv index 4665011a..cfc700e3 100644 --- a/examples/sources/slog/top.sv +++ b/examples/sources/slog/top.sv @@ -9,47 +9,28 @@ module Top #( output led_o ); - initial begin - if (!FREQ) begin - $stop("FREQ not set"); - $error("FREQ not set"); - $fatal("FREQ not set"); - end - if (!SECS) begin - $stop("SECS not set"); - $error("SECS not set"); - $fatal("SECS not set"); - end - `ifndef INCLUDE1 - $stop("INCLUDE1 not defined"); - $error("INCLUDE1 not defined"); - $fatal("INCLUDE1 not defined"); - `endif - `ifndef INCLUDE2 - $stop("INCLUDE2 not defined"); - $error("INCLUDE2 not defined"); - $fatal("INCLUDE2 not defined"); - `endif - `ifndef DEFINE1 - $stop("DEFINE1 not defined"); - $error("DEFINE1 not defined"); - $fatal("DEFINE1 not defined"); - `endif - `ifndef DEFINE2 - $stop("DEFINE2 not defined"); - $error("DEFINE2 not defined"); - $fatal("DEFINE2 not defined"); - `endif - end - -`ifdef INCLUDE1 -`ifdef INCLUDE2 -`ifdef DEFINE1 -`ifdef DEFINE2 Blink #(.FREQ (FREQ), .SECS (SECS)) dut (.clk_i (clk_i), .led_o (led_o)); + +`ifndef INCLUDE1 + Top Top (.clk_i (clk_i), .led_o (led_o)); `endif + +`ifndef INCLUDE2 + Top Top (.clk_i (clk_i), .led_o (led_o)); `endif + +`ifndef DEFINE1 + Top Top (.clk_i (clk_i), .led_o (led_o)); `endif + +`ifndef DEFINE2 + Top Top (.clk_i (clk_i), .led_o (led_o)); `endif + generate + if (!FREQ || !SECS) begin: gen_error + Top Top (.clk_i (clk_i), .led_o (led_o)); + end + endgenerate + endmodule diff --git a/examples/sources/vhdl/top.vhdl b/examples/sources/vhdl/top.vhdl index 24977d6c..c6537185 100644 --- a/examples/sources/vhdl/top.vhdl +++ b/examples/sources/vhdl/top.vhdl @@ -17,11 +17,13 @@ end entity Top; architecture ARCH of Top is begin - assert FREQ > 0 report "FREQ not set" severity failure; - assert SECS > 0 report "SECS not set" severity failure; - 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 + top_i: entity work.Top + port map (clk_i => clk_i, led_o => led_o); + end generate gen_error; + end architecture ARCH; diff --git a/examples/sources/vlog/top.v b/examples/sources/vlog/top.v index fb6dff0b..f47433ba 100644 --- a/examples/sources/vlog/top.v +++ b/examples/sources/vlog/top.v @@ -9,47 +9,28 @@ module Top #( output led_o ); - initial begin - if (!FREQ) begin - $stop("FREQ not set"); - $error("FREQ not set"); - $fatal("FREQ not set"); - end - if (!SECS) begin - $stop("SECS not set"); - $error("SECS not set"); - $fatal("SECS not set"); - end - `ifndef INCLUDE1 - $stop("INCLUDE1 not defined"); - $error("INCLUDE1 not defined"); - $fatal("INCLUDE1 not defined"); - `endif - `ifndef INCLUDE2 - $stop("INCLUDE2 not defined"); - $error("INCLUDE2 not defined"); - $fatal("INCLUDE2 not defined"); - `endif - `ifndef DEFINE1 - $stop("DEFINE1 not defined"); - $error("DEFINE1 not defined"); - $fatal("DEFINE1 not defined"); - `endif - `ifndef DEFINE2 - $stop("DEFINE2 not defined"); - $error("DEFINE2 not defined"); - $fatal("DEFINE2 not defined"); - `endif - end - -`ifdef INCLUDE1 -`ifdef INCLUDE2 -`ifdef DEFINE1 -`ifdef DEFINE2 Blink #(.FREQ (FREQ), .SECS (SECS)) dut (.clk_i (clk_i), .led_o (led_o)); + +`ifndef INCLUDE1 + Top Top (.clk_i (clk_i), .led_o (led_o)); `endif + +`ifndef INCLUDE2 + Top Top (.clk_i (clk_i), .led_o (led_o)); `endif + +`ifndef DEFINE1 + Top Top (.clk_i (clk_i), .led_o (led_o)); `endif + +`ifndef DEFINE2 + Top Top (.clk_i (clk_i), .led_o (led_o)); `endif + generate + if (!FREQ || !SECS) begin: gen_error + Top Top (.clk_i (clk_i), .led_o (led_o)); + end + endgenerate + endmodule From 032a8e171a8d7cbf816601230b4d652e3ad86163 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 29 Jun 2024 22:34:34 -0300 Subject: [PATCH 123/248] Fix VHDL libraries support for ISE --- pyfpga/ise.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpga/ise.py b/pyfpga/ise.py index 370c80ce..6109742d 100644 --- a/pyfpga/ise.py +++ b/pyfpga/ise.py @@ -40,7 +40,7 @@ def _make_prepare(self, steps): files = [] if 'files' in self.data: for file in self.data['files']: - if 'lib' in self.data['files']: + if 'lib' in self.data['files'][file]: lib = self.data['files'][file]['lib'] files.append(f'lib_vhdl new {lib}') files.append(f'xfile add {file} -lib_vhdl {lib}') From c072eafad1caa60039a86d897888cc5b8664111e Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 29 Jun 2024 22:36:35 -0300 Subject: [PATCH 124/248] Improve/simplify the test to verify support --- tests/projects/support.py | 49 ++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/tests/projects/support.py b/tests/projects/support.py index 3b529f14..99ec3cb7 100644 --- a/tests/projects/support.py +++ b/tests/projects/support.py @@ -24,10 +24,10 @@ if Class is None: sys.exit('Unsupported tool') -print(f'* Class Under Test: {Class.__name__}') +print(f'INFO: the Class Under Test is {Class.__name__}') try: - print('* Verilog Includes') + print('INFO: checking Verilog Includes') prj = Class() prj.add_vlog('../../examples/sources/vlog/*.v') prj.set_top('Top') @@ -35,15 +35,15 @@ prj.add_define('DEFINE2', '1') prj.add_param('FREQ', '1') prj.add_param('SECS', '1') - prj.make() - sys.exit('* FAIL') + prj.make(last='syn') + sys.exit('ERROR: something does not work as expected') except SystemExit: raise except: - print('* PASS') + pass try: - print('* Verilog Defines') + print('INFO: checking Verilog Defines') prj = Class() prj.add_vlog('../../examples/sources/vlog/*.v') prj.set_top('Top') @@ -51,15 +51,15 @@ prj.add_include('../../examples/sources/vlog/include2') prj.add_param('FREQ', '1') prj.add_param('SECS', '1') - prj.make() - sys.exit('* FAIL') + prj.make(last='syn') + sys.exit('ERROR: something does not work as expected') except SystemExit: raise except: - print('* PASS') + pass try: - print('* Verilog Parameters') + print('INFO: checking Verilog Parameters') prj = Class() prj.add_vlog('../../examples/sources/vlog/*.v') prj.set_top('Top') @@ -67,14 +67,14 @@ prj.add_include('../../examples/sources/vlog/include2') prj.add_define('DEFINE1', '1') prj.add_define('DEFINE2', '1') - prj.make() - sys.exit('* FAIL') + prj.make(last='syn') + sys.exit('ERROR: something does not work as expected') except SystemExit: raise except: - print('* PASS') + pass -print('* Verilog Support') +print('INFO: checking Verilog Support') prj = Class() prj.add_vlog('../../examples/sources/vlog/*.v') prj.set_top('Top') @@ -84,11 +84,10 @@ prj.add_define('DEFINE2', '1') prj.add_param('FREQ', '1') prj.add_param('SECS', '1') -prj.make() -print('* PASS') +prj.make(last='syn') if tool not in ['ise', 'openflow']: - print('* System Verilog Support') + print('INFO: checking System Verilog Support') prj = Class() prj.add_vlog('../../examples/sources/slog/*.sv') prj.set_top('Top') @@ -98,21 +97,20 @@ prj.add_define('DEFINE2', '1') prj.add_param('FREQ', '1') prj.add_param('SECS', '1') - prj.make() - print('* PASS') + prj.make(last='syn') if tool not in ['openflow']: try: - print('* VHDL Generics') + print('INFO: checking VHDL Generics') prj = Class() prj.add_vhdl('../../examples/sources/vhdl/*.vhdl', 'blink_lib') prj.set_top('Top') - prj.make() - sys.exit('* FAIL') + prj.make(last='syn') + sys.exit('ERROR: something does not work as expected') except SystemExit: raise except: - print('* PASS') + pass print('* VHDL Support') prj = Class() @@ -120,7 +118,6 @@ prj.set_top('Top') prj.add_param('FREQ', '1') prj.add_param('SECS', '1') - prj.make() - print('* PASS') + prj.make(last='syn') -print(f'* Class Under Test works as expected') +print(f'INFO: Class Under Test works as expected') From f403d88826cf25fd131dfe997d1857e8e34af638 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 29 Jun 2024 22:37:11 -0300 Subject: [PATCH 125/248] Add a script to run the ISE examples --- examples/ise/run.sh | 13 +++++++++++++ examples/openflow/run.sh | 7 ++----- examples/vivado/run.sh | 7 ++----- 3 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 examples/ise/run.sh diff --git a/examples/ise/run.sh b/examples/ise/run.sh new file mode 100644 index 00000000..c13ee1c0 --- /dev/null +++ b/examples/ise/run.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e + +BOARDS=("s6micro" "nexys3") +SOURCES=("vlog" "vhdl") + +for BOARD in "${BOARDS[@]}"; do + for SOURCE in "${SOURCES[@]}"; do + echo "> $BOARD - $SOURCE" + python3 run.py --board $BOARD --source $SOURCE + done +done diff --git a/examples/openflow/run.sh b/examples/openflow/run.sh index 4e213f40..986538d5 100644 --- a/examples/openflow/run.sh +++ b/examples/openflow/run.sh @@ -4,13 +4,10 @@ set -e BOARDS=("icestick" "edu-ciaa" "orangecrab" "ecp5evn") SOURCES=("vlog" "vhdl" "slog") -ACTIONS=("make" "prog" "all") for BOARD in "${BOARDS[@]}"; do for SOURCE in "${SOURCES[@]}"; do - for ACTION in "${ACTIONS[@]}"; do - echo "> $BOARD - $SOURCE - $ACTION" - python3 run.py --board $BOARD --source $SOURCE --action $ACTION - done + echo "> $BOARD - $SOURCE" + python3 run.py --board $BOARD --source $SOURCE done done diff --git a/examples/vivado/run.sh b/examples/vivado/run.sh index bd894b91..3b6a065a 100644 --- a/examples/vivado/run.sh +++ b/examples/vivado/run.sh @@ -4,13 +4,10 @@ set -e BOARDS=("zybo" "arty") SOURCES=("vlog" "vhdl" "slog") -ACTIONS=("make" "prog" "all") for BOARD in "${BOARDS[@]}"; do for SOURCE in "${SOURCES[@]}"; do - for ACTION in "${ACTIONS[@]}"; do - echo "> $BOARD - $SOURCE - $ACTION" - python3 run.py --board $BOARD --source $SOURCE --action $ACTION - done + echo "> $BOARD - $SOURCE" + python3 run.py --board $BOARD --source $SOURCE done done From f027f228968188289861fce8945ba2b2685efb2d Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 29 Jun 2024 22:39:53 -0300 Subject: [PATCH 126/248] Fix pylint complaint --- tests/projects/support.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/projects/support.py b/tests/projects/support.py index 99ec3cb7..358cb8ae 100644 --- a/tests/projects/support.py +++ b/tests/projects/support.py @@ -39,7 +39,7 @@ sys.exit('ERROR: something does not work as expected') except SystemExit: raise -except: +except Exception: pass try: @@ -55,7 +55,7 @@ sys.exit('ERROR: something does not work as expected') except SystemExit: raise -except: +except Exception: pass try: @@ -71,7 +71,7 @@ sys.exit('ERROR: something does not work as expected') except SystemExit: raise -except: +except Exception: pass print('INFO: checking Verilog Support') @@ -109,7 +109,7 @@ sys.exit('ERROR: something does not work as expected') except SystemExit: raise - except: + except Exception: pass print('* VHDL Support') From daf7f7ca82ee48d01179dd44f136db7a3beea21a Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 30 Jun 2024 14:38:48 -0300 Subject: [PATCH 127/248] Add checking of success to force an error if not --- pyfpga/templates/vivado.jinja | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index 2c593f69..8f0cbb8e 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -50,6 +50,8 @@ open_project {{ PROJECT }} 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 } {{ POSTSYN }} {% endif %} @@ -60,6 +62,8 @@ wait_on_run synth_1 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 } {{ POSTPAR }} {% endif %} From 23a89d720f6b0b91cb42a9de60635493f8262b76 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 30 Jun 2024 15:10:05 -0300 Subject: [PATCH 128/248] Modified default part --- pyfpga/quartus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpga/quartus.py b/pyfpga/quartus.py index fda50647..fec1e25b 100644 --- a/pyfpga/quartus.py +++ b/pyfpga/quartus.py @@ -21,7 +21,7 @@ class Quartus(Project): def _make_prepare(self, steps): context = { 'PROJECT': self.name or 'quartus', - 'PART': self.data.get('part', '10cl120zf780i8g') + 'PART': self.data.get('part', '10M50SCE144I7G') } for step in steps: context[step] = 1 From b26ac58a3155c39888c360a16451ff4b83df6e47 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 30 Jun 2024 15:10:22 -0300 Subject: [PATCH 129/248] Fix SystemVerilog test --- tests/projects/support.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/projects/support.py b/tests/projects/support.py index 358cb8ae..4ce15596 100644 --- a/tests/projects/support.py +++ b/tests/projects/support.py @@ -89,7 +89,7 @@ if tool not in ['ise', 'openflow']: print('INFO: checking System Verilog Support') prj = Class() - prj.add_vlog('../../examples/sources/slog/*.sv') + 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') From 063c5bb9e07dfc3c69f33897ab31fd2658c9e8d2 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 30 Jun 2024 15:25:39 -0300 Subject: [PATCH 130/248] Fix Vivado mock-up after last changes on the Vivado template --- tests/mocks/vivado | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/mocks/vivado b/tests/mocks/vivado index f6a47eff..fb59d819 100755 --- a/tests/mocks/vivado +++ b/tests/mocks/vivado @@ -42,6 +42,21 @@ tool = parser.prog 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} ''' From 73417f832a950ff1bac77409af97099e8a524dd5 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 30 Jun 2024 20:03:36 -0300 Subject: [PATCH 131/248] Clean-up --- pyfpga/ise.py | 17 +-------- pyfpga/libero.py | 57 ++++++++++++++++++++++++++--- pyfpga/openflow.py | 43 +++++++--------------- pyfpga/project.py | 2 - pyfpga/quartus.py | 27 +++----------- pyfpga/templates/quartus-prog.jinja | 8 +++- pyfpga/vivado.py | 5 +-- 7 files changed, 81 insertions(+), 78 deletions(-) diff --git a/pyfpga/ise.py b/pyfpga/ise.py index 6109742d..a964af7c 100644 --- a/pyfpga/ise.py +++ b/pyfpga/ise.py @@ -14,7 +14,6 @@ import re -from pathlib import Path from pyfpga.project import Project @@ -77,7 +76,7 @@ def _make_prepare(self, steps): def _prog_prepare(self, bitstream, position): if not bitstream: basename = self.name or 'ise' - bitstream = Path(self.odir).resolve() / f'{basename}.bit' + bitstream = f'{basename}.bit' context = {'BITSTREAM': bitstream, 'POSITION': position} self._create_file('vivado-prog', 'tcl', context) return 'impact -batch impact-prog' @@ -86,20 +85,6 @@ def add_slog(self, pathname): """Add System Verilog file/s.""" raise NotImplementedError('ISE does not support SystemVerilog') -# _DEVTYPES = ['fpga', 'spi', 'bpi', 'detect', 'unlock'] - -# 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_info(part): """Get info about the FPGA part. diff --git a/pyfpga/libero.py b/pyfpga/libero.py index 5cf8efe4..1d0dc983 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -20,13 +20,58 @@ class Libero(Project): """Class to support Libero.""" - def __init__(self, name='libero', odir='results'): - super().__init__(name=name, odir=odir) - self.set_part('mpf100t-1-fcg484') - def _make_prepare(self, steps): - self.tool['make-app'] = 'libero' - self.tool['make-cmd'] = 'libero SCRIPT:libero.tcl' + info = get_info(self.data.get('part', 'mpf100t-1-fcg484')) + context = { + 'PROJECT': self.name or 'ise', + 'FAMILY': info['family'], + 'DEVICE': info['device'], + 'SPEED': info['speed'], + 'PACKAGE': info['package'] + } + for step in steps: + context[step] = 1 + if 'includes' in self.data: + includes = [] + for include in self.data['includes']: + includes.append(str(include)) + context['INCLUDES'] = '|'.join(includes) + files = [] + if 'files' in self.data: + for file in self.data['files']: + if 'lib' in self.data['files'][file]: + lib = self.data['files'][file]['lib'] + files.append(f'lib_vhdl new {lib}') + files.append(f'xfile add {file} -lib_vhdl {lib}') + else: + files.append(f'xfile add {file}') + if 'constraints' in self.data: + constraints = [] + for file in self.data['constraints']: + files.append(f'xfile add {file}') + if file.suffix == '.xcf': + constraints.append(str(file)) + if constraints: + context['CONSTRAINTS'] = " ".join(constraints) + if files: + context['FILES'] = '\n'.join(files) + if 'top' in self.data: + context['TOP'] = self.data['top'] + if 'defines' in self.data: + defines = [] + for key, value in self.data['defines'].items(): + defines.append(f'{key}={value}') + context['DEFINES'] = ' | '.join(defines) + if 'params' in self.data: + params = [] + for key, value in self.data['params'].items(): + params.append(f'{key}={value}') + context['PARAMS'] = ' '.join(params) + if 'hooks' in self.data: + for stage in self.data['hooks']: + context[stage.upper()] = '\n'.join(self.data['hooks'][stage]) + self._create_file('ise', 'tcl', context) + return 'libero SCRIPT:libero.tcl' def _prog_prepare(self, bitstream, position): raise NotImplementedError('Libero programming not supported') diff --git a/pyfpga/openflow.py b/pyfpga/openflow.py index 8b40b259..51dcd84a 100644 --- a/pyfpga/openflow.py +++ b/pyfpga/openflow.py @@ -12,7 +12,6 @@ # pylint: disable=too-many-branches # pylint: disable=duplicate-code -from pathlib import Path from pyfpga.project import Project @@ -40,12 +39,18 @@ def _make_prepare(self, steps): files.append(f'read_verilog -defer {file}') if files: context['VLOGS'] = '\n'.join(files) -# for file in self.data['files']: -# if 'lib' in self.data['files'][file]: -# lib = self.data['files'][file]['lib'] -# files.append( -# f'set_property library {lib} [get_files {file}]' -# ) +# 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]}') +# if len(vhdls) > 0: +# verilogs = [f'ghdl $FLAGS {self.top}'] if 'constraints' in self.data: constraints = [] for constraint in self.data['constraints']: @@ -70,34 +75,14 @@ def _make_prepare(self, steps): return 'bash openflow.sh' def _prog_prepare(self, bitstream, position): - _ = position + _ = position # Not needed if not bitstream: basename = self.name or 'openflow' - bitstream = Path(self.odir).resolve() / f'{basename}.bit' + bitstream = f'{basename}.bit' context = {'BITSTREAM': bitstream} self._create_file('openflow-prog', 'sh', context) return 'bash openflow-prog.sh' -# def _create_gen_script(self, tasks): -# # 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}'] - def get_info(part): """Get info about the FPGA part. diff --git a/pyfpga/project.py b/pyfpga/project.py index b9192c03..b77b8159 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -27,8 +27,6 @@ class Project: :type odir: str, optional """ - tool = {} - def __init__(self, name=None, odir='results'): """Class constructor.""" self.data = {} diff --git a/pyfpga/quartus.py b/pyfpga/quartus.py index fec1e25b..417a497a 100644 --- a/pyfpga/quartus.py +++ b/pyfpga/quartus.py @@ -73,26 +73,11 @@ def _make_prepare(self, steps): return 'quartus_sh --script quartus.tcl' def _prog_prepare(self, bitstream, position): - # binaries = ['sof', 'pof'] - _ = position # Not needed for Vivado - # if not bitstream: - # basename = self.name or 'quartus' - # bitstream = Path(self.odir).resolve() / f'{basename}.bit' - context = {'BITSTREAM': bitstream} + # sof: SRAM Object File + # pof: Programming Object File + if not bitstream: + basename = self.name or 'quartus' + bitstream = f'{basename}.sof' + context = {'BITSTREAM': bitstream, 'POSITION': position} self._create_file('quartus-prog', 'tcl', context) return 'bash quartus-prog.sh' - -# 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/pyfpga/templates/quartus-prog.jinja b/pyfpga/templates/quartus-prog.jinja index d754fee5..62839a6b 100644 --- a/pyfpga/templates/quartus-prog.jinja +++ b/pyfpga/templates/quartus-prog.jinja @@ -4,4 +4,10 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -quartus_pgm -c {{ CABLE }} --mode jtag -o "p;{{ BITSTREAM }}@{{ POSITION }}" +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/vivado.py b/pyfpga/vivado.py index c0a625e6..1858ed82 100644 --- a/pyfpga/vivado.py +++ b/pyfpga/vivado.py @@ -11,7 +11,6 @@ # pylint: disable=too-many-locals # pylint: disable=too-many-branches -from pathlib import Path from pyfpga.project import Project @@ -74,10 +73,10 @@ def _make_prepare(self, steps): return 'vivado -mode batch -notrace -quiet -source vivado.tcl' def _prog_prepare(self, bitstream, position): - _ = position # Not needed for Vivado + _ = position # Not needed if not bitstream: basename = self.name or 'vivado' - bitstream = Path(self.odir).resolve() / f'{basename}.bit' + bitstream = f'{basename}.bit' context = {'BITSTREAM': bitstream} self._create_file('vivado-prog', 'tcl', context) return 'vivado -mode batch -notrace -quiet -source vivado-prog.tcl' From 030b335ddb24e759b420b67ce1a00884a36bd825 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 30 Jun 2024 23:14:40 -0300 Subject: [PATCH 132/248] Re-added support for Libero (WIP) --- examples/libero/libero.py | 32 -------- examples/libero/run.py | 45 +++++++++++ pyfpga/libero.py | 22 ++--- pyfpga/templates/libero.jinja | 148 ++++++++++++++-------------------- 4 files changed, 116 insertions(+), 131 deletions(-) delete mode 100644 examples/libero/libero.py create mode 100644 examples/libero/run.py diff --git a/examples/libero/libero.py b/examples/libero/libero.py deleted file mode 100644 index 32ef38a5..00000000 --- a/examples/libero/libero.py +++ /dev/null @@ -1,32 +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']: - prj.generate() - -if args.action in ['transfer', 'all']: - print('ERROR:transfer:Not yet implemented') diff --git a/examples/libero/run.py b/examples/libero/run.py new file mode 100644 index 00000000..87e9dc06 --- /dev/null +++ b/examples/libero/run.py @@ -0,0 +1,45 @@ +"""Libero examples.""" + +import argparse + +from pyfpga.libero import Libero + +parser = argparse.ArgumentParser() +parser.add_argument( + '--source', choices=['vlog', 'vhdl', 'slog'], default='vlog' +) +parser.add_argument( + '--action', choices=['make', 'prog', 'all'], default='make' +) +args = parser.parse_args() + +prj = Libero(odir='../build/libero') + +prj.set_part('m2s010-1-tq144') +prj.add_param('FREQ', '125000000') +prj.add_cons('../sources/maker-board/clk.sdc', 'syn') +prj.add_cons('../sources/maker-board/clk.pdc', 'par') +prj.add_cons('../sources/maker-board/led.pdc', 'par') +prj.add_param('SECS', '1') + +if args.source == 'vhdl': + prj.add_vhdl('../sources/vhdl/*.vhdl', 'blink_lib') +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') + +if args.action in ['make', 'all']: + prj.make() + +if args.action in ['prog', 'all']: + prj.prog() diff --git a/pyfpga/libero.py b/pyfpga/libero.py index 1d0dc983..29fc471a 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -23,7 +23,7 @@ class Libero(Project): def _make_prepare(self, steps): info = get_info(self.data.get('part', 'mpf100t-1-fcg484')) context = { - 'PROJECT': self.name or 'ise', + 'PROJECT': self.name or 'libero', 'FAMILY': info['family'], 'DEVICE': info['device'], 'SPEED': info['speed'], @@ -35,22 +35,24 @@ def _make_prepare(self, steps): includes = [] for include in self.data['includes']: includes.append(str(include)) - context['INCLUDES'] = '|'.join(includes) + context['INCLUDES'] = ';'.join(includes) files = [] if 'files' in self.data: for file in self.data['files']: if 'lib' in self.data['files'][file]: lib = self.data['files'][file]['lib'] - files.append(f'lib_vhdl new {lib}') - files.append(f'xfile add {file} -lib_vhdl {lib}') + files.append( + f'create_links -library {lib} -hdl_source {file}' + ) else: - files.append(f'xfile add {file}') + files.append(f'create_links -hdl_source {file}') if 'constraints' in self.data: constraints = [] for file in self.data['constraints']: - files.append(f'xfile add {file}') - if file.suffix == '.xcf': - constraints.append(str(file)) + if file.suffix == '.sdc': + constraints.append(f'create_links -sdc {file}') + else: + constraints.append(f'create_links -io_pdc {file}') if constraints: context['CONSTRAINTS'] = " ".join(constraints) if files: @@ -61,7 +63,7 @@ def _make_prepare(self, steps): defines = [] for key, value in self.data['defines'].items(): defines.append(f'{key}={value}') - context['DEFINES'] = ' | '.join(defines) + context['DEFINES'] = ' '.join(defines) if 'params' in self.data: params = [] for key, value in self.data['params'].items(): @@ -70,7 +72,7 @@ def _make_prepare(self, steps): if 'hooks' in self.data: for stage in self.data['hooks']: context[stage.upper()] = '\n'.join(self.data['hooks'][stage]) - self._create_file('ise', 'tcl', context) + self._create_file('libero', 'tcl', context) return 'libero SCRIPT:libero.tcl' def _prog_prepare(self, bitstream, position): diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index 1b230c2c..cc8af1e9 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -4,106 +4,76 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -set PROJECT {{ PROJECT }} -set PART {{ PART }} -set FAMILY {{ FAMILY }} -set DEVICE {{ DEVICE }} -set PACKAGE {{ PACKAGE }} -set SPEED {{ SPEED }} -set TOP {{ TOP }} - -set PARAMS [list {{ PARAMS }}] - -proc fpga_file {FILE {LIBRARY "work"}} { - set message "adding the file '$FILE'" - if { $LIBRARY != "work" } { append message " (into the VHDL library '$LIBRARY')" } - regexp -nocase {\.(\w*)$} $FILE -> ext - if { $ext == "tcl" } { - source $FILE - return - } - 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 - } -} - -proc fpga_include {PATH} { - lappend INCLUDED $PATH - # 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 -} - -proc fpga_params {} { - if { [llength $PARAMS] == 0 } { return } - # They must be specified after set_root (see fpga_top) -} +#proc fpga_include {PATH} { +# lappend INCLUDED $PATH +# # 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 +#} #--[ Project configuration ]--------------------------------------------------- {% if CFG %} -if { [ file exists $PROJECT ] } { file delete -force -- $PROJECT } -new_project -name $PROJECT -location $PROJECT -hdl {VHDL} -family {SmartFusion2} +if { [ file exists {{ PROJECT }} ] } { file delete -force -- {{ PROJECT }} } +new_project -name {{ PROJECT }} -location . -hdl {VHDL} -family {SmartFusion2} -set_device -family $FAMILY -die $DEVICE -package $PACKAGE -speed $SPEED +set_device -family {{ FAMILY }} -die {{ DEVICE }} -package {{ PACKAGE }} -speed {{ SPEED }} {{ PRECFG }} -fpga_files - -set_root $TOP -# Verilog Included files -set cmd "configure_tool -name {SYNTHESIZE} -params {SYNPLIFY_OPTIONS:" -if { [info exists INCLUDED] && [llength $INCLUDED] > 0 } { - # See <ROOT>/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" +{{ FILES }} +build_design_hierarchy + +{% if TOP %} +set_root {{ TOP }} +{% endif %} + +{% if INCLUDES or DEFINES or PARAMS %} +configure_tool -name {SYNTHESIZE} -params {SYNPLIFY_OPTIONS: +{% if INCLUDES %} set_option -include_path "{{ INCLUDES }}"{% endif %} +{% if DEFINES %} set_option -hdl_define "{{ DEFINES }}"{% endif %} +{% if PARAMS %} set_option -hdl_param "{{ PARAMS }}"{% endif %} } -append cmd "}" -eval $cmd +{% endif %} + # 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 -} - -fpga_params +#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 +#} + +#organize_tool_files -tool {SYNTHESIZE} \ +# -file ../resources/constraints/maker-board/clk.sdc \ +# -module Blink -input_type {constraint} + +#organize_tool_files -tool {PLACEROUTE} \ +# -file ../resources/constraints/maker-board/clk.sdc \ +# -file ../resources/constraints/maker-board/clk.pdc \ +# -file ../resources/constraints/maker-board/led.pdc \ +# -module Blink -input_type {constraint} + +#organize_tool_files -tool {VERIFYTIMING} \ +# -file ../resources/constraints/maker-board/clk.sdc \ +# -module Blink -input_type {constraint} {{ POSTCFG }} @@ -113,7 +83,7 @@ close_project #--[ Design flow ]------------------------------------------------------------- {% if SYN or PAR or BIT %} -open_project $PROJECT/$PROJECT.prjx +open_project {{ PROJECT }}/{{ PROJECT }}.prjx {% if SYN %} {{ PRESYN }} From 6ae7ce5f603e9a3f712d866e0b38d54da2a59e1a Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 1 Jul 2024 22:07:18 -0300 Subject: [PATCH 133/248] Fix quartus_sh mock-up --- tests/mocks/quartus_sh | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/tests/mocks/quartus_sh b/tests/mocks/quartus_sh index 64a8cf65..472f639f 100755 --- a/tests/mocks/quartus_sh +++ b/tests/mocks/quartus_sh @@ -20,30 +20,18 @@ args = parser.parse_args() tool = parser.prog tcl = f''' -lappend auto_path pkg - proc unknown args {{ }} -source {args.script} -''' +package provide ::quartus::project 1.0 +namespace eval ::quartus::project {{ }} -with open(f'{tool}-mock.tcl', 'w', encoding='utf-8') as file: - file.write(tcl) +package provide ::quartus::flow 1.0 +namespace eval ::quartus::flow {{ }} -tcl = f''' -namespace eval ::quartus {{ - namespace export project -}} +source {args.script} ''' -if not os.path.exists('pkg'): - os.makedirs('pkg') - -package ifneeded ::quartus 1.0 [list source quartus-pkg.tcl] - -pkgIndex.tcl - -with open(f'pkg/quartus.tcl', 'w', encoding='utf-8') as file: +with open(f'{tool}-mock.tcl', 'w', encoding='utf-8') as file: file.write(tcl) subprocess.run( From 4b8fe9eade9434042aaa5295b1061762e27fa835 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 1 Jul 2024 22:08:41 -0300 Subject: [PATCH 134/248] Fix ecp5 flow --- pyfpga/templates/openflow.jinja | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja index 971149be..f9b3ba05 100644 --- a/pyfpga/templates/openflow.jinja +++ b/pyfpga/templates/openflow.jinja @@ -98,7 +98,7 @@ icepack {{ PROJECT }}.asc {{ PROJECT }}.bit {% endif %} {% if FAMILY == 'ecp5' %} -$DOCKER hdlc/icestorm /bin/bash -c " +$DOCKER hdlc/prjtrellis /bin/bash -c " {{ PREBIT }} ecppack --svf {{ PROJECT }}.svf {{ PROJECT }}.config {{ PROJECT }}.bit {{ POSTBIT }} From 4769fe29191af81156f57ee2548175d24df78e65 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 1 Jul 2024 22:10:25 -0300 Subject: [PATCH 135/248] Fix to abort if the bit file doesn't exists --- pyfpga/templates/ise.jinja | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyfpga/templates/ise.jinja b/pyfpga/templates/ise.jinja index 024e0edb..701a028d 100644 --- a/pyfpga/templates/ise.jinja +++ b/pyfpga/templates/ise.jinja @@ -78,8 +78,7 @@ if { [process get "Place & Route" status] == "errors" } { exit 1 } process run "Generate Programming File" if { [process get "Generate Programming File" status] == "errors" } { exit 1 } -# catch { file rename -force {{ TOP }}.bit {{ PROJECT }}.bit } -file rename -force {{ TOP }}.bit {{ PROJECT }}.bit +catch { file rename -force {{ TOP }}.bit {{ PROJECT }}.bit } {{ POSTBIT }} {% endif %} From 17af7b689a0e80773e6f268691f29065a87f6033 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 1 Jul 2024 22:11:39 -0300 Subject: [PATCH 136/248] Merged several examples under the project directory --- examples/ise/run.sh | 13 --------- examples/openflow/run.sh | 13 --------- examples/{ise/run.py => projects/ise.py} | 0 .../{libero/run.py => projects/libero.py} | 14 +++++---- .../{openflow/run.py => projects/openflow.py} | 0 .../{quartus/run.py => projects/quartus.py} | 0 examples/projects/regress.sh | 29 +++++++++++++++++++ .../{vivado/run.py => projects/vivado.py} | 0 examples/vivado/run.sh | 13 --------- 9 files changed, 38 insertions(+), 44 deletions(-) delete mode 100644 examples/ise/run.sh delete mode 100644 examples/openflow/run.sh rename examples/{ise/run.py => projects/ise.py} (100%) rename examples/{libero/run.py => projects/libero.py} (73%) rename examples/{openflow/run.py => projects/openflow.py} (100%) rename examples/{quartus/run.py => projects/quartus.py} (100%) create mode 100644 examples/projects/regress.sh rename examples/{vivado/run.py => projects/vivado.py} (100%) delete mode 100644 examples/vivado/run.sh diff --git a/examples/ise/run.sh b/examples/ise/run.sh deleted file mode 100644 index c13ee1c0..00000000 --- a/examples/ise/run.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -set -e - -BOARDS=("s6micro" "nexys3") -SOURCES=("vlog" "vhdl") - -for BOARD in "${BOARDS[@]}"; do - for SOURCE in "${SOURCES[@]}"; do - echo "> $BOARD - $SOURCE" - python3 run.py --board $BOARD --source $SOURCE - done -done diff --git a/examples/openflow/run.sh b/examples/openflow/run.sh deleted file mode 100644 index 986538d5..00000000 --- a/examples/openflow/run.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -set -e - -BOARDS=("icestick" "edu-ciaa" "orangecrab" "ecp5evn") -SOURCES=("vlog" "vhdl" "slog") - -for BOARD in "${BOARDS[@]}"; do - for SOURCE in "${SOURCES[@]}"; do - echo "> $BOARD - $SOURCE" - python3 run.py --board $BOARD --source $SOURCE - done -done diff --git a/examples/ise/run.py b/examples/projects/ise.py similarity index 100% rename from examples/ise/run.py rename to examples/projects/ise.py diff --git a/examples/libero/run.py b/examples/projects/libero.py similarity index 73% rename from examples/libero/run.py rename to examples/projects/libero.py index 87e9dc06..9d2fc6da 100644 --- a/examples/libero/run.py +++ b/examples/projects/libero.py @@ -5,6 +5,9 @@ from pyfpga.libero import Libero parser = argparse.ArgumentParser() +parser.add_argument( + '--board', choices=['maker-board'], default='maker-board' +) parser.add_argument( '--source', choices=['vlog', 'vhdl', 'slog'], default='vlog' ) @@ -15,11 +18,12 @@ prj = Libero(odir='../build/libero') -prj.set_part('m2s010-1-tq144') -prj.add_param('FREQ', '125000000') -prj.add_cons('../sources/maker-board/clk.sdc', 'syn') -prj.add_cons('../sources/maker-board/clk.pdc', 'par') -prj.add_cons('../sources/maker-board/led.pdc', 'par') +if args.board == 'maker-board': + prj.set_part('m2s010-1-tq144') + prj.add_param('FREQ', '125000000') + prj.add_cons('../sources/maker-board/clk.sdc', 'syn') + prj.add_cons('../sources/maker-board/clk.pdc', 'par') + prj.add_cons('../sources/maker-board/led.pdc', 'par') prj.add_param('SECS', '1') if args.source == 'vhdl': diff --git a/examples/openflow/run.py b/examples/projects/openflow.py similarity index 100% rename from examples/openflow/run.py rename to examples/projects/openflow.py diff --git a/examples/quartus/run.py b/examples/projects/quartus.py similarity index 100% rename from examples/quartus/run.py rename to examples/projects/quartus.py diff --git a/examples/projects/regress.sh b/examples/projects/regress.sh new file mode 100644 index 00000000..f5513b3e --- /dev/null +++ b/examples/projects/regress.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +set -e + +declare -A TOOLS + +TOOLS["ise"]="s6micro nexys3" +TOOLS["libero"]="maker-board" +TOOLS["openflow"]="icestick edu-ciaa orangecrab ecp5evn" +TOOLS["quartus"]="de10nano" +TOOLS["vivado"]="zybo arty" + +SOURCES=("vlog" "vhdl" "slog") + +for TOOL in "${!TOOLS[@]}"; do + BOARDS=${TOOLS[$TOOL]} + for BOARD in $BOARDS; do + for SOURCE in "${SOURCES[@]}"; do + if [[ "$TOOL" == "ise" && "$SOURCE" == "slog" ]]; then + continue + fi + if [[ "$TOOL" == "openflow" && "$SOURCE" != "vlog" ]]; then + continue + fi + echo "> $TOOL - $BOARD - $SOURCE" + python3 $TOOL.py --board $BOARD --source $SOURCE + done + done +done diff --git a/examples/vivado/run.py b/examples/projects/vivado.py similarity index 100% rename from examples/vivado/run.py rename to examples/projects/vivado.py diff --git a/examples/vivado/run.sh b/examples/vivado/run.sh deleted file mode 100644 index 3b6a065a..00000000 --- a/examples/vivado/run.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -set -e - -BOARDS=("zybo" "arty") -SOURCES=("vlog" "vhdl" "slog") - -for BOARD in "${BOARDS[@]}"; do - for SOURCE in "${SOURCES[@]}"; do - echo "> $BOARD - $SOURCE" - python3 run.py --board $BOARD --source $SOURCE - done -done From bce2b2f23107a01285c367194121790851f17bd8 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 1 Jul 2024 22:17:50 -0300 Subject: [PATCH 137/248] Removed deprecated files/examples --- examples/Makefile | 11 ------- examples/configs.yml | 26 ---------------- examples/multi/memory.py | 24 --------------- examples/multi/parameters.py | 44 -------------------------- examples/multi/projects.py | 60 ------------------------------------ examples/multi/verilog.py | 24 --------------- examples/multi/vhdl.py | 21 ------------- 7 files changed, 210 deletions(-) delete mode 100644 examples/Makefile delete mode 100644 examples/configs.yml delete mode 100644 examples/multi/memory.py delete mode 100644 examples/multi/parameters.py delete mode 100644 examples/multi/projects.py delete mode 100644 examples/multi/verilog.py delete mode 100644 examples/multi/vhdl.py diff --git a/examples/Makefile b/examples/Makefile deleted file mode 100644 index 7bfe3fc4..00000000 --- a/examples/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/make - -ifdef MOCKS -export PATH := $(PATH):$(PWD)/../test/mocks -$(info INFO: using MOCKS for the vendor EDA tools) -endif - -DIRS=$(wildcard */) - -all: - @$(foreach DIR, $(DIRS), make -C $(DIR) || exit;) 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/multi/memory.py b/examples/multi/memory.py deleted file mode 100644 index bbec7300..00000000 --- a/examples/multi/memory.py +++ /dev/null @@ -1,24 +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') - PRJ.generate(to_task='syn') diff --git a/examples/multi/parameters.py b/examples/multi/parameters.py deleted file mode 100644 index f1912c85..00000000 --- a/examples/multi/parameters.py +++ /dev/null @@ -1,44 +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.add_param('FREQ', '50000000') - PRJ.add_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_vlog_include('../../hdl/headers1') - PRJ.add_vlog_include('../../hdl/headers2') - PRJ.add_files('../../hdl/blinking.v') - PRJ.set_top('Blinking') - # PRJ.add_param('INT', '15') - # PRJ.add_param('REA', '1.5') - # PRJ.add_param('LOG', "'1'") - # PRJ.add_param('VEC', '"10101010"') - # PRJ.add_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') - PRJ.generate(to_task='syn') diff --git a/examples/multi/projects.py b/examples/multi/projects.py deleted file mode 100644 index 3d267a93..00000000 --- a/examples/multi/projects.py +++ /dev/null @@ -1,60 +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', - meta={ - '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', - meta={ - 'outdir': '../../build/multi/projects/ise', - 'part': 'xc6slx9-2-csg324', - 'vhdl': [ - '../../hdl/blinking.vhdl' - ], - 'top': 'Blinking' - } - ), - 'prj3': Project( - 'quartus', - 'qurtus-prj', - meta={ - '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: - PROJECTS[prj].generate('syn') diff --git a/examples/multi/verilog.py b/examples/multi/verilog.py deleted file mode 100644 index b16be138..00000000 --- a/examples/multi/verilog.py +++ /dev/null @@ -1,24 +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_vlog_include('../../hdl/headers1') - PRJ.add_vlog_include('../../hdl/headers2') - PRJ.add_files('../../hdl/blinking.v') - PRJ.add_files('../../hdl/top.v') - PRJ.set_top('Top') - PRJ.generate(to_task='syn') diff --git a/examples/multi/vhdl.py b/examples/multi/vhdl.py deleted file mode 100644 index 7c6ee7d8..00000000 --- a/examples/multi/vhdl.py +++ /dev/null @@ -1,21 +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') - PRJ.generate(to_task='syn') From db4a4eae532775492a91c6e51fc269fb14fbc7b8 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 1 Jul 2024 22:36:48 -0300 Subject: [PATCH 138/248] Moved constraint files under the cons directory --- examples/projects/ise.py | 12 ++++++------ examples/projects/libero.py | 8 ++++---- examples/projects/openflow.py | 16 ++++++++-------- examples/projects/quartus.py | 6 +++--- examples/projects/regress.sh | 2 +- examples/projects/vivado.py | 12 ++++++------ examples/sources/{zybo => cons}/ZYBO/clk.xdc | 0 examples/sources/{zybo => cons}/ZYBO/led.xdc | 0 examples/sources/{zybo => cons}/ZYBO/timing.xdc | 0 .../{arty/a7-35t => cons/arty_a7_35t}/clk.xdc | 0 .../{arty/a7-35t => cons/arty_a7_35t}/led.xdc | 0 .../{arty/a7-35t => cons/arty_a7_35t}/timing.xdc | 0 examples/sources/{ => cons}/de10nano/clk.sdc | 0 examples/sources/{ => cons}/de10nano/clk.tcl | 0 examples/sources/{ => cons}/de10nano/led.tcl | 0 examples/sources/{ => cons}/ecp5evn/clk.lpf | 0 examples/sources/{ => cons}/ecp5evn/led.lpf | 0 examples/sources/{ => cons}/edu-ciaa/clk.pcf | 0 examples/sources/{ => cons}/edu-ciaa/led.pcf | 0 examples/sources/{ => cons}/icestick/clk.pcf | 0 examples/sources/{ => cons}/icestick/led.pcf | 0 .../sources/{maker-board => cons/maker}/clk.pdc | 0 .../sources/{maker-board => cons/maker}/clk.sdc | 0 .../sources/{maker-board => cons/maker}/led.pdc | 0 examples/sources/{ => cons}/nexys3/clk.ucf | 0 examples/sources/{ => cons}/nexys3/clk.xcf | 0 examples/sources/{ => cons}/nexys3/led.ucf | 0 examples/sources/{ => cons}/orangecrab/clk.lpf | 0 examples/sources/{ => cons}/orangecrab/led.lpf | 0 examples/sources/{ => cons}/s6micro/clk.ucf | 0 examples/sources/{ => cons}/s6micro/clk.xcf | 0 examples/sources/{ => cons}/s6micro/led.ucf | 0 32 files changed, 28 insertions(+), 28 deletions(-) rename examples/sources/{zybo => cons}/ZYBO/clk.xdc (100%) rename examples/sources/{zybo => cons}/ZYBO/led.xdc (100%) rename examples/sources/{zybo => cons}/ZYBO/timing.xdc (100%) rename examples/sources/{arty/a7-35t => cons/arty_a7_35t}/clk.xdc (100%) rename examples/sources/{arty/a7-35t => cons/arty_a7_35t}/led.xdc (100%) rename examples/sources/{arty/a7-35t => cons/arty_a7_35t}/timing.xdc (100%) rename examples/sources/{ => cons}/de10nano/clk.sdc (100%) rename examples/sources/{ => cons}/de10nano/clk.tcl (100%) rename examples/sources/{ => cons}/de10nano/led.tcl (100%) rename examples/sources/{ => cons}/ecp5evn/clk.lpf (100%) rename examples/sources/{ => cons}/ecp5evn/led.lpf (100%) rename examples/sources/{ => cons}/edu-ciaa/clk.pcf (100%) rename examples/sources/{ => cons}/edu-ciaa/led.pcf (100%) rename examples/sources/{ => cons}/icestick/clk.pcf (100%) rename examples/sources/{ => cons}/icestick/led.pcf (100%) rename examples/sources/{maker-board => cons/maker}/clk.pdc (100%) rename examples/sources/{maker-board => cons/maker}/clk.sdc (100%) rename examples/sources/{maker-board => cons/maker}/led.pdc (100%) rename examples/sources/{ => cons}/nexys3/clk.ucf (100%) rename examples/sources/{ => cons}/nexys3/clk.xcf (100%) rename examples/sources/{ => cons}/nexys3/led.ucf (100%) rename examples/sources/{ => cons}/orangecrab/clk.lpf (100%) rename examples/sources/{ => cons}/orangecrab/led.lpf (100%) rename examples/sources/{ => cons}/s6micro/clk.ucf (100%) rename examples/sources/{ => cons}/s6micro/clk.xcf (100%) rename examples/sources/{ => cons}/s6micro/led.ucf (100%) diff --git a/examples/projects/ise.py b/examples/projects/ise.py index 3bd89df9..a1b9dba0 100644 --- a/examples/projects/ise.py +++ b/examples/projects/ise.py @@ -22,15 +22,15 @@ if args.board == 's6micro': prj.set_part('xc6slx9-2-csg324') prj.add_param('FREQ', '125000000') - prj.add_cons('../sources/s6micro/clk.xcf', 'syn') - prj.add_cons('../sources/s6micro/clk.ucf', 'par') - prj.add_cons('../sources/s6micro/led.ucf', 'par') + prj.add_cons('../sources/cons/s6micro/clk.xcf', 'syn') + prj.add_cons('../sources/cons/s6micro/clk.ucf', 'par') + prj.add_cons('../sources/cons/s6micro/led.ucf', 'par') if args.board == 'nexys3': prj.set_part('xc6slx16-3-csg32') prj.add_param('FREQ', '100000000') - prj.add_cons('../sources/nexys3/clk.xcf', 'syn') - prj.add_cons('../sources/nexys3/clk.ucf', 'par') - prj.add_cons('../sources/nexys3/led.ucf', 'par') + prj.add_cons('../sources/cons/nexys3/clk.xcf', 'syn') + prj.add_cons('../sources/cons/nexys3/clk.ucf', 'par') + prj.add_cons('../sources/cons/nexys3/led.ucf', 'par') prj.add_param('SECS', '1') if args.source == 'vhdl': diff --git a/examples/projects/libero.py b/examples/projects/libero.py index 9d2fc6da..9c570a0e 100644 --- a/examples/projects/libero.py +++ b/examples/projects/libero.py @@ -6,7 +6,7 @@ parser = argparse.ArgumentParser() parser.add_argument( - '--board', choices=['maker-board'], default='maker-board' + '--board', choices=['maker'], default='maker' ) parser.add_argument( '--source', choices=['vlog', 'vhdl', 'slog'], default='vlog' @@ -21,9 +21,9 @@ if args.board == 'maker-board': prj.set_part('m2s010-1-tq144') prj.add_param('FREQ', '125000000') - prj.add_cons('../sources/maker-board/clk.sdc', 'syn') - prj.add_cons('../sources/maker-board/clk.pdc', 'par') - prj.add_cons('../sources/maker-board/led.pdc', 'par') + prj.add_cons('../sources/cons/maker/clk.sdc', 'syn') + prj.add_cons('../sources/cons/maker/clk.pdc', 'par') + prj.add_cons('../sources/cons/maker/led.pdc', 'par') prj.add_param('SECS', '1') if args.source == 'vhdl': diff --git a/examples/projects/openflow.py b/examples/projects/openflow.py index b01153c3..43c20f0b 100644 --- a/examples/projects/openflow.py +++ b/examples/projects/openflow.py @@ -23,23 +23,23 @@ if args.board == 'icestick': prj.set_part('hx1k-tq144') prj.add_param('FREQ', '100000000') - prj.add_cons('../sources/icestick/clk.pcf', 'par') - prj.add_cons('../sources/icestick/led.pcf', 'par') + prj.add_cons('../sources/cons/icestick/clk.pcf', 'par') + prj.add_cons('../sources/cons/icestick/led.pcf', 'par') if args.board == 'edu-ciaa': prj.set_part('hx1k-tq144') prj.add_param('FREQ', '100000000') - prj.add_cons('../sources/edu-ciaa/clk.pcf', 'par') - prj.add_cons('../sources/edu-ciaa/led.pcf', 'par') + prj.add_cons('../sources/cons/edu-ciaa/clk.pcf', 'par') + prj.add_cons('../sources/cons/edu-ciaa/led.pcf', 'par') if args.board == 'orangecrab': prj.set_part('25k-CSFBGA285') prj.add_param('FREQ', '100000000') - prj.add_cons('../sources/orangecrab/clk.lpf', 'par') - prj.add_cons('../sources/orangecrab/led.lpf', 'par') + prj.add_cons('../sources/cons/orangecrab/clk.lpf', 'par') + prj.add_cons('../sources/cons/orangecrab/led.lpf', 'par') if args.board == 'ecp5evn': prj.set_part('um5g-85k-CABGA381') prj.add_param('FREQ', '100000000') - prj.add_cons('../sources/ecp5evn/clk.lpf', 'par') - prj.add_cons('../sources/ecp5evn/led.lpf', 'par') + prj.add_cons('../sources/cons/ecp5evn/clk.lpf', 'par') + prj.add_cons('../sources/cons/ecp5evn/led.lpf', 'par') prj.add_param('FREQ', '100000000') prj.add_param('SECS', '1') diff --git a/examples/projects/quartus.py b/examples/projects/quartus.py index 0453ac8e..ed5e4270 100644 --- a/examples/projects/quartus.py +++ b/examples/projects/quartus.py @@ -22,9 +22,9 @@ if args.board == 'de10nano': prj.set_part('5CSEBA6U23I7') prj.add_param('FREQ', '125000000') - prj.add_cons('../sources/de10nano/clk.sdc', 'syn') - prj.add_cons('../sources/de10nano/clk.tcl', 'par') - prj.add_cons('../sources/de10nano/led.tcl', 'par') + prj.add_cons('../sources/cons/de10nano/clk.sdc', 'syn') + prj.add_cons('../sources/cons/de10nano/clk.tcl', 'par') + prj.add_cons('../sources/cons/de10nano/led.tcl', 'par') prj.add_param('SECS', '1') if args.source == 'vhdl': diff --git a/examples/projects/regress.sh b/examples/projects/regress.sh index f5513b3e..6af2f12b 100644 --- a/examples/projects/regress.sh +++ b/examples/projects/regress.sh @@ -5,7 +5,7 @@ set -e declare -A TOOLS TOOLS["ise"]="s6micro nexys3" -TOOLS["libero"]="maker-board" +TOOLS["libero"]="maker" TOOLS["openflow"]="icestick edu-ciaa orangecrab ecp5evn" TOOLS["quartus"]="de10nano" TOOLS["vivado"]="zybo arty" diff --git a/examples/projects/vivado.py b/examples/projects/vivado.py index 8ae97d6f..1ef6ace7 100644 --- a/examples/projects/vivado.py +++ b/examples/projects/vivado.py @@ -22,15 +22,15 @@ if args.board == 'zybo': prj.set_part('xc7z010-1-clg400') prj.add_param('FREQ', '125000000') - prj.add_cons('../sources/zybo/ZYBO/timing.xdc', 'syn') - prj.add_cons('../sources/zybo/ZYBO/clk.xdc', 'par') - prj.add_cons('../sources/zybo/ZYBO/led.xdc', 'par') + prj.add_cons('../sources/cons/ZYBO/timing.xdc', 'syn') + prj.add_cons('../sources/cons/ZYBO/clk.xdc', 'par') + prj.add_cons('../sources/cons/ZYBO/led.xdc', 'par') if args.board == 'arty': prj.set_part('xc7a35ticsg324-1L') prj.add_param('FREQ', '100000000') - prj.add_cons('../sources/arty/a7-35t/timing.xdc', 'syn') - prj.add_cons('../sources/arty/a7-35t/clk.xdc', 'par') - prj.add_cons('../sources/arty/a7-35t/led.xdc', 'par') + prj.add_cons('../sources/cons/arty_a7_35t/timing.xdc', 'syn') + prj.add_cons('../sources/cons/arty_a7_35t/clk.xdc', 'par') + prj.add_cons('../sources/cons/arty_a7_35t/led.xdc', 'par') prj.add_param('SECS', '1') if args.source == 'vhdl': diff --git a/examples/sources/zybo/ZYBO/clk.xdc b/examples/sources/cons/ZYBO/clk.xdc similarity index 100% rename from examples/sources/zybo/ZYBO/clk.xdc rename to examples/sources/cons/ZYBO/clk.xdc diff --git a/examples/sources/zybo/ZYBO/led.xdc b/examples/sources/cons/ZYBO/led.xdc similarity index 100% rename from examples/sources/zybo/ZYBO/led.xdc rename to examples/sources/cons/ZYBO/led.xdc diff --git a/examples/sources/zybo/ZYBO/timing.xdc b/examples/sources/cons/ZYBO/timing.xdc similarity index 100% rename from examples/sources/zybo/ZYBO/timing.xdc rename to examples/sources/cons/ZYBO/timing.xdc diff --git a/examples/sources/arty/a7-35t/clk.xdc b/examples/sources/cons/arty_a7_35t/clk.xdc similarity index 100% rename from examples/sources/arty/a7-35t/clk.xdc rename to examples/sources/cons/arty_a7_35t/clk.xdc diff --git a/examples/sources/arty/a7-35t/led.xdc b/examples/sources/cons/arty_a7_35t/led.xdc similarity index 100% rename from examples/sources/arty/a7-35t/led.xdc rename to examples/sources/cons/arty_a7_35t/led.xdc diff --git a/examples/sources/arty/a7-35t/timing.xdc b/examples/sources/cons/arty_a7_35t/timing.xdc similarity index 100% rename from examples/sources/arty/a7-35t/timing.xdc rename to examples/sources/cons/arty_a7_35t/timing.xdc diff --git a/examples/sources/de10nano/clk.sdc b/examples/sources/cons/de10nano/clk.sdc similarity index 100% rename from examples/sources/de10nano/clk.sdc rename to examples/sources/cons/de10nano/clk.sdc diff --git a/examples/sources/de10nano/clk.tcl b/examples/sources/cons/de10nano/clk.tcl similarity index 100% rename from examples/sources/de10nano/clk.tcl rename to examples/sources/cons/de10nano/clk.tcl diff --git a/examples/sources/de10nano/led.tcl b/examples/sources/cons/de10nano/led.tcl similarity index 100% rename from examples/sources/de10nano/led.tcl rename to examples/sources/cons/de10nano/led.tcl diff --git a/examples/sources/ecp5evn/clk.lpf b/examples/sources/cons/ecp5evn/clk.lpf similarity index 100% rename from examples/sources/ecp5evn/clk.lpf rename to examples/sources/cons/ecp5evn/clk.lpf diff --git a/examples/sources/ecp5evn/led.lpf b/examples/sources/cons/ecp5evn/led.lpf similarity index 100% rename from examples/sources/ecp5evn/led.lpf rename to examples/sources/cons/ecp5evn/led.lpf diff --git a/examples/sources/edu-ciaa/clk.pcf b/examples/sources/cons/edu-ciaa/clk.pcf similarity index 100% rename from examples/sources/edu-ciaa/clk.pcf rename to examples/sources/cons/edu-ciaa/clk.pcf diff --git a/examples/sources/edu-ciaa/led.pcf b/examples/sources/cons/edu-ciaa/led.pcf similarity index 100% rename from examples/sources/edu-ciaa/led.pcf rename to examples/sources/cons/edu-ciaa/led.pcf diff --git a/examples/sources/icestick/clk.pcf b/examples/sources/cons/icestick/clk.pcf similarity index 100% rename from examples/sources/icestick/clk.pcf rename to examples/sources/cons/icestick/clk.pcf diff --git a/examples/sources/icestick/led.pcf b/examples/sources/cons/icestick/led.pcf similarity index 100% rename from examples/sources/icestick/led.pcf rename to examples/sources/cons/icestick/led.pcf diff --git a/examples/sources/maker-board/clk.pdc b/examples/sources/cons/maker/clk.pdc similarity index 100% rename from examples/sources/maker-board/clk.pdc rename to examples/sources/cons/maker/clk.pdc diff --git a/examples/sources/maker-board/clk.sdc b/examples/sources/cons/maker/clk.sdc similarity index 100% rename from examples/sources/maker-board/clk.sdc rename to examples/sources/cons/maker/clk.sdc diff --git a/examples/sources/maker-board/led.pdc b/examples/sources/cons/maker/led.pdc similarity index 100% rename from examples/sources/maker-board/led.pdc rename to examples/sources/cons/maker/led.pdc diff --git a/examples/sources/nexys3/clk.ucf b/examples/sources/cons/nexys3/clk.ucf similarity index 100% rename from examples/sources/nexys3/clk.ucf rename to examples/sources/cons/nexys3/clk.ucf diff --git a/examples/sources/nexys3/clk.xcf b/examples/sources/cons/nexys3/clk.xcf similarity index 100% rename from examples/sources/nexys3/clk.xcf rename to examples/sources/cons/nexys3/clk.xcf diff --git a/examples/sources/nexys3/led.ucf b/examples/sources/cons/nexys3/led.ucf similarity index 100% rename from examples/sources/nexys3/led.ucf rename to examples/sources/cons/nexys3/led.ucf diff --git a/examples/sources/orangecrab/clk.lpf b/examples/sources/cons/orangecrab/clk.lpf similarity index 100% rename from examples/sources/orangecrab/clk.lpf rename to examples/sources/cons/orangecrab/clk.lpf diff --git a/examples/sources/orangecrab/led.lpf b/examples/sources/cons/orangecrab/led.lpf similarity index 100% rename from examples/sources/orangecrab/led.lpf rename to examples/sources/cons/orangecrab/led.lpf diff --git a/examples/sources/s6micro/clk.ucf b/examples/sources/cons/s6micro/clk.ucf similarity index 100% rename from examples/sources/s6micro/clk.ucf rename to examples/sources/cons/s6micro/clk.ucf diff --git a/examples/sources/s6micro/clk.xcf b/examples/sources/cons/s6micro/clk.xcf similarity index 100% rename from examples/sources/s6micro/clk.xcf rename to examples/sources/cons/s6micro/clk.xcf diff --git a/examples/sources/s6micro/led.ucf b/examples/sources/cons/s6micro/led.ucf similarity index 100% rename from examples/sources/s6micro/led.ucf rename to examples/sources/cons/s6micro/led.ucf From ba268eeab18d12eb8e71532c51a444fda60fd6c9 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 3 Jul 2024 21:19:34 -0300 Subject: [PATCH 139/248] Added a list of similar projects --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index aa17c849..9630e0ce 100644 --- a/README.md +++ b/README.md @@ -65,3 +65,11 @@ pip install -e . > 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. + +## Similar projects + +* [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. +* [tsfpa](https://github.com/tsfpga/tsfpga): a flexible and scalable development platform for modern FPGA projects. From b13cc74292a19e5b4f0b8b174fd0406e148f9995 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 3 Jul 2024 21:20:03 -0300 Subject: [PATCH 140/248] Removed an extra add_param --- examples/projects/openflow.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/projects/openflow.py b/examples/projects/openflow.py index 43c20f0b..8eec90e4 100644 --- a/examples/projects/openflow.py +++ b/examples/projects/openflow.py @@ -40,8 +40,6 @@ prj.add_param('FREQ', '100000000') prj.add_cons('../sources/cons/ecp5evn/clk.lpf', 'par') prj.add_cons('../sources/cons/ecp5evn/led.lpf', 'par') - -prj.add_param('FREQ', '100000000') prj.add_param('SECS', '1') if args.source == 'vhdl': From e153c19e29c1495fb5fdbf106098366ecfa28484 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 3 Jul 2024 21:21:30 -0300 Subject: [PATCH 141/248] Small improvements --- tests/projects/support.py | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/tests/projects/support.py b/tests/projects/support.py index 4ce15596..12cb13c9 100644 --- a/tests/projects/support.py +++ b/tests/projects/support.py @@ -1,5 +1,6 @@ -"""Vivado examples.""" +"""Support examples.""" +import argparse import sys from pyfpga.ise import Ise @@ -9,7 +10,7 @@ from pyfpga.vivado import Vivado -classes = { +tools = { 'ise': Ise, 'libero': Libero, 'openflow': Openflow, @@ -17,18 +18,18 @@ 'vivado': Vivado } -tool = sys.argv[1] if len(sys.argv) > 1 else 'openflow' +parser = argparse.ArgumentParser() +parser.add_argument( + '--tool', default='openflow', + choices=['ise', 'libero', 'quartus', 'openflow', 'vivado'] +) +args = parser.parse_args() -Class = classes.get(tool) - -if Class is None: - sys.exit('Unsupported tool') - -print(f'INFO: the Class Under Test is {Class.__name__}') +print(f'INFO: the Tool Under Test is {args.tool}') try: print('INFO: checking Verilog Includes') - prj = Class() + prj = tools[args.tool]() prj.add_vlog('../../examples/sources/vlog/*.v') prj.set_top('Top') prj.add_define('DEFINE1', '1') @@ -44,7 +45,7 @@ try: print('INFO: checking Verilog Defines') - prj = Class() + prj = tools[args.tool]() prj.add_vlog('../../examples/sources/vlog/*.v') prj.set_top('Top') prj.add_include('../../examples/sources/vlog/include1') @@ -60,7 +61,7 @@ try: print('INFO: checking Verilog Parameters') - prj = Class() + prj = tools[args.tool]() prj.add_vlog('../../examples/sources/vlog/*.v') prj.set_top('Top') prj.add_include('../../examples/sources/vlog/include1') @@ -75,7 +76,7 @@ pass print('INFO: checking Verilog Support') -prj = Class() +prj = tools[args.tool]() prj.add_vlog('../../examples/sources/vlog/*.v') prj.set_top('Top') prj.add_include('../../examples/sources/vlog/include1') @@ -86,9 +87,9 @@ prj.add_param('SECS', '1') prj.make(last='syn') -if tool not in ['ise', 'openflow']: +if args.tool not in ['ise', 'openflow']: print('INFO: checking System Verilog Support') - prj = Class() + prj = tools[args.tool]() prj.add_slog('../../examples/sources/slog/*.sv') prj.set_top('Top') prj.add_include('../../examples/sources/slog/include1') @@ -99,10 +100,10 @@ prj.add_param('SECS', '1') prj.make(last='syn') -if tool not in ['openflow']: +if args.tool not in ['openflow']: try: print('INFO: checking VHDL Generics') - prj = Class() + prj = tools[args.tool]() prj.add_vhdl('../../examples/sources/vhdl/*.vhdl', 'blink_lib') prj.set_top('Top') prj.make(last='syn') @@ -113,7 +114,7 @@ pass print('* VHDL Support') - prj = Class() + prj = tools[args.tool]() prj.add_vhdl('../../examples/sources/vhdl/*.vhdl', 'blink_lib') prj.set_top('Top') prj.add_param('FREQ', '1') From 508f58a5ae6b29ab4855b8fc1210206c4390051d Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 3 Jul 2024 21:22:13 -0300 Subject: [PATCH 142/248] Fix board name --- examples/projects/libero.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/projects/libero.py b/examples/projects/libero.py index 9c570a0e..10d6b36d 100644 --- a/examples/projects/libero.py +++ b/examples/projects/libero.py @@ -18,7 +18,7 @@ prj = Libero(odir='../build/libero') -if args.board == 'maker-board': +if args.board == 'maker': prj.set_part('m2s010-1-tq144') prj.add_param('FREQ', '125000000') prj.add_cons('../sources/cons/maker/clk.sdc', 'syn') From aa342893ea291931f46601bf37d672ab6540c779 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 3 Jul 2024 21:23:06 -0300 Subject: [PATCH 143/248] Improved support for Libero (still WIP) --- pyfpga/libero.py | 35 +++++++++++++-------------------- pyfpga/templates/libero.jinja | 37 +++++++++++++++++++---------------- 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/pyfpga/libero.py b/pyfpga/libero.py index 29fc471a..a6aa6adf 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -31,11 +31,6 @@ def _make_prepare(self, steps): } for step in steps: context[step] = 1 - if 'includes' in self.data: - includes = [] - for include in self.data['includes']: - includes.append(str(include)) - context['INCLUDES'] = ';'.join(includes) files = [] if 'files' in self.data: for file in self.data['files']: @@ -57,21 +52,19 @@ def _make_prepare(self, steps): context['CONSTRAINTS'] = " ".join(constraints) if files: context['FILES'] = '\n'.join(files) - if 'top' in self.data: - context['TOP'] = self.data['top'] - if 'defines' in self.data: - defines = [] - for key, value in self.data['defines'].items(): - defines.append(f'{key}={value}') - context['DEFINES'] = ' '.join(defines) - if 'params' in self.data: - params = [] - for key, value in self.data['params'].items(): - params.append(f'{key}={value}') - context['PARAMS'] = ' '.join(params) + context['INCLUDES'] = self.data.get('includes', None) # ';'.join(includes) + context['TOP'] = self.data.get('top', None) + context['DEFINES'] = self.data.get('defines', None) + context['PARAMS'] = self.data.get('params', None) if 'hooks' in self.data: - for stage in self.data['hooks']: - context[stage.upper()] = '\n'.join(self.data['hooks'][stage]) + context['PRECFG'] = self.data['hooks'].get('precfg', None) + context['POSTCFG'] = self.data['hooks'].get('postcfg', None) + context['PRESYN'] = self.data['hooks'].get('presyn', None) + context['POSTSYN'] = self.data['hooks'].get('postsyn', None) + context['PREPAR'] = self.data['hooks'].get('prepar', None) + context['POSTPAR'] = self.data['hooks'].get('postpar', None) + context['PRESBIT'] = self.data['hooks'].get('prebit', None) + context['POSTBIT'] = self.data['hooks'].get('postbit', None) self._create_file('libero', 'tcl', context) return 'libero SCRIPT:libero.tcl' @@ -118,10 +111,10 @@ def get_info(part): elif len(aux) == 3: device = aux[0] if len(aux[1]) < len(aux[2]): - speed = aux[1] + speed = f'-{aux[1]}' package = aux[2] else: - speed = aux[2] + speed = f'-{aux[2]}' package = aux[1] else: raise ValueError( diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index cc8af1e9..6eb0e090 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -4,41 +4,44 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -#proc fpga_include {PATH} { -# lappend INCLUDED $PATH -# # 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 -#} - #--[ Project configuration ]--------------------------------------------------- {% if CFG %} if { [ file exists {{ PROJECT }} ] } { file delete -force -- {{ PROJECT }} } -new_project -name {{ PROJECT }} -location . -hdl {VHDL} -family {SmartFusion2} +new_project -name {{ PROJECT }} -location {libero} -hdl {VERILOG} -family {SmartFusion2} set_device -family {{ FAMILY }} -die {{ DEVICE }} -package {{ PACKAGE }} -speed {{ SPEED }} {{ PRECFG }} +{% if INCLUDES %}set_global_include_path_order -paths "{{ INCLUDES | join(' ') }}"{% endif %} + {{ FILES }} build_design_hierarchy -{% if TOP %} -set_root {{ TOP }} -{% endif %} +{% if TOP %}set_root {{ TOP }}{% endif %} {% if INCLUDES or DEFINES or PARAMS %} configure_tool -name {SYNTHESIZE} -params {SYNPLIFY_OPTIONS: -{% if INCLUDES %} set_option -include_path "{{ INCLUDES }}"{% endif %} -{% if DEFINES %} set_option -hdl_define "{{ DEFINES }}"{% endif %} -{% if PARAMS %} set_option -hdl_param "{{ PARAMS }}"{% endif %} +{% if INCLUDES %} +{% for INCLUDE in INCLUDES %} + set_option -include_path "{{ INCLUDE }}" +{% endfor %} +{% endif %} +{% if DEFINES %} +{% for KEY, VALUE in DEFINES.items() %} + set_option -hdl_define -set {{ KEY }}={{ VALUE }} +{% endfor %} +{% endif %} +{% if PARAMS %} +{% for KEY, VALUE in PARAMS.items() %} + set_option -hdl_param -set {{ KEY }}={{ VALUE }} +{% endfor %} +{% endif %} } {% endif %} + # Constraints # PDC is only used for PLACEROUTE. # SDC is used by ALL (SYNTHESIZE, PLACEROUTE and VERIFYTIMING). From 0c2ecb35fa33eae06155bc345b57bc9f84257789 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 4 Jul 2024 18:25:51 -0300 Subject: [PATCH 144/248] Modify how to generate an intentional error --- examples/sources/slog/top.sv | 32 ++++++++++++++++++++++++++------ examples/sources/vhdl/top.vhdl | 19 ++++++++++++++++--- examples/sources/vlog/top.v | 32 ++++++++++++++++++++++++++------ 3 files changed, 68 insertions(+), 15 deletions(-) diff --git a/examples/sources/slog/top.sv b/examples/sources/slog/top.sv index cfc700e3..f36d5ee0 100644 --- a/examples/sources/slog/top.sv +++ b/examples/sources/slog/top.sv @@ -12,24 +12,44 @@ module Top #( Blink #(.FREQ (FREQ), .SECS (SECS)) dut (.clk_i (clk_i), .led_o (led_o)); `ifndef INCLUDE1 - Top Top (.clk_i (clk_i), .led_o (led_o)); + reg led; + always @(posedge clk_i) led <= 1'b0; + always @(posedge clk_i) led <= 1'b1; + assign led_o = led; + initial begin $stop; end `endif `ifndef INCLUDE2 - Top Top (.clk_i (clk_i), .led_o (led_o)); + reg led; + always @(posedge clk_i) led <= 1'b0; + always @(posedge clk_i) led <= 1'b1; + assign led_o = led; + initial begin $stop; end `endif `ifndef DEFINE1 - Top Top (.clk_i (clk_i), .led_o (led_o)); + reg led; + always @(posedge clk_i) led <= 1'b0; + always @(posedge clk_i) led <= 1'b1; + assign led_o = led; + initial begin $stop; end `endif `ifndef DEFINE2 - Top Top (.clk_i (clk_i), .led_o (led_o)); + reg led; + always @(posedge clk_i) led <= 1'b0; + always @(posedge clk_i) led <= 1'b1; + assign led_o = led; + initial begin $stop; end `endif generate - if (!FREQ || !SECS) begin: gen_error - Top Top (.clk_i (clk_i), .led_o (led_o)); + 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; end end endgenerate diff --git a/examples/sources/vhdl/top.vhdl b/examples/sources/vhdl/top.vhdl index c6537185..c89ad50e 100644 --- a/examples/sources/vhdl/top.vhdl +++ b/examples/sources/vhdl/top.vhdl @@ -15,15 +15,28 @@ entity Top is 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 - top_i: entity work.Top - 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/top.v b/examples/sources/vlog/top.v index f47433ba..5d3b9562 100644 --- a/examples/sources/vlog/top.v +++ b/examples/sources/vlog/top.v @@ -12,24 +12,44 @@ module Top #( Blink #(.FREQ (FREQ), .SECS (SECS)) dut (.clk_i (clk_i), .led_o (led_o)); `ifndef INCLUDE1 - Top Top (.clk_i (clk_i), .led_o (led_o)); + reg led; + always @(posedge clk_i) led <= 1'b0; + always @(posedge clk_i) led <= 1'b1; + assign led_o = led; + initial begin $stop; end `endif `ifndef INCLUDE2 - Top Top (.clk_i (clk_i), .led_o (led_o)); + reg led; + always @(posedge clk_i) led <= 1'b0; + always @(posedge clk_i) led <= 1'b1; + assign led_o = led; + initial begin $stop; end `endif `ifndef DEFINE1 - Top Top (.clk_i (clk_i), .led_o (led_o)); + reg led; + always @(posedge clk_i) led <= 1'b0; + always @(posedge clk_i) led <= 1'b1; + assign led_o = led; + initial begin $stop; end `endif `ifndef DEFINE2 - Top Top (.clk_i (clk_i), .led_o (led_o)); + reg led; + always @(posedge clk_i) led <= 1'b0; + always @(posedge clk_i) led <= 1'b1; + assign led_o = led; + initial begin $stop; end `endif generate - if (!FREQ || !SECS) begin: gen_error - Top Top (.clk_i (clk_i), .led_o (led_o)); + 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; end end endgenerate From 8b93f66fea27ed7156f49a277a8ef4910ffcf84e Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 4 Jul 2024 18:28:20 -0300 Subject: [PATCH 145/248] Modified to be Libero compatible --- examples/projects/libero.py | 1 + tests/projects/support.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/examples/projects/libero.py b/examples/projects/libero.py index 10d6b36d..4f4360d7 100644 --- a/examples/projects/libero.py +++ b/examples/projects/libero.py @@ -28,6 +28,7 @@ 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') diff --git a/tests/projects/support.py b/tests/projects/support.py index 12cb13c9..d0f4827e 100644 --- a/tests/projects/support.py +++ b/tests/projects/support.py @@ -105,6 +105,7 @@ print('INFO: checking VHDL Generics') prj = tools[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') @@ -116,6 +117,7 @@ print('* VHDL Support') prj = tools[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') From 211bbb7495fa392716a4308344c84e8e0c92533d Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 5 Jul 2024 00:01:53 -0300 Subject: [PATCH 146/248] Finished support for Libero --- pyfpga/libero.py | 25 ++-------------- pyfpga/templates/libero.jinja | 55 ++++++++++------------------------- 2 files changed, 19 insertions(+), 61 deletions(-) diff --git a/pyfpga/libero.py b/pyfpga/libero.py index a6aa6adf..b9a76d60 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -31,28 +31,9 @@ def _make_prepare(self, steps): } for step in steps: context[step] = 1 - files = [] - if 'files' in self.data: - for file in self.data['files']: - if 'lib' in self.data['files'][file]: - lib = self.data['files'][file]['lib'] - files.append( - f'create_links -library {lib} -hdl_source {file}' - ) - else: - files.append(f'create_links -hdl_source {file}') - if 'constraints' in self.data: - constraints = [] - for file in self.data['constraints']: - if file.suffix == '.sdc': - constraints.append(f'create_links -sdc {file}') - else: - constraints.append(f'create_links -io_pdc {file}') - if constraints: - context['CONSTRAINTS'] = " ".join(constraints) - if files: - context['FILES'] = '\n'.join(files) - context['INCLUDES'] = self.data.get('includes', None) # ';'.join(includes) + context['FILES'] = self.data.get('files', None) + context['CONSTRAINTS'] = self.data.get('constraints', None) + context['INCLUDES'] = self.data.get('includes', None) context['TOP'] = self.data.get('top', None) context['DEFINES'] = self.data.get('defines', None) context['PARAMS'] = self.data.get('params', None) diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index 6eb0e090..36db875a 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -9,18 +9,32 @@ {% if CFG %} if { [ file exists {{ PROJECT }} ] } { file delete -force -- {{ PROJECT }} } new_project -name {{ PROJECT }} -location {libero} -hdl {VERILOG} -family {SmartFusion2} - set_device -family {{ FAMILY }} -die {{ DEVICE }} -package {{ PACKAGE }} -speed {{ SPEED }} {{ PRECFG }} {% if INCLUDES %}set_global_include_path_order -paths "{{ INCLUDES | join(' ') }}"{% endif %} -{{ FILES }} +{% for name, attr in FILES.items() %} +create_links -hdl_source {{ name }}{% if 'lib' in attr %} -library {{ attr.lib }}{% endif %} +{% endfor %} +{% for name, attr in CONSTRAINTS.items() %}{% set name_str = name|string %} +create_links {% if name_str.endswith('.sdc') %}-sdc{% else %}-io_pdc{% endif %} {{ name_str }} +{% endfor %} + build_design_hierarchy {% if TOP %}set_root {{ TOP }}{% endif %} +{% set sdc_files = [] %}{% set pdc_files = [] %} +{% for name, attr in CONSTRAINTS.items() %}{% set name_str = name|string %} +{% if name_str.endswith('.sdc') %}{% set _ = sdc_files.append(name_str) %}{% endif %}{% set _ = pdc_files.append(name_str) %} +{% endfor %} + +{% 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 %} configure_tool -name {SYNTHESIZE} -params {SYNPLIFY_OPTIONS: {% if INCLUDES %} @@ -41,43 +55,6 @@ configure_tool -name {SYNTHESIZE} -params {SYNPLIFY_OPTIONS: } {% endif %} - -# 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 -#} - -#organize_tool_files -tool {SYNTHESIZE} \ -# -file ../resources/constraints/maker-board/clk.sdc \ -# -module Blink -input_type {constraint} - -#organize_tool_files -tool {PLACEROUTE} \ -# -file ../resources/constraints/maker-board/clk.sdc \ -# -file ../resources/constraints/maker-board/clk.pdc \ -# -file ../resources/constraints/maker-board/led.pdc \ -# -module Blink -input_type {constraint} - -#organize_tool_files -tool {VERIFYTIMING} \ -# -file ../resources/constraints/maker-board/clk.sdc \ -# -module Blink -input_type {constraint} - {{ POSTCFG }} close_project From c64e9f648295ebc79386853c3d0a633e324a00ac Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 5 Jul 2024 00:07:53 -0300 Subject: [PATCH 147/248] Fix value of speed in a Libero test case --- tests/test_part.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_part.py b/tests/test_part.py index 7fd24ec7..86087946 100644 --- a/tests/test_part.py +++ b/tests/test_part.py @@ -18,7 +18,7 @@ def test_libero(): info = { 'family': 'SmartFusion2', 'device': 'm2s010', - 'speed': '1', + 'speed': '-1', 'package': 'tq144' } assert get_info_libero('m2s010-1-tq144') == info From 7ddd0e50ca019b25de1145b15b02972f63a9e5a5 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 5 Jul 2024 18:27:00 -0300 Subject: [PATCH 148/248] Updated tools versions --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9630e0ce..cfc2e872 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # PyFPGA [![License](https://img.shields.io/badge/License-GPL--3.0-darkgreen?style=flat-square)](LICENSE) -![Vivado](https://img.shields.io/badge/Vivado-2019.2-blue.svg?style=flat-square) -![Quartus](https://img.shields.io/badge/Quartus--Prime-19.1-blue.svg?style=flat-square) -![Libero](https://img.shields.io/badge/Libero--Soc-12.2-blue.svg?style=flat-square) +![Vivado](https://img.shields.io/badge/Vivado-2022.1-blue.svg?style=flat-square) +![Quartus](https://img.shields.io/badge/Quartus--Prime-23.1-blue.svg?style=flat-square) +![Libero](https://img.shields.io/badge/Libero--Soc-2024.1-blue.svg?style=flat-square) ![ISE](https://img.shields.io/badge/ISE-14.7-blue.svg?style=flat-square) ![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) From 154da913850b538f8118e59754458683cfbadec4 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 5 Jul 2024 20:30:42 -0300 Subject: [PATCH 149/248] Add to check basic support based on the Blink module --- tests/projects/support.py | 70 ++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/tests/projects/support.py b/tests/projects/support.py index d0f4827e..13c9b868 100644 --- a/tests/projects/support.py +++ b/tests/projects/support.py @@ -27,8 +27,26 @@ print(f'INFO: the Tool Under Test is {args.tool}') +print('INFO: checking basic Verilog Support') +prj = tools[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 = tools[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') + print('INFO: checking Verilog Includes Support') prj = tools[args.tool]() prj.add_vlog('../../examples/sources/vlog/*.v') prj.set_top('Top') @@ -44,7 +62,7 @@ pass try: - print('INFO: checking Verilog Defines') + print('INFO: checking Verilog Defines Support') prj = tools[args.tool]() prj.add_vlog('../../examples/sources/vlog/*.v') prj.set_top('Top') @@ -60,7 +78,7 @@ pass try: - print('INFO: checking Verilog Parameters') + print('INFO: checking Verilog Parameters Support') prj = tools[args.tool]() prj.add_vlog('../../examples/sources/vlog/*.v') prj.set_top('Top') @@ -75,20 +93,14 @@ except Exception: pass -print('INFO: checking Verilog Support') -prj = tools[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') - if args.tool not in ['ise', 'openflow']: - print('INFO: checking System Verilog Support') + print('INFO: checking basic System Verilog Support') + prj = tools[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 = tools[args.tool]() prj.add_slog('../../examples/sources/slog/*.sv') prj.set_top('Top') @@ -101,6 +113,21 @@ prj.make(last='syn') if args.tool not in ['openflow']: + print('* INFO: checking basic VHDL Support') + prj = tools[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 = tools[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 = tools[args.tool]() @@ -114,13 +141,4 @@ except Exception: pass - print('* VHDL Support') - prj = tools[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') - -print(f'INFO: Class Under Test works as expected') +print(f'INFO: Tool Under Test works as expected') From c00c278073cbac9cefc07da2622b1911a9552f7d Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 5 Jul 2024 20:33:30 -0300 Subject: [PATCH 150/248] Libero support slightly improved --- pyfpga/libero.py | 2 +- pyfpga/templates/libero.jinja | 29 +++++++++++++---------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/pyfpga/libero.py b/pyfpga/libero.py index b9a76d60..813eb3da 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -31,9 +31,9 @@ def _make_prepare(self, steps): } for step in steps: context[step] = 1 + context['INCLUDES'] = self.data.get('includes', None) context['FILES'] = self.data.get('files', None) context['CONSTRAINTS'] = self.data.get('constraints', None) - context['INCLUDES'] = self.data.get('includes', None) context['TOP'] = self.data.get('top', None) context['DEFINES'] = self.data.get('defines', None) context['PARAMS'] = self.data.get('params', None) diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index 36db875a..a0f6259d 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -15,21 +15,24 @@ set_device -family {{ FAMILY }} -die {{ DEVICE }} -package {{ PACKAGE }} -speed {% if INCLUDES %}set_global_include_path_order -paths "{{ INCLUDES | join(' ') }}"{% endif %} -{% for name, attr in FILES.items() %} +{% if FILES %}{% for name, attr in FILES.items() %} create_links -hdl_source {{ name }}{% if 'lib' in attr %} -library {{ attr.lib }}{% endif %} -{% endfor %} -{% for name, attr in CONSTRAINTS.items() %}{% set name_str = name|string %} +{% endfor %}{% endif %} + +{% if CONSTRAINTS %}{% for name, attr in CONSTRAINTS.items() %}{% set name_str = name|string %} create_links {% if name_str.endswith('.sdc') %}-sdc{% else %}-io_pdc{% endif %} {{ name_str }} -{% endfor %} +{% endfor %}{% endif %} build_design_hierarchy {% if TOP %}set_root {{ TOP }}{% endif %} +{% if CONSTRAINTS %} {% set sdc_files = [] %}{% set pdc_files = [] %} {% for name, attr in CONSTRAINTS.items() %}{% set name_str = name|string %} {% if name_str.endswith('.sdc') %}{% set _ = sdc_files.append(name_str) %}{% endif %}{% set _ = pdc_files.append(name_str) %} {% 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 %} @@ -37,21 +40,15 @@ build_design_hierarchy {% if INCLUDES or DEFINES or PARAMS %} configure_tool -name {SYNTHESIZE} -params {SYNPLIFY_OPTIONS: -{% if INCLUDES %} -{% for INCLUDE in INCLUDES %} +{% if INCLUDES %}{% for INCLUDE in INCLUDES %} set_option -include_path "{{ INCLUDE }}" -{% endfor %} -{% endif %} -{% if DEFINES %} -{% for KEY, VALUE in DEFINES.items() %} +{% endfor %}{% endif %} +{% if DEFINES %}{% for KEY, VALUE in DEFINES.items() %} set_option -hdl_define -set {{ KEY }}={{ VALUE }} -{% endfor %} -{% endif %} -{% if PARAMS %} -{% for KEY, VALUE in PARAMS.items() %} +{% endfor %}{% endif %} +{% if PARAMS %}{% for KEY, VALUE in PARAMS.items() %} set_option -hdl_param -set {{ KEY }}={{ VALUE }} -{% endfor %} -{% endif %} +{% endfor %}{% endif %} } {% endif %} From 7963eb1c36bd518b50ec9b175b5e63d114246bff Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 5 Jul 2024 20:34:09 -0300 Subject: [PATCH 151/248] Quartus support improved/simplified --- pyfpga/quartus.py | 57 +++++++++------------------------- pyfpga/templates/quartus.jinja | 41 +++++++++++++++--------- 2 files changed, 40 insertions(+), 58 deletions(-) diff --git a/pyfpga/quartus.py b/pyfpga/quartus.py index 417a497a..10a5927e 100644 --- a/pyfpga/quartus.py +++ b/pyfpga/quartus.py @@ -25,50 +25,21 @@ def _make_prepare(self, steps): } for step in steps: context[step] = 1 - if 'includes' in self.data: - includes = [] - for include in self.data['includes']: - includes.append(str(include)) - context['INCLUDES'] = ' '.join(includes) - files = [] - if 'files' in self.data: - types = { - 'slog': 'SYSTEMVERILOG_FILE', - 'vhdl': 'VHDL_FILE', - 'vlog': 'VERILOG_FILE' - } - for file in self.data['files']: - hdl = self.data['files'][file]['hdl'] - lib = self.data['files'][file].get('lib', None) - typ = types[hdl] - line = f'set_global_assignment -name {typ} {file}' - if lib: - line += f' -library {lib}' - files.append(line) - if 'constraints' in self.data: - for file in self.data['constraints']: - if file.suffix == '.sdc': - line = f'set_global_assignment -name SDC_FILE {file}' - else: - line = f'source {file}' - files.append(line) - if files: - context['FILES'] = '\n'.join(files) - if 'top' in self.data: - context['TOP'] = self.data['top'] - if 'defines' in self.data: - defines = [] - for key, value in self.data['defines'].items(): - defines.append(f'{key} {value}') - context['DEFINES'] = ' '.join(defines) - if 'params' in self.data: - params = [] - for key, value in self.data['params'].items(): - params.append(f'{key} {value}') - context['PARAMS'] = ' '.join(params) + context['INCLUDES'] = self.data.get('includes', None) + context['FILES'] = self.data.get('files', None) + context['CONSTRAINTS'] = self.data.get('constraints', None) + context['TOP'] = self.data.get('top', None) + context['DEFINES'] = self.data.get('defines', None) + context['PARAMS'] = self.data.get('params', None) if 'hooks' in self.data: - for stage in self.data['hooks']: - context[stage.upper()] = '\n'.join(self.data['hooks'][stage]) + context['PRECFG'] = self.data['hooks'].get('precfg', None) + context['POSTCFG'] = self.data['hooks'].get('postcfg', None) + context['PRESYN'] = self.data['hooks'].get('presyn', None) + context['POSTSYN'] = self.data['hooks'].get('postsyn', None) + context['PREPAR'] = self.data['hooks'].get('prepar', None) + context['POSTPAR'] = self.data['hooks'].get('postpar', None) + context['PRESBIT'] = self.data['hooks'].get('prebit', None) + context['POSTBIT'] = self.data['hooks'].get('postbit', None) self._create_file('quartus', 'tcl', context) return 'quartus_sh --script quartus.tcl' diff --git a/pyfpga/templates/quartus.jinja b/pyfpga/templates/quartus.jinja index 4b98174a..3f97e363 100644 --- a/pyfpga/templates/quartus.jinja +++ b/pyfpga/templates/quartus.jinja @@ -13,26 +13,37 @@ set_global_assignment -name DEVICE {{ PART }} {{ PRECFG }} -{{ FILES }} - -{% if TOP %} -set_global_assignment -name TOP_LEVEL_ENTITY {{ TOP }} +{% if FILES %}{% 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 DEFINES %} -set defines [dict create {{ DEFINES }}] -foreach {key value} $defines {set_global_assignment -name VERILOG_MACRO "$key=$value"} +{% if CONSTRAINTS %}{% for name, attr in CONSTRAINTS.items() %}{% set name_str = name|string %} +{% if name_str.endswith('.sdc') %} +set_global_assignment -name SDC_FILE {{ name_str }} +{% else %} +source {{ name_str }} {% endif %} +{% endfor %}{% endif %} -{% if INCLUDES %} -set includes { {{ INCLUDES}} } -foreach value $includes {set_global_assignment -name SEARCH_PATH $value} -{% endif %} +{% if TOP %}set_global_assignment -name TOP_LEVEL_ENTITY {{ TOP }}{% endif %} -{% if PARAMS %} -set params [dict create {{ PARAMS }}] -foreach {key value} $params {set_parameter -name $key $value} -{% endif %} +{% if INCLUDES %}{% for INCLUDE in INCLUDES %} +set_global_assignment -name SEARCH_PATH {{ INCLUDE }} +{% endfor %}{% endif %} + +{% if DEFINES %}{% for KEY, VALUE in DEFINES.items() %} +set_global_assignment -name VERILOG_MACRO {{ KEY }}={{ VALUE }} +{% endfor %}{% endif %} + +{% if PARAMS %}{% for KEY, VALUE in PARAMS.items() %} +set_parameter -name {{ KEY }} {{ VALUE }} +{% endfor %}{% endif %} {{ POSTCFG }} From 250409906a0c8f3466097e64b1f55aff57097812 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 5 Jul 2024 22:00:58 -0300 Subject: [PATCH 152/248] Applied a few format fixes --- pyfpga/templates/libero.jinja | 8 ++++---- pyfpga/templates/quartus.jinja | 22 +++++++++++----------- pyfpga/templates/vivado.jinja | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index a0f6259d..3d961e51 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -43,11 +43,11 @@ configure_tool -name {SYNTHESIZE} -params {SYNPLIFY_OPTIONS: {% if INCLUDES %}{% for INCLUDE in INCLUDES %} set_option -include_path "{{ INCLUDE }}" {% endfor %}{% endif %} -{% if DEFINES %}{% for KEY, VALUE in DEFINES.items() %} - set_option -hdl_define -set {{ KEY }}={{ VALUE }} +{% if DEFINES %}{% for key, value in DEFINES.items() %} + set_option -hdl_define -set {{ key }}={{ value }} {% endfor %}{% endif %} -{% if PARAMS %}{% for KEY, VALUE in PARAMS.items() %} - set_option -hdl_param -set {{ KEY }}={{ VALUE }} +{% if PARAMS %}{% for key, value in PARAMS.items() %} + set_option -hdl_param -set {{ key }}={{ value }} {% endfor %}{% endif %} } {% endif %} diff --git a/pyfpga/templates/quartus.jinja b/pyfpga/templates/quartus.jinja index 3f97e363..1caa2108 100644 --- a/pyfpga/templates/quartus.jinja +++ b/pyfpga/templates/quartus.jinja @@ -13,13 +13,13 @@ set_global_assignment -name DEVICE {{ PART }} {{ PRECFG }} -{% if FILES %}{% 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 }} +{% if FILES %}{% 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 %} @@ -37,12 +37,12 @@ source {{ name_str }} set_global_assignment -name SEARCH_PATH {{ INCLUDE }} {% endfor %}{% endif %} -{% if DEFINES %}{% for KEY, VALUE in DEFINES.items() %} -set_global_assignment -name VERILOG_MACRO {{ KEY }}={{ VALUE }} +{% if DEFINES %}{% for key, value in DEFINES.items() %} +set_global_assignment -name VERILOG_MACRO {{ key }}={{ value }} {% endfor %}{% endif %} -{% if PARAMS %}{% for KEY, VALUE in PARAMS.items() %} -set_parameter -name {{ KEY }} {{ VALUE }} +{% if PARAMS %}{% for key, value in PARAMS.items() %} +set_parameter -name {{ key }} {{ value }} {% endfor %}{% endif %} {{ POSTCFG }} diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index 8f0cbb8e..ffef4aa8 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -4,7 +4,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -#--[ Project configuration ]-------------------------------------------------- +#--[ Project configuration ]--------------------------------------------------- {% if CFG %} create_project -force {{ PROJECT }} From cae0d326fe1c903787767879970704aca96fd8eb Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 5 Jul 2024 22:01:48 -0300 Subject: [PATCH 153/248] ISE support improved/simplified --- pyfpga/ise.py | 52 ++++++++++---------------------------- pyfpga/templates/ise.jinja | 30 ++++++++++++---------- 2 files changed, 31 insertions(+), 51 deletions(-) diff --git a/pyfpga/ise.py b/pyfpga/ise.py index a964af7c..9e173f2f 100644 --- a/pyfpga/ise.py +++ b/pyfpga/ise.py @@ -31,45 +31,21 @@ def _make_prepare(self, steps): } for step in steps: context[step] = 1 - if 'includes' in self.data: - includes = [] - for include in self.data['includes']: - includes.append(str(include)) - context['INCLUDES'] = '|'.join(includes) - files = [] - if 'files' in self.data: - for file in self.data['files']: - if 'lib' in self.data['files'][file]: - lib = self.data['files'][file]['lib'] - files.append(f'lib_vhdl new {lib}') - files.append(f'xfile add {file} -lib_vhdl {lib}') - else: - files.append(f'xfile add {file}') - if 'constraints' in self.data: - constraints = [] - for file in self.data['constraints']: - files.append(f'xfile add {file}') - if file.suffix == '.xcf': - constraints.append(str(file)) - if constraints: - context['CONSTRAINTS'] = " ".join(constraints) - if files: - context['FILES'] = '\n'.join(files) - if 'top' in self.data: - context['TOP'] = self.data['top'] - if 'defines' in self.data: - defines = [] - for key, value in self.data['defines'].items(): - defines.append(f'{key}={value}') - context['DEFINES'] = ' | '.join(defines) - if 'params' in self.data: - params = [] - for key, value in self.data['params'].items(): - params.append(f'{key}={value}') - context['PARAMS'] = ' '.join(params) + context['INCLUDES'] = self.data.get('includes', None) + context['FILES'] = self.data.get('files', None) + context['CONSTRAINTS'] = self.data.get('constraints', None) + context['TOP'] = self.data.get('top', None) + context['DEFINES'] = self.data.get('defines', None) + context['PARAMS'] = self.data.get('params', None) if 'hooks' in self.data: - for stage in self.data['hooks']: - context[stage.upper()] = '\n'.join(self.data['hooks'][stage]) + context['PRECFG'] = self.data['hooks'].get('precfg', None) + context['POSTCFG'] = self.data['hooks'].get('postcfg', None) + context['PRESYN'] = self.data['hooks'].get('presyn', None) + context['POSTSYN'] = self.data['hooks'].get('postsyn', None) + context['PREPAR'] = self.data['hooks'].get('prepar', None) + context['POSTPAR'] = self.data['hooks'].get('postpar', None) + context['PRESBIT'] = self.data['hooks'].get('prebit', None) + context['POSTBIT'] = self.data['hooks'].get('postbit', None) self._create_file('ise', 'tcl', context) return 'xtclsh ise.tcl' diff --git a/pyfpga/templates/ise.jinja b/pyfpga/templates/ise.jinja index 701a028d..d72a26ae 100644 --- a/pyfpga/templates/ise.jinja +++ b/pyfpga/templates/ise.jinja @@ -4,7 +4,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -#--[ Project configuration ]-------------------------------------------------- +#--[ Project configuration ]--------------------------------------------------- {% if CFG %} if { [ file exists {{ PROJECT }}.xise ] } { file delete {{ PROJECT }}.xise } @@ -16,26 +16,30 @@ project set speed -{{ SPEED }} {{ PRECFG }} -{{ FILES }} -{% if CONSTRAINTS %} -project set "Synthesis Constraints File" "{{ CONSTRAINTS }}" -process "Synthesize - XST" -{% endif %} +{% if FILES %}{% 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 TOP %} -project set top {{ TOP }} +{% if CONSTRAINTS %}{% for name, attr in CONSTRAINTS.items() %}{% set name_str = name|string %} +xfile add {{ name_str }} +{% if name_str.endswith('.xcf') %} +project set "Synthesis Constraints File" "{{ name_str }}" -process "Synthesize - XST" {% endif %} +{% endfor %}{% endif %} -{% if DEFINES %} -project set "Verilog Macros" "{{ DEFINES }}" -process "Synthesize - XST" -{% endif %} +{% if TOP %}project set top {{ TOP }}{% endif %} {% if INCLUDES %} -project set "Verilog Include Directories" "{{ INCLUDES }}" -process "Synthesize - XST" -# [join $INCLUDED "|"] +project set "Verilog Include Directories" "{{ INCLUDES | join('|') }}" -process "Synthesize - XST" +{% endif %} + +{% if DEFINES %} +project set "Verilog Macros" "{{ DEFINES.items() | map('join', '=') | join(' | ') }}" -process "Synthesize - XST" {% endif %} {% if PARAMS %} -project set "Generics, Parameters" "{{ PARAMS }}" -process "Synthesize - XST" +project set "Generics, Parameters" "{{ PARAMS.items() | map('join', '=') | join(' ') }}" -process "Synthesize - XST" {% endif %} {{ POSTCFG }} From d0226be81eaaaa949da142d7ff0f28a1351fd3ac Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 6 Jul 2024 00:09:38 -0300 Subject: [PATCH 154/248] Fix intentional error generation for Vivado --- examples/sources/slog/top.sv | 10 +++++----- examples/sources/vlog/top.v | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/sources/slog/top.sv b/examples/sources/slog/top.sv index f36d5ee0..732ae368 100644 --- a/examples/sources/slog/top.sv +++ b/examples/sources/slog/top.sv @@ -16,7 +16,7 @@ module Top #( always @(posedge clk_i) led <= 1'b0; always @(posedge clk_i) led <= 1'b1; assign led_o = led; - initial begin $stop; end + initial begin $stop; $error("intentional"); end `endif `ifndef INCLUDE2 @@ -24,7 +24,7 @@ module Top #( always @(posedge clk_i) led <= 1'b0; always @(posedge clk_i) led <= 1'b1; assign led_o = led; - initial begin $stop; end + initial begin $stop; $error("intentional"); end `endif `ifndef DEFINE1 @@ -32,7 +32,7 @@ module Top #( always @(posedge clk_i) led <= 1'b0; always @(posedge clk_i) led <= 1'b1; assign led_o = led; - initial begin $stop; end + initial begin $stop; $error("intentional"); end `endif `ifndef DEFINE2 @@ -40,7 +40,7 @@ module Top #( always @(posedge clk_i) led <= 1'b0; always @(posedge clk_i) led <= 1'b1; assign led_o = led; - initial begin $stop; end + initial begin $stop; $error("intentional"); end `endif generate @@ -49,7 +49,7 @@ module Top #( always @(posedge clk_i) led <= 1'b0; always @(posedge clk_i) led <= 1'b1; assign led_o = led; - initial begin $stop; end + initial begin $stop; $error("intentional"); end end endgenerate diff --git a/examples/sources/vlog/top.v b/examples/sources/vlog/top.v index 5d3b9562..9a6d452b 100644 --- a/examples/sources/vlog/top.v +++ b/examples/sources/vlog/top.v @@ -16,7 +16,7 @@ module Top #( always @(posedge clk_i) led <= 1'b0; always @(posedge clk_i) led <= 1'b1; assign led_o = led; - initial begin $stop; end + initial begin $stop; $error("intentional"); end `endif `ifndef INCLUDE2 @@ -24,7 +24,7 @@ module Top #( always @(posedge clk_i) led <= 1'b0; always @(posedge clk_i) led <= 1'b1; assign led_o = led; - initial begin $stop; end + initial begin $stop; $error("intentional"); end `endif `ifndef DEFINE1 @@ -32,7 +32,7 @@ module Top #( always @(posedge clk_i) led <= 1'b0; always @(posedge clk_i) led <= 1'b1; assign led_o = led; - initial begin $stop; end + initial begin $stop; $error("intentional"); end `endif `ifndef DEFINE2 @@ -40,7 +40,7 @@ module Top #( always @(posedge clk_i) led <= 1'b0; always @(posedge clk_i) led <= 1'b1; assign led_o = led; - initial begin $stop; end + initial begin $stop; $error("intentional"); end `endif generate @@ -49,7 +49,7 @@ module Top #( always @(posedge clk_i) led <= 1'b0; always @(posedge clk_i) led <= 1'b1; assign led_o = led; - initial begin $stop; end + initial begin $stop; $error("intentional"); end end endgenerate From fbd52b16cf3050d6038a31e14cb89ed50058796e Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 6 Jul 2024 00:10:05 -0300 Subject: [PATCH 155/248] Vivado support improved/simplified --- pyfpga/templates/vivado.jinja | 29 ++++++++++++------ pyfpga/vivado.py | 58 +++++++++-------------------------- 2 files changed, 34 insertions(+), 53 deletions(-) diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index ffef4aa8..6a40c459 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -14,22 +14,33 @@ set_property PART {{ PART }} [current_project] {{ PRECFG }} -{{ FILES }} - -{% if TOP %} -set_property TOP {{ TOP }} [current_fileset] +{% if FILES %}{% 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 %}{% for name, attr in CONSTRAINTS.items() %} +add_file -fileset constrs_1 {{ name }} +{% if attr == "syn" %} +set_property USED_IN_IMPLEMENTATION FALSE [get_files {{ name }}] +{% elif attr == "par" %} +set_property USED_IN_SYNTHESIS FALSE [get_files {{ name }}] {% endif %} +{% if loop.first %}set_property TARGET_CONSTRS_FILE {{ name }} [current_fileset -constrset]{% endif %} +{% endfor %}{% endif %} -{% if DEFINES %} -set_property VERILOG_DEFINE { {{ DEFINES}} } [current_fileset] -{% endif %} +{% if TOP %}set_property TOP {{ TOP }} [current_fileset]{% endif %} {% if INCLUDES %} -set_property INCLUDE_DIRS { {{ INCLUDES}} } [current_fileset] +set_property INCLUDE_DIRS { {{ INCLUDES | join(' ') }} } [current_fileset] +{% endif %} + +{% if DEFINES %} +set_property VERILOG_DEFINE { {{ DEFINES.items() | map('join', '=') | join(' ') }} } [current_fileset] {% endif %} {% if PARAMS %} -set_property GENERIC { {{ PARAMS }} } -objects [get_filesets sources_1] +set_property GENERIC { {{ PARAMS.items() | map('join', '=') | join(' ') }} } -objects [get_filesets sources_1] {% endif %} {{ POSTCFG }} diff --git a/pyfpga/vivado.py b/pyfpga/vivado.py index 1858ed82..c0a48864 100644 --- a/pyfpga/vivado.py +++ b/pyfpga/vivado.py @@ -24,51 +24,21 @@ def _make_prepare(self, steps): } for step in steps: context[step] = 1 - if 'includes' in self.data: - includes = [] - for include in self.data['includes']: - includes.append(str(include)) - context['INCLUDES'] = ' '.join(includes) - files = [] - if 'files' in self.data: - for file in self.data['files']: - files.append(f'add_file {file}') - for file in self.data['files']: - if 'lib' in self.data['files'][file]: - lib = self.data['files'][file]['lib'] - files.append( - f'set_property library {lib} [get_files {file}]' - ) - if 'constraints' in self.data: - for file in self.data['constraints']: - files.append(f'add_file -fileset constrs_1 {file}') - for file in self.data['constraints']: - if self.data['constraints'][file] == 'syn': - prop = 'USED_IN_IMPLEMENTATION FALSE' - if self.data['constraints'][file] == 'syn': - prop = 'USED_IN_SYNTHESIS FALSE' - if self.data['constraints'][file] != 'all': - files.append(f'set_property {prop} [get_files {file}]') - first = next(iter(self.data['constraints'])) - prop = f'TARGET_CONSTRS_FILE {first}' - files.append(f'set_property {prop} [current_fileset -constrset]') - if files: - context['FILES'] = '\n'.join(files) - if 'top' in self.data: - context['TOP'] = self.data['top'] - if 'defines' in self.data: - defines = [] - for key, value in self.data['defines'].items(): - defines.append(f'{key}={value}') - context['DEFINES'] = ' '.join(defines) - if 'params' in self.data: - params = [] - for key, value in self.data['params'].items(): - params.append(f'{key}={value}') - context['PARAMS'] = ' '.join(params) + context['INCLUDES'] = self.data.get('includes', None) + context['FILES'] = self.data.get('files', None) + context['CONSTRAINTS'] = self.data.get('constraints', None) + context['TOP'] = self.data.get('top', None) + context['DEFINES'] = self.data.get('defines', None) + context['PARAMS'] = self.data.get('params', None) if 'hooks' in self.data: - for stage in self.data['hooks']: - context[stage.upper()] = '\n'.join(self.data['hooks'][stage]) + context['PRECFG'] = self.data['hooks'].get('precfg', None) + context['POSTCFG'] = self.data['hooks'].get('postcfg', None) + context['PRESYN'] = self.data['hooks'].get('presyn', None) + context['POSTSYN'] = self.data['hooks'].get('postsyn', None) + context['PREPAR'] = self.data['hooks'].get('prepar', None) + context['POSTPAR'] = self.data['hooks'].get('postpar', None) + context['PRESBIT'] = self.data['hooks'].get('prebit', None) + context['POSTBIT'] = self.data['hooks'].get('postbit', None) self._create_file('vivado', 'tcl', context) return 'vivado -mode batch -notrace -quiet -source vivado.tcl' From 8d2a067f4bd8374b6e7e7fda54dac8cd866eb0d8 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 6 Jul 2024 13:42:33 -0300 Subject: [PATCH 156/248] Openflow support improved/simplified --- pyfpga/openflow.py | 56 +++++++++------------------------ pyfpga/templates/openflow.jinja | 18 ++++++----- 2 files changed, 25 insertions(+), 49 deletions(-) diff --git a/pyfpga/openflow.py b/pyfpga/openflow.py index 51dcd84a..a2e4092d 100644 --- a/pyfpga/openflow.py +++ b/pyfpga/openflow.py @@ -28,49 +28,21 @@ def _make_prepare(self, steps): } for step in steps: context[step] = 1 - if 'includes' in self.data: - includes = [] - for include in self.data['includes']: - includes.append(f'-I{str(include)}') - context['INCLUDES'] = ' '.join(includes) - files = [] - if 'files' in self.data: - for file in self.data['files']: - files.append(f'read_verilog -defer {file}') - if files: - context['VLOGS'] = '\n'.join(files) -# 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]}') -# if len(vhdls) > 0: -# verilogs = [f'ghdl $FLAGS {self.top}'] - if 'constraints' in self.data: - constraints = [] - for constraint in self.data['constraints']: - constraints.append(str(constraint)) - context['CONSTRAINTS'] = ' '.join(constraints) - if 'top' in self.data: - context['TOP'] = self.data['top'] - if 'defines' in self.data: - defines = [] - for key, value in self.data['defines'].items(): - defines.append(f'-D{key}={value}') - context['DEFINES'] = ' '.join(defines) - if 'params' in self.data: - params = [] - for key, value in self.data['params'].items(): - params.append(f'-set {key} {value}') - context['PARAMS'] = ' '.join(params) + context['INCLUDES'] = self.data.get('includes', None) + context['FILES'] = self.data.get('files', None) + context['CONSTRAINTS'] = self.data.get('constraints', None) + context['TOP'] = self.data.get('top', None) + context['DEFINES'] = self.data.get('defines', None) + context['PARAMS'] = self.data.get('params', None) if 'hooks' in self.data: - for stage in self.data['hooks']: - context[stage.upper()] = '\n'.join(self.data['hooks'][stage]) + context['PRECFG'] = self.data['hooks'].get('precfg', None) + context['POSTCFG'] = self.data['hooks'].get('postcfg', None) + context['PRESYN'] = self.data['hooks'].get('presyn', None) + context['POSTSYN'] = self.data['hooks'].get('postsyn', None) + context['PREPAR'] = self.data['hooks'].get('prepar', None) + context['POSTPAR'] = self.data['hooks'].get('postpar', None) + context['PRESBIT'] = self.data['hooks'].get('prebit', None) + context['POSTBIT'] = self.data['hooks'].get('postbit', None) self._create_file('openflow', 'sh', context) return 'bash openflow.sh' diff --git a/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja index f9b3ba05..d29cbbc5 100644 --- a/pyfpga/templates/openflow.jinja +++ b/pyfpga/templates/openflow.jinja @@ -13,18 +13,22 @@ DOCKER="docker run --user $(id -u):$(id -g) --rm -v $HOME:$HOME -w $PWD" {% if SYN %} $DOCKER hdlc/ghdl:yosys /bin/bash -c " {{ PRESYN }} -{{ VHDLS }} yosys -Q -m ghdl -p ' {% if INCLUDES %} -verilog_defaults -add {{ INCLUDES }} +verilog_defaults -add{% for path in INCLUDES %} -I{{ path }}{% endfor %} {% endif %} {% if DEFINES %} -verilog_defines {{ DEFINES }} +verilog_defines{% for key, value in DEFINES.items() %} -D{{ key }}={{ value }}{% endfor %} {% endif %} -{{ VLOGS }} -{{ SLOGS }} +{% if FILES %}{% for name, attr in FILES.items() %} +{% if attr.hdl == "vlog" %} +read_verilog -defer {{ name }} +{% elif attr.hdl == "slog" %} +read_verilog -defer -sv {{ name }} +{% endif %} +{% endfor %}{% endif %} {% if PARAMS %} -chparam {{ PARAMS }} {{ TOP }} +chparam{% for key, value in PARAMS.items() %} -set {{ key }} {{ value }}{% endfor %} {% endif %} synth -top {{ TOP }} synth_{{ FAMILY }} -top {{ TOP }} -json {{ PROJECT }}.json @@ -54,7 +58,7 @@ synth_{{ FAMILY }} -top {{ TOP }} -json {{ PROJECT }}.json {% if PAR %} -CONSTRAINTS="{{ CONSTRAINTS }}" +CONSTRAINTS="{{ CONSTRAINTS | join(' ') }}" {% if FAMILY == 'ice40' %} if [ -n "$CONSTRAINTS" ]; then From 6936d14cef5bc2e56ca4374b107b4f9ac3999782 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 6 Jul 2024 14:09:16 -0300 Subject: [PATCH 157/248] Fix SV files inclusion --- examples/projects/openflow.py | 2 +- examples/projects/quartus.py | 2 +- examples/projects/vivado.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/projects/openflow.py b/examples/projects/openflow.py index 8eec90e4..b40f6c96 100644 --- a/examples/projects/openflow.py +++ b/examples/projects/openflow.py @@ -51,7 +51,7 @@ if args.source == 'slog': prj.add_include('../sources/slog/include1') prj.add_include('../sources/slog/include2') - prj.add_vlog('../sources/slog/*.sv') + prj.add_slog('../sources/slog/*.sv') if args.source in ['vlog', 'slog']: prj.add_define('DEFINE1', '1') prj.add_define('DEFINE2', '1') diff --git a/examples/projects/quartus.py b/examples/projects/quartus.py index ed5e4270..07cfedc4 100644 --- a/examples/projects/quartus.py +++ b/examples/projects/quartus.py @@ -36,7 +36,7 @@ if args.source == 'slog': prj.add_include('../sources/slog/include1') prj.add_include('../sources/slog/include2') - prj.add_vlog('../sources/slog/*.sv') + prj.add_slog('../sources/slog/*.sv') if args.source in ['vlog', 'slog']: prj.add_define('DEFINE1', '1') prj.add_define('DEFINE2', '1') diff --git a/examples/projects/vivado.py b/examples/projects/vivado.py index 1ef6ace7..367f2861 100644 --- a/examples/projects/vivado.py +++ b/examples/projects/vivado.py @@ -42,7 +42,7 @@ if args.source == 'slog': prj.add_include('../sources/slog/include1') prj.add_include('../sources/slog/include2') - prj.add_vlog('../sources/slog/*.sv') + prj.add_slog('../sources/slog/*.sv') if args.source in ['vlog', 'slog']: prj.add_define('DEFINE1', '1') prj.add_define('DEFINE2', '1') From ed6257b0bb4c4f8cdce9bfe5b966a6eafdd143f1 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 7 Jul 2024 21:49:29 -0300 Subject: [PATCH 158/248] Modified to try SV for Openflow --- tests/projects/support.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/projects/support.py b/tests/projects/support.py index 13c9b868..4aa0d009 100644 --- a/tests/projects/support.py +++ b/tests/projects/support.py @@ -93,7 +93,7 @@ except Exception: pass -if args.tool not in ['ise', 'openflow']: +if args.tool not in ['ise']: print('INFO: checking basic System Verilog Support') prj = tools[args.tool]() prj.add_slog('../../examples/sources/slog/blink.sv') From f249f1431c25e1a9147a0aa6513efa4300c4a6ed Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 7 Jul 2024 22:02:31 -0300 Subject: [PATCH 159/248] Cosmetic changes to Jinja templates --- pyfpga/templates/ise-prog.jinja | 10 ++++ pyfpga/templates/ise.jinja | 45 ++++++++++++--- pyfpga/templates/libero-prog.jinja | 1 + pyfpga/templates/libero.jinja | 82 ++++++++++++++++++++++------ pyfpga/templates/openflow-prog.jinja | 1 + pyfpga/templates/openflow.jinja | 25 +++++++-- pyfpga/templates/quartus-prog.jinja | 2 + pyfpga/templates/quartus.jinja | 63 ++++++++++++++++----- pyfpga/templates/vivado-prog.jinja | 2 + pyfpga/templates/vivado.jinja | 47 +++++++++++++--- 10 files changed, 228 insertions(+), 50 deletions(-) diff --git a/pyfpga/templates/ise-prog.jinja b/pyfpga/templates/ise-prog.jinja index 2ba0c193..0f0e38c9 100644 --- a/pyfpga/templates/ise-prog.jinja +++ b/pyfpga/templates/ise-prog.jinja @@ -1,11 +1,15 @@ +{# # # Copyright (C) 2015-2024 Rodrigo A. Melo # # SPDX-License-Identifier: GPL-3.0-or-later # +#} cleancablelock +{# ------------------------------------------------------------------------- #} + {% if FPGA %} setMode -bs setCable -port auto @@ -14,6 +18,8 @@ assignFile -p {{ POSITION }} -file {{ BITSTREAM }} Program -p {{ POSITION }} {% endif %} +{# ------------------------------------------------------------------------- #} + {% if SPI %} setMode -pff addConfigDevice -name {{ NAME }} -path . @@ -31,6 +37,8 @@ assignfiletoattachedflash -position {{ POSITION }} -file ./{{ NAME }}.mcs Program -p {{ POSITION }} -dataWidth {{ WIDTH }} -spionly -e -v -loadfpga {% endif %} +{# ------------------------------------------------------------------------- #} + {% if BPI %} setMode -pff addConfigDevice -name {{ NAME }} -path . @@ -49,4 +57,6 @@ 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 index d72a26ae..10811ebf 100644 --- a/pyfpga/templates/ise.jinja +++ b/pyfpga/templates/ise.jinja @@ -1,12 +1,17 @@ +{# # # Copyright (C) 2015-2024 Rodrigo A. Melo # # SPDX-License-Identifier: GPL-3.0-or-later # +#} -#--[ Project configuration ]--------------------------------------------------- +{# ------------------------------------------------------------------------- #} +{# Project configuration #} +{# ------------------------------------------------------------------------- #} {% if CFG %} + if { [ file exists {{ PROJECT }}.xise ] } { file delete {{ PROJECT }}.xise } project new {{ PROJECT }}.xise project set family {{ FAMILY }} @@ -16,19 +21,26 @@ project set speed -{{ SPEED }} {{ PRECFG }} -{% if FILES %}{% for name, attr in FILES.items() %} +{% if FILES %} +{% 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 %} +{% endfor %} +{% endif %} -{% if CONSTRAINTS %}{% for name, attr in CONSTRAINTS.items() %}{% set name_str = name|string %} +{% if CONSTRAINTS %} +{% for name, attr in CONSTRAINTS.items() %} +{% set name_str = name|string %} xfile add {{ name_str }} {% if name_str.endswith('.xcf') %} project set "Synthesis Constraints File" "{{ name_str }}" -process "Synthesize - XST" {% endif %} -{% endfor %}{% endif %} +{% endfor %} +{% endif %} -{% if TOP %}project set top {{ TOP }}{% endif %} +{% if TOP %} +project set top {{ TOP }} +{% endif %} {% if INCLUDES %} project set "Verilog Include Directories" "{{ INCLUDES | join('|') }}" -process "Synthesize - XST" @@ -45,14 +57,21 @@ project set "Generics, Parameters" "{{ PARAMS.items() | map('join', '=') | join( {{ POSTCFG }} project close + {% endif %} -#--[ Design flow ]------------------------------------------------------------- +{# ------------------------------------------------------------------------- #} +{# Design flow #} +{# ------------------------------------------------------------------------- #} {% if SYN or PAR or BIT %} + project open {{ PROJECT }}.xise +{# Synthesis --------------------------------------------------------------- #} + {% if SYN %} + {{ PRESYN }} # PRESYNTH @@ -62,9 +81,13 @@ process run "Synthesize" if { [process get "Synthesize" status] == "errors" } { exit 1 } {{ POSTSYN }} + {% endif %} +{# Place and Route --------------------------------------------------------- #} + {% if PAR %} + {{ PREPAR }} process run "Translate" @@ -75,9 +98,13 @@ process run "Place & Route" if { [process get "Place & Route" status] == "errors" } { exit 1 } {{ POSTPAR }} + {% endif %} +{# Bitstream generation ---------------------------------------------------- #} + {% if BIT %} + {{ PREBIT }} process run "Generate Programming File" @@ -85,7 +112,11 @@ if { [process get "Generate Programming File" status] == "errors" } { exit 1 } catch { file rename -force {{ TOP }}.bit {{ PROJECT }}.bit } {{ POSTBIT }} + {% endif %} +{# ------------------------------------------------------------------------- #} + project close + {% endif %} diff --git a/pyfpga/templates/libero-prog.jinja b/pyfpga/templates/libero-prog.jinja index 95b740f0..5bd27ab5 100644 --- a/pyfpga/templates/libero-prog.jinja +++ b/pyfpga/templates/libero-prog.jinja @@ -3,6 +3,7 @@ # # SPDX-License-Identifier: GPL-3.0-or-later # +#} # open_project -file {$TEMPDIR/libero.prjx} # run_tool -name {CONFIGURE_CHAIN} -script {$TEMPDIR/flashpro.tcl} diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index 3d961e51..f62d78dc 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -1,36 +1,55 @@ +{# # # Copyright (C) 2015-2024 Rodrigo A. Melo # # SPDX-License-Identifier: GPL-3.0-or-later # +#} -#--[ Project configuration ]--------------------------------------------------- +{# ------------------------------------------------------------------------- #} +{# Project configuration #} +{# ------------------------------------------------------------------------- #} {% if CFG %} + if { [ file exists {{ PROJECT }} ] } { file delete -force -- {{ PROJECT }} } new_project -name {{ PROJECT }} -location {libero} -hdl {VERILOG} -family {SmartFusion2} set_device -family {{ FAMILY }} -die {{ DEVICE }} -package {{ PACKAGE }} -speed {{ SPEED }} {{ PRECFG }} -{% if INCLUDES %}set_global_include_path_order -paths "{{ INCLUDES | join(' ') }}"{% endif %} +{% if INCLUDES %} +set_global_include_path_order -paths "{{ INCLUDES | join(' ') }}" +{% endif %} -{% if FILES %}{% for name, attr in FILES.items() %} +{% if FILES %} +{% for name, attr in FILES.items() %} create_links -hdl_source {{ name }}{% if 'lib' in attr %} -library {{ attr.lib }}{% endif %} -{% endfor %}{% endif %} +{% endfor %} +{% endif %} -{% if CONSTRAINTS %}{% for name, attr in CONSTRAINTS.items() %}{% set name_str = name|string %} +{% if CONSTRAINTS %} +{% for name, attr in CONSTRAINTS.items() %} +{% set name_str = name|string %} create_links {% if name_str.endswith('.sdc') %}-sdc{% else %}-io_pdc{% endif %} {{ name_str }} -{% endfor %}{% endif %} +{% endfor %} +{% endif %} build_design_hierarchy -{% if TOP %}set_root {{ TOP }}{% endif %} +{% if TOP %} +set_root {{ TOP }} +{% endif %} {% if CONSTRAINTS %} -{% set sdc_files = [] %}{% set pdc_files = [] %} -{% for name, attr in CONSTRAINTS.items() %}{% set name_str = name|string %} -{% if name_str.endswith('.sdc') %}{% set _ = sdc_files.append(name_str) %}{% endif %}{% set _ = pdc_files.append(name_str) %} +{% set sdc_files = [] %} +{% set pdc_files = [] %} +{% for name, attr in CONSTRAINTS.items() %} +{% set name_str = name|string %} +{% if name_str.endswith('.sdc') %} +{% set _ = sdc_files.append(name_str) %} +{% endif %} +{% set _ = pdc_files.append(name_str) %} {% endfor %} {% endif %} @@ -40,52 +59,81 @@ build_design_hierarchy {% if INCLUDES or DEFINES or PARAMS %} configure_tool -name {SYNTHESIZE} -params {SYNPLIFY_OPTIONS: -{% if INCLUDES %}{% for INCLUDE in INCLUDES %} + +{% if INCLUDES %} +{% for INCLUDE in INCLUDES %} set_option -include_path "{{ INCLUDE }}" -{% endfor %}{% endif %} -{% if DEFINES %}{% for key, value in DEFINES.items() %} +{% endfor %} +{% endif %} + +{% if DEFINES %} +{% for key, value in DEFINES.items() %} set_option -hdl_define -set {{ key }}={{ value }} -{% endfor %}{% endif %} -{% if PARAMS %}{% for key, value in PARAMS.items() %} +{% endfor %} +{% endif %} + +{% if PARAMS %} +{% for key, value in PARAMS.items() %} set_option -hdl_param -set {{ key }}={{ value }} -{% endfor %}{% endif %} +{% endfor %} +{% endif %} + } {% endif %} {{ POSTCFG }} close_project + {% endif %} -#--[ Design flow ]------------------------------------------------------------- +{# ------------------------------------------------------------------------- #} +{# Design flow #} +{# ------------------------------------------------------------------------- #} {% if SYN or PAR or BIT %} + open_project {{ PROJECT }}/{{ PROJECT }}.prjx +{# Synthesis --------------------------------------------------------------- #} + {% if SYN %} + {{ PRESYN }} run_tool -name {SYNTHESIZE} {{ POSTSYN }} + {% endif %} +{# Place and Route --------------------------------------------------------- #} + {% if PAR %} + {{ PREPAR }} run_tool -name {PLACEROUTE} run_tool -name {VERIFYTIMING} {{ POSTPAR }} + {% endif %} +{# Bitstream generation ---------------------------------------------------- #} + {% if BIT %} + {{ PREBIT }} run_tool -name {GENERATEPROGRAMMINGFILE} {{ POSTBIT }} + {% endif %} +{# ------------------------------------------------------------------------- #} + close_project + {% endif %} diff --git a/pyfpga/templates/openflow-prog.jinja b/pyfpga/templates/openflow-prog.jinja index fc1c1105..637a3c7c 100644 --- a/pyfpga/templates/openflow-prog.jinja +++ b/pyfpga/templates/openflow-prog.jinja @@ -3,6 +3,7 @@ # # SPDX-License-Identifier: GPL-3.0-or-later # +#} set -e diff --git a/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja index d29cbbc5..338cb74e 100644 --- a/pyfpga/templates/openflow.jinja +++ b/pyfpga/templates/openflow.jinja @@ -1,35 +1,48 @@ +{# # # Copyright (C) 2020-2024 Rodrigo A. Melo # # SPDX-License-Identifier: GPL-3.0-or-later # +#} set -e DOCKER="docker run --user $(id -u):$(id -g) --rm -v $HOME:$HOME -w $PWD" -# Synthesis ------------------------------------------------------------------- +{# ------------------------------------------------------------------------- #} +{# Design flow #} +{# ------------------------------------------------------------------------- #} + +{# Synthesis --------------------------------------------------------------- #} {% if SYN %} $DOCKER hdlc/ghdl:yosys /bin/bash -c " {{ PRESYN }} yosys -Q -m ghdl -p ' + {% if INCLUDES %} verilog_defaults -add{% for path in INCLUDES %} -I{{ path }}{% endfor %} {% endif %} + {% if DEFINES %} verilog_defines{% for key, value in DEFINES.items() %} -D{{ key }}={{ value }}{% endfor %} {% endif %} -{% if FILES %}{% for name, attr in FILES.items() %} + +{% if FILES %} +{% for name, attr in FILES.items() %} {% if attr.hdl == "vlog" %} read_verilog -defer {{ name }} {% elif attr.hdl == "slog" %} read_verilog -defer -sv {{ name }} {% endif %} -{% endfor %}{% endif %} +{% endfor %} +{% endif %} + {% if PARAMS %} chparam{% for key, value in PARAMS.items() %} -set {{ key }} {{ value }}{% endfor %} {% endif %} + synth -top {{ TOP }} synth_{{ FAMILY }} -top {{ TOP }} -json {{ PROJECT }}.json ' @@ -37,6 +50,7 @@ synth_{{ FAMILY }} -top {{ TOP }} -json {{ PROJECT }}.json " {% endif %} +{# #SYNTH= #WRITE= #if [[ $BACKEND == "vivado" ]]; then @@ -53,8 +67,9 @@ synth_{{ FAMILY }} -top {{ TOP }} -json {{ PROJECT }}.json # SYNTH="synth -top $TOP" # WRITE="write_verilog $PROJECT.v" #fi +#} -# Place and Route ------------------------------------------------------------- +{# Place and Route --------------------------------------------------------- #} {% if PAR %} @@ -89,7 +104,7 @@ nextpnr-ecp5 --{{ DEVICE }} --package {{ PACKAGE }} $CONSTRAINT --json {{ PROJEC {% endif %} -# Bitstream ------------------------------------------------------------------- +{# Bitstream generation ---------------------------------------------------- #} {% if BIT %} diff --git a/pyfpga/templates/quartus-prog.jinja b/pyfpga/templates/quartus-prog.jinja index 62839a6b..f33a7fb6 100644 --- a/pyfpga/templates/quartus-prog.jinja +++ b/pyfpga/templates/quartus-prog.jinja @@ -1,8 +1,10 @@ +{# # # Copyright (C) 2015-2024 Rodrigo A. Melo # # SPDX-License-Identifier: GPL-3.0-or-later # +#} RESULT=$(jtagconfig) echo "$RESULT" diff --git a/pyfpga/templates/quartus.jinja b/pyfpga/templates/quartus.jinja index 1caa2108..acd221ea 100644 --- a/pyfpga/templates/quartus.jinja +++ b/pyfpga/templates/quartus.jinja @@ -1,19 +1,25 @@ +{# # # Copyright (C) 2015-2024 Rodrigo A. Melo # # SPDX-License-Identifier: GPL-3.0-or-later # +#} -#--[ Project configuration ]--------------------------------------------------- +{# ------------------------------------------------------------------------- #} +{# Project configuration #} +{# ------------------------------------------------------------------------- #} {% if CFG %} + package require ::quartus::project project_new {{ PROJECT }} -overwrite set_global_assignment -name DEVICE {{ PART }} {{ PRECFG }} -{% if FILES %}{% for name, attr in FILES.items() %} +{% if FILES %} +{% 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" %} @@ -21,66 +27,97 @@ set_global_assignment -name VERILOG_FILE {{ name }} {% elif attr.hdl == "slog" %} set_global_assignment -name SYSTEMVERILOG_FILE {{ name }} {% endif %} -{% endfor %}{% endif %} +{% endfor %} +{% endif %} -{% if CONSTRAINTS %}{% for name, attr in CONSTRAINTS.items() %}{% set name_str = name|string %} +{% if CONSTRAINTS %} +{% for name, attr in CONSTRAINTS.items() %} +{% set name_str = name|string %} {% if name_str.endswith('.sdc') %} set_global_assignment -name SDC_FILE {{ name_str }} {% else %} source {{ name_str }} {% endif %} -{% endfor %}{% endif %} +{% endfor %} +{% endif %} -{% if TOP %}set_global_assignment -name TOP_LEVEL_ENTITY {{ TOP }}{% endif %} +{% if TOP %} +set_global_assignment -name TOP_LEVEL_ENTITY {{ TOP }} +{% endif %} -{% if INCLUDES %}{% for INCLUDE in INCLUDES %} +{% if INCLUDES %} +{% for INCLUDE in INCLUDES %} set_global_assignment -name SEARCH_PATH {{ INCLUDE }} -{% endfor %}{% endif %} +{% endfor %} +{% endif %} -{% if DEFINES %}{% for key, value in DEFINES.items() %} +{% if DEFINES %} +{% for key, value in DEFINES.items() %} set_global_assignment -name VERILOG_MACRO {{ key }}={{ value }} -{% endfor %}{% endif %} +{% endfor %} +{% endif %} -{% if PARAMS %}{% for key, value in PARAMS.items() %} +{% if PARAMS %} +{% for key, value in PARAMS.items() %} set_parameter -name {{ key }} {{ value }} -{% endfor %}{% endif %} +{% endfor %} +{% endif %} {{ POSTCFG }} project_close + {% endif %} -#--[ Design flow ]------------------------------------------------------------- +{# ------------------------------------------------------------------------- #} +{# Design flow #} +{# ------------------------------------------------------------------------- #} {% if SYN or PAR or BIT %} + package require ::quartus::flow project_open -force {{ PROJECT }}.qpf # set_global_assignment -name NUM_PARALLEL_PROCESSORS ALL +{# Synthesis --------------------------------------------------------------- #} + {% if SYN %} + {{ PRESYN }} execute_module -tool map {{ POSTSYN }} + {% endif %} +{# Place and Route --------------------------------------------------------- #} + {% if PAR %} + {{ PREPAR }} execute_module -tool fit execute_module -tool sta {{ POSTPAR }} + {% endif %} +{# Bitstream generation ---------------------------------------------------- #} + {% if BIT %} + {{ PREBIT }} execute_module -tool asm {{ POSTBIT }} + {% endif %} +{# ------------------------------------------------------------------------- #} + project_close + {% endif %} diff --git a/pyfpga/templates/vivado-prog.jinja b/pyfpga/templates/vivado-prog.jinja index 7b148b22..c2d9a9cb 100644 --- a/pyfpga/templates/vivado-prog.jinja +++ b/pyfpga/templates/vivado-prog.jinja @@ -1,8 +1,10 @@ +{# # # Copyright (C) 2015-2024 Rodrigo A. Melo # # SPDX-License-Identifier: GPL-3.0-or-later # +#} if { [ catch { open_hw_manager } ] } { open_hw } connect_hw_server diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index 6a40c459..6c303254 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -1,12 +1,17 @@ +{# # # Copyright (C) 2015-2024 Rodrigo A. Melo # # SPDX-License-Identifier: GPL-3.0-or-later # +#} -#--[ Project configuration ]--------------------------------------------------- +{# ------------------------------------------------------------------------- #} +{# Project configuration #} +{# ------------------------------------------------------------------------- #} {% if CFG %} + create_project -force {{ PROJECT }} set_property SOURCE_MGMT_MODE None [current_project] set_property STEPS.SYNTH_DESIGN.ARGS.ASSERT true [get_runs synth_1] @@ -14,12 +19,15 @@ set_property PART {{ PART }} [current_project] {{ PRECFG }} -{% if FILES %}{% for name, attr in FILES.items() %} +{% if FILES %} +{% for name, attr in FILES.items() %} add_file {{ name }} {% if 'lib' in attr %}set_property library {{ attr.lib }} [get_files {{ name }}]{% endif %} -{% endfor %}{% endif %} +{% endfor %} +{% endif %} -{% if CONSTRAINTS %}{% for name, attr in CONSTRAINTS.items() %} +{% if CONSTRAINTS %} +{% for name, attr in CONSTRAINTS.items() %} add_file -fileset constrs_1 {{ name }} {% if attr == "syn" %} set_property USED_IN_IMPLEMENTATION FALSE [get_files {{ name }}] @@ -27,16 +35,19 @@ set_property USED_IN_IMPLEMENTATION FALSE [get_files {{ name }}] set_property USED_IN_SYNTHESIS FALSE [get_files {{ name }}] {% endif %} {% if loop.first %}set_property TARGET_CONSTRS_FILE {{ name }} [current_fileset -constrset]{% endif %} -{% endfor %}{% endif %} +{% endfor %} +{% endif %} -{% if TOP %}set_property TOP {{ TOP }} [current_fileset]{% endif %} +{% if TOP %} +set_property TOP {{ TOP }} [current_fileset] +{% endif %} {% if INCLUDES %} set_property INCLUDE_DIRS { {{ INCLUDES | join(' ') }} } [current_fileset] {% endif %} {% if DEFINES %} -set_property VERILOG_DEFINE { {{ DEFINES.items() | map('join', '=') | join(' ') }} } [current_fileset] +set_property VERILOG_DEFINE { {{ DEFINES.items() | map('join', '=') | join(' ') }} } [current_fileset] {% endif %} {% if PARAMS %} @@ -46,14 +57,21 @@ set_property GENERIC { {{ PARAMS.items() | map('join', '=') | join(' ') }} } -ob {{ POSTCFG }} close_project + {% endif %} -#--[ Design flow ]------------------------------------------------------------- +{# ------------------------------------------------------------------------- #} +{# Design flow #} +{# ------------------------------------------------------------------------- #} {% if SYN or PAR or BIT %} + open_project {{ PROJECT }} +{# Synthesis --------------------------------------------------------------- #} + {% if SYN %} + {{ PRESYN }} # PRESYNTH @@ -62,12 +80,17 @@ 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 } {{ POSTSYN }} + {% endif %} +{# Place and Route --------------------------------------------------------- #} + {% if PAR %} + {{ PREPAR }} reset_run impl_1 @@ -77,9 +100,13 @@ wait_on_run impl_1 if { [get_property STATUS [get_runs impl_1]] ne "route_design Complete!" } { exit 1 } {{ POSTPAR }} + {% endif %} +{# Bitstream generation ---------------------------------------------------- #} + {% if BIT %} + {{ PREBIT }} open_run impl_1 @@ -87,7 +114,11 @@ write_bitstream -force {{ PROJECT }} write_debug_probes -force -quiet {{ PROJECT }}.ltx {{ POSTBIT }} + {% endif %} +{# ------------------------------------------------------------------------- #} + close_project + {% endif %} From 0efbd0e098eb683f1629455cbf3946e5a9489ea9 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 7 Jul 2024 22:17:25 -0300 Subject: [PATCH 160/248] Copyright notice updated --- pyfpga/helpers/bitprog.py | 16 ++-------------- pyfpga/helpers/hdl2bit.py | 16 ++-------------- pyfpga/helpers/prj2bit.py | 16 ++-------------- pyfpga/ise.py | 2 +- pyfpga/libero.py | 2 +- pyfpga/openflow.py | 2 +- pyfpga/project.py | 2 +- pyfpga/quartus.py | 2 +- pyfpga/templates/ise-prog.jinja | 2 +- pyfpga/templates/ise.jinja | 2 +- pyfpga/templates/libero-prog.jinja | 2 +- pyfpga/templates/libero.jinja | 2 +- pyfpga/templates/openflow-prog.jinja | 2 +- pyfpga/templates/openflow.jinja | 2 +- pyfpga/templates/quartus-prog.jinja | 2 +- pyfpga/templates/quartus.jinja | 2 +- pyfpga/templates/vivado-prog.jinja | 2 +- pyfpga/templates/vivado.jinja | 2 +- pyfpga/vivado.py | 2 +- tests/mocks/impact | 2 +- tests/mocks/libero | 2 +- tests/mocks/quartus_pgm | 2 +- tests/mocks/quartus_sh | 2 +- tests/mocks/vivado | 2 +- tests/mocks/xtclsh | 2 +- 25 files changed, 28 insertions(+), 64 deletions(-) diff --git a/pyfpga/helpers/bitprog.py b/pyfpga/helpers/bitprog.py index a15e18a3..0282f263 100644 --- a/pyfpga/helpers/bitprog.py +++ b/pyfpga/helpers/bitprog.py @@ -1,20 +1,8 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 INTI -# Copyright (C) 2020 Rodrigo A. Melo +# Copyright (C) 2020 PyFPGA Project # -# 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 <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # """ diff --git a/pyfpga/helpers/hdl2bit.py b/pyfpga/helpers/hdl2bit.py index 87ed4159..8b6d9173 100644 --- a/pyfpga/helpers/hdl2bit.py +++ b/pyfpga/helpers/hdl2bit.py @@ -1,20 +1,8 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 INTI -# Copyright (C) 2020 Rodrigo A. Melo +# Copyright (C) 2020 PyFPGA Project # -# 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 <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # """ diff --git a/pyfpga/helpers/prj2bit.py b/pyfpga/helpers/prj2bit.py index 1cd8a74e..4a0a6886 100644 --- a/pyfpga/helpers/prj2bit.py +++ b/pyfpga/helpers/prj2bit.py @@ -1,20 +1,8 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 INTI -# Copyright (C) 2020 Rodrigo A. Melo +# Copyright (C) 2020 PyFPGA Project # -# 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 <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # """ diff --git a/pyfpga/ise.py b/pyfpga/ise.py index 9e173f2f..b8f33706 100644 --- a/pyfpga/ise.py +++ b/pyfpga/ise.py @@ -1,5 +1,5 @@ # -# Copyright (C) 2019-2024 Rodrigo A. Melo +# Copyright (C) 2019-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/pyfpga/libero.py b/pyfpga/libero.py index 813eb3da..7c07a6a1 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -1,5 +1,5 @@ # -# Copyright (C) 2019-2024 Rodrigo A. Melo +# Copyright (C) 2019-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/pyfpga/openflow.py b/pyfpga/openflow.py index a2e4092d..63712a85 100644 --- a/pyfpga/openflow.py +++ b/pyfpga/openflow.py @@ -1,5 +1,5 @@ # -# Copyright (C) 2020-2024 Rodrigo A. Melo +# Copyright (C) 2020-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/pyfpga/project.py b/pyfpga/project.py index b77b8159..19570036 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -1,5 +1,5 @@ # -# Copyright (C) 2019-2024 Rodrigo A. Melo +# Copyright (C) 2019-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/pyfpga/quartus.py b/pyfpga/quartus.py index 10a5927e..0cc5f93d 100644 --- a/pyfpga/quartus.py +++ b/pyfpga/quartus.py @@ -1,5 +1,5 @@ # -# Copyright (C) 2019-2024 Rodrigo A. Melo +# Copyright (C) 2019-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/pyfpga/templates/ise-prog.jinja b/pyfpga/templates/ise-prog.jinja index 0f0e38c9..1c2203ee 100644 --- a/pyfpga/templates/ise-prog.jinja +++ b/pyfpga/templates/ise-prog.jinja @@ -1,6 +1,6 @@ {# # -# Copyright (C) 2015-2024 Rodrigo A. Melo +# Copyright (C) 2015-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/pyfpga/templates/ise.jinja b/pyfpga/templates/ise.jinja index 10811ebf..73c5fce9 100644 --- a/pyfpga/templates/ise.jinja +++ b/pyfpga/templates/ise.jinja @@ -1,6 +1,6 @@ {# # -# Copyright (C) 2015-2024 Rodrigo A. Melo +# Copyright (C) 2015-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/pyfpga/templates/libero-prog.jinja b/pyfpga/templates/libero-prog.jinja index 5bd27ab5..f2369645 100644 --- a/pyfpga/templates/libero-prog.jinja +++ b/pyfpga/templates/libero-prog.jinja @@ -1,5 +1,5 @@ # -# Copyright (C) 2015-2024 Rodrigo A. Melo +# Copyright (C) 2015-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index f62d78dc..3f061cdf 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -1,6 +1,6 @@ {# # -# Copyright (C) 2015-2024 Rodrigo A. Melo +# Copyright (C) 2015-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/pyfpga/templates/openflow-prog.jinja b/pyfpga/templates/openflow-prog.jinja index 637a3c7c..81942426 100644 --- a/pyfpga/templates/openflow-prog.jinja +++ b/pyfpga/templates/openflow-prog.jinja @@ -1,5 +1,5 @@ # -# Copyright (C) 2020-2024 Rodrigo A. Melo +# Copyright (C) 2020-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja index 338cb74e..15a82f4a 100644 --- a/pyfpga/templates/openflow.jinja +++ b/pyfpga/templates/openflow.jinja @@ -1,6 +1,6 @@ {# # -# Copyright (C) 2020-2024 Rodrigo A. Melo +# Copyright (C) 2020-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/pyfpga/templates/quartus-prog.jinja b/pyfpga/templates/quartus-prog.jinja index f33a7fb6..23786f22 100644 --- a/pyfpga/templates/quartus-prog.jinja +++ b/pyfpga/templates/quartus-prog.jinja @@ -1,6 +1,6 @@ {# # -# Copyright (C) 2015-2024 Rodrigo A. Melo +# Copyright (C) 2015-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/pyfpga/templates/quartus.jinja b/pyfpga/templates/quartus.jinja index acd221ea..fa26aa9a 100644 --- a/pyfpga/templates/quartus.jinja +++ b/pyfpga/templates/quartus.jinja @@ -1,6 +1,6 @@ {# # -# Copyright (C) 2015-2024 Rodrigo A. Melo +# Copyright (C) 2015-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/pyfpga/templates/vivado-prog.jinja b/pyfpga/templates/vivado-prog.jinja index c2d9a9cb..72ccb91d 100644 --- a/pyfpga/templates/vivado-prog.jinja +++ b/pyfpga/templates/vivado-prog.jinja @@ -1,6 +1,6 @@ {# # -# Copyright (C) 2015-2024 Rodrigo A. Melo +# Copyright (C) 2015-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index 6c303254..5b80a7d5 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -1,6 +1,6 @@ {# # -# Copyright (C) 2015-2024 Rodrigo A. Melo +# Copyright (C) 2015-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/pyfpga/vivado.py b/pyfpga/vivado.py index c0a48864..3cc39d6e 100644 --- a/pyfpga/vivado.py +++ b/pyfpga/vivado.py @@ -1,5 +1,5 @@ # -# Copyright (C) 2019-2024 Rodrigo A. Melo +# Copyright (C) 2019-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/tests/mocks/impact b/tests/mocks/impact index 0fbbacbc..1446c5e0 100755 --- a/tests/mocks/impact +++ b/tests/mocks/impact @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022-2024 Rodrigo A. Melo +# Copyright (C) 2022-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/tests/mocks/libero b/tests/mocks/libero index a4663e9d..510b9926 100755 --- a/tests/mocks/libero +++ b/tests/mocks/libero @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022-2024 Rodrigo A. Melo +# Copyright (C) 2022-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/tests/mocks/quartus_pgm b/tests/mocks/quartus_pgm index 8993ca10..150e1ae9 100755 --- a/tests/mocks/quartus_pgm +++ b/tests/mocks/quartus_pgm @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022-2024 Rodrigo A. Melo +# Copyright (C) 2022-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/tests/mocks/quartus_sh b/tests/mocks/quartus_sh index 472f639f..7f437145 100755 --- a/tests/mocks/quartus_sh +++ b/tests/mocks/quartus_sh @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022-2024 Rodrigo A. Melo +# Copyright (C) 2022-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/tests/mocks/vivado b/tests/mocks/vivado index fb59d819..3bc95381 100755 --- a/tests/mocks/vivado +++ b/tests/mocks/vivado @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022-2024 Rodrigo A. Melo +# Copyright (C) 2022-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # diff --git a/tests/mocks/xtclsh b/tests/mocks/xtclsh index 7621c4d9..22f66a77 100755 --- a/tests/mocks/xtclsh +++ b/tests/mocks/xtclsh @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022-2024 Rodrigo A. Melo +# Copyright (C) 2022-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # From fa84608232053ef59e84b6862954c45acb72f194 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 7 Jul 2024 23:53:39 -0300 Subject: [PATCH 161/248] Fix paths in TCL on Windows --- pyfpga/project.py | 6 +++--- pyfpga/templates/ise.jinja | 7 +++---- pyfpga/templates/libero.jinja | 10 ++++----- pyfpga/templates/quartus.jinja | 7 +++---- tests/test_data.py | 38 ++++++++++++++++++---------------- 5 files changed, 33 insertions(+), 35 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index 19570036..16ee090e 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -66,7 +66,7 @@ def add_include(self, path): path = Path(path).resolve() if not path.is_dir(): raise NotADirectoryError(path) - self.data.setdefault('includes', []).append(path) + self.data.setdefault('includes', []).append(path.as_posix()) def _add_file(self, pathname, hdl=None, lib=None): files = glob.glob(pathname, recursive=True) @@ -79,7 +79,7 @@ def _add_file(self, pathname, hdl=None, lib=None): attr['hdl'] = hdl if lib: attr['lib'] = lib - self.data.setdefault('files', {})[path] = attr + self.data.setdefault('files', {})[path.as_posix()] = attr def add_slog(self, pathname): """Add System Verilog file/s. @@ -128,7 +128,7 @@ def add_cons(self, path, when='all'): raise FileNotFoundError(path) if when not in ['all', 'syn', 'par']: raise ValueError('Invalid only.') - self.data.setdefault('constraints', {})[path] = when + self.data.setdefault('constraints', {})[path.as_posix()] = when def add_param(self, name, value): """Add a Parameter/Generic Value. diff --git a/pyfpga/templates/ise.jinja b/pyfpga/templates/ise.jinja index 73c5fce9..647856e2 100644 --- a/pyfpga/templates/ise.jinja +++ b/pyfpga/templates/ise.jinja @@ -30,10 +30,9 @@ xfile add {{ name }}{% if 'lib' in attr %} -lib_vhdl {{ attr.lib }}{% endif %} {% if CONSTRAINTS %} {% for name, attr in CONSTRAINTS.items() %} -{% set name_str = name|string %} -xfile add {{ name_str }} -{% if name_str.endswith('.xcf') %} -project set "Synthesis Constraints File" "{{ name_str }}" -process "Synthesize - XST" +xfile add {{ name }} +{% if name.endswith('.xcf') %} +project set "Synthesis Constraints File" "{{ name }}" -process "Synthesize - XST" {% endif %} {% endfor %} {% endif %} diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index 3f061cdf..b4a36d64 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -30,8 +30,7 @@ create_links -hdl_source {{ name }}{% if 'lib' in attr %} -library {{ attr.lib } {% if CONSTRAINTS %} {% for name, attr in CONSTRAINTS.items() %} -{% set name_str = name|string %} -create_links {% if name_str.endswith('.sdc') %}-sdc{% else %}-io_pdc{% endif %} {{ name_str }} +create_links {% if name.endswith('.sdc') %}-sdc{% else %}-io_pdc{% endif %} {{ name }} {% endfor %} {% endif %} @@ -45,11 +44,10 @@ set_root {{ TOP }} {% set sdc_files = [] %} {% set pdc_files = [] %} {% for name, attr in CONSTRAINTS.items() %} -{% set name_str = name|string %} -{% if name_str.endswith('.sdc') %} -{% set _ = sdc_files.append(name_str) %} +{% if name.endswith('.sdc') %} +{% set _ = sdc_files.append(name) %} {% endif %} -{% set _ = pdc_files.append(name_str) %} +{% set _ = pdc_files.append(name) %} {% endfor %} {% endif %} diff --git a/pyfpga/templates/quartus.jinja b/pyfpga/templates/quartus.jinja index fa26aa9a..29826df1 100644 --- a/pyfpga/templates/quartus.jinja +++ b/pyfpga/templates/quartus.jinja @@ -32,11 +32,10 @@ set_global_assignment -name SYSTEMVERILOG_FILE {{ name }} {% if CONSTRAINTS %} {% for name, attr in CONSTRAINTS.items() %} -{% set name_str = name|string %} -{% if name_str.endswith('.sdc') %} -set_global_assignment -name SDC_FILE {{ name_str }} +{% if name.endswith('.sdc') %} +set_global_assignment -name SDC_FILE {{ name }} {% else %} -source {{ name_str }} +source {{ name }} {% endif %} {% endfor %} {% endif %} diff --git a/tests/test_data.py b/tests/test_data.py index 250ea793..34f4c633 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -5,35 +5,37 @@ pattern = { 'part': 'PARTNAME', 'includes': [ - Path('fakedata/dir1').resolve(), - Path('fakedata/dir2').resolve(), - Path('fakedata/dir3').resolve() + Path('fakedata/dir1').resolve().as_posix(), + Path('fakedata/dir2').resolve().as_posix(), + Path('fakedata/dir3').resolve().as_posix() ], 'files': { - Path('fakedata/vhdl0.vhdl').resolve(): {'hdl': 'vhdl', 'lib': 'LIB'}, - Path('fakedata/dir1/vhdl1.vhdl').resolve(): { + Path('fakedata/vhdl0.vhdl').resolve().as_posix(): { 'hdl': 'vhdl', 'lib': 'LIB' }, - Path('fakedata/dir2/vhdl2.vhdl').resolve(): { + Path('fakedata/dir1/vhdl1.vhdl').resolve().as_posix(): { 'hdl': 'vhdl', 'lib': 'LIB' }, - Path('fakedata/dir3/vhdl3.vhdl').resolve(): { + Path('fakedata/dir2/vhdl2.vhdl').resolve().as_posix(): { 'hdl': 'vhdl', 'lib': 'LIB' }, - Path('fakedata/vlog0.v').resolve(): {'hdl': 'vlog'}, - Path('fakedata/dir1/vlog1.v').resolve(): {'hdl': 'vlog'}, - Path('fakedata/dir2/vlog2.v').resolve(): {'hdl': 'vlog'}, - Path('fakedata/dir3/vlog3.v').resolve(): {'hdl': 'vlog'}, - Path('fakedata/slog0.sv').resolve(): {'hdl': 'slog'}, - Path('fakedata/dir1/slog1.sv').resolve(): {'hdl': 'slog'}, - Path('fakedata/dir2/slog2.sv').resolve(): {'hdl': 'slog'}, - Path('fakedata/dir3/slog3.sv').resolve(): {'hdl': 'slog'} + Path('fakedata/dir3/vhdl3.vhdl').resolve().as_posix(): { + 'hdl': 'vhdl', 'lib': 'LIB' + }, + Path('fakedata/vlog0.v').resolve().as_posix(): {'hdl': 'vlog'}, + Path('fakedata/dir1/vlog1.v').resolve().as_posix(): {'hdl': 'vlog'}, + Path('fakedata/dir2/vlog2.v').resolve().as_posix(): {'hdl': 'vlog'}, + Path('fakedata/dir3/vlog3.v').resolve().as_posix(): {'hdl': 'vlog'}, + Path('fakedata/slog0.sv').resolve().as_posix(): {'hdl': 'slog'}, + Path('fakedata/dir1/slog1.sv').resolve().as_posix(): {'hdl': 'slog'}, + Path('fakedata/dir2/slog2.sv').resolve().as_posix(): {'hdl': 'slog'}, + Path('fakedata/dir3/slog3.sv').resolve().as_posix(): {'hdl': 'slog'} }, 'top': 'TOPNAME', 'constraints': { - Path('fakedata/cons/all.xdc').resolve(): 'all', - Path('fakedata/cons/syn.xdc').resolve(): 'syn', - Path('fakedata/cons/par.xdc').resolve(): 'par' + Path('fakedata/cons/all.xdc').resolve().as_posix(): 'all', + Path('fakedata/cons/syn.xdc').resolve().as_posix(): 'syn', + Path('fakedata/cons/par.xdc').resolve().as_posix(): 'par' }, 'params': { 'PAR1': 'VAL1', From f437eb7b7d4a97f77198b25a2d42b3a4e4055675 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 8 Jul 2024 22:34:45 -0300 Subject: [PATCH 162/248] Added comments to easily identify each section --- pyfpga/templates/ise.jinja | 38 +++++++++------------------- pyfpga/templates/libero.jinja | 44 +++++++++++---------------------- pyfpga/templates/openflow.jinja | 16 +++--------- pyfpga/templates/quartus.jinja | 38 +++++++++------------------- pyfpga/templates/vivado.jinja | 38 +++++++++------------------- 5 files changed, 50 insertions(+), 124 deletions(-) diff --git a/pyfpga/templates/ise.jinja b/pyfpga/templates/ise.jinja index 647856e2..6af287ca 100644 --- a/pyfpga/templates/ise.jinja +++ b/pyfpga/templates/ise.jinja @@ -6,11 +6,7 @@ # #} -{# ------------------------------------------------------------------------- #} -{# Project configuration #} -{# ------------------------------------------------------------------------- #} - -{% if CFG %} +{% if CFG %}# Project configuration ------------------------------------------------------- if { [ file exists {{ PROJECT }}.xise ] } { file delete {{ PROJECT }}.xise } project new {{ PROJECT }}.xise @@ -21,14 +17,14 @@ project set speed -{{ SPEED }} {{ PRECFG }} -{% if FILES %} +{% 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 %} +{% if CONSTRAINTS %}# Constraints inclusion {% for name, attr in CONSTRAINTS.items() %} xfile add {{ name }} {% if name.endswith('.xcf') %} @@ -37,19 +33,19 @@ project set "Synthesis Constraints File" "{{ name }}" -process "Synthesize - XST {% endfor %} {% endif %} -{% if TOP %} +{% if TOP %}# Top-level specification project set top {{ TOP }} {% endif %} -{% if INCLUDES %} +{% if INCLUDES %}# Verilog Includes project set "Verilog Include Directories" "{{ INCLUDES | join('|') }}" -process "Synthesize - XST" {% endif %} -{% if DEFINES %} +{% if DEFINES %}# Verilog Defines project set "Verilog Macros" "{{ DEFINES.items() | map('join', '=') | join(' | ') }}" -process "Synthesize - XST" {% endif %} -{% if PARAMS %} +{% if PARAMS %}# Verilog Parameters / VHDL Generics project set "Generics, Parameters" "{{ PARAMS.items() | map('join', '=') | join(' ') }}" -process "Synthesize - XST" {% endif %} @@ -59,17 +55,11 @@ project close {% endif %} -{# ------------------------------------------------------------------------- #} -{# Design flow #} -{# ------------------------------------------------------------------------- #} - -{% if SYN or PAR or BIT %} +{% if SYN or PAR or BIT %}# Design flow ----------------------------------------------------------------- project open {{ PROJECT }}.xise -{# Synthesis --------------------------------------------------------------- #} - -{% if SYN %} +{% if SYN %}# Synthesis {{ PRESYN }} @@ -83,9 +73,7 @@ if { [process get "Synthesize" status] == "errors" } { exit 1 } {% endif %} -{# Place and Route --------------------------------------------------------- #} - -{% if PAR %} +{% if PAR %}# Place and Route {{ PREPAR }} @@ -100,9 +88,7 @@ if { [process get "Place & Route" status] == "errors" } { exit 1 } {% endif %} -{# Bitstream generation ---------------------------------------------------- #} - -{% if BIT %} +{% if BIT %}# Bitstream generation {{ PREBIT }} @@ -114,8 +100,6 @@ catch { file rename -force {{ TOP }}.bit {{ PROJECT }}.bit } {% endif %} -{# ------------------------------------------------------------------------- #} - project close {% endif %} diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index b4a36d64..a4be188f 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -6,11 +6,7 @@ # #} -{# ------------------------------------------------------------------------- #} -{# Project configuration #} -{# ------------------------------------------------------------------------- #} - -{% if CFG %} +{% if CFG %}# Project configuration ------------------------------------------------------- if { [ file exists {{ PROJECT }} ] } { file delete -force -- {{ PROJECT }} } new_project -name {{ PROJECT }} -location {libero} -hdl {VERILOG} -family {SmartFusion2} @@ -18,17 +14,17 @@ set_device -family {{ FAMILY }} -die {{ DEVICE }} -package {{ PACKAGE }} -speed {{ PRECFG }} -{% if INCLUDES %} +{% if INCLUDES %}# Verilog Includes (Libero) set_global_include_path_order -paths "{{ INCLUDES | join(' ') }}" {% endif %} -{% if FILES %} +{% 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 %} +{% if CONSTRAINTS %}# Constraints inclusion {% for name, attr in CONSTRAINTS.items() %} create_links {% if name.endswith('.sdc') %}-sdc{% else %}-io_pdc{% endif %} {{ name }} {% endfor %} @@ -36,11 +32,11 @@ create_links {% if name.endswith('.sdc') %}-sdc{% else %}-io_pdc{% endif %} {{ n build_design_hierarchy -{% if TOP %} +{% if TOP %}# Top-level specification set_root {{ TOP }} {% endif %} -{% if CONSTRAINTS %} +{% if CONSTRAINTS %}# Constraints configuration {% set sdc_files = [] %} {% set pdc_files = [] %} {% for name, attr in CONSTRAINTS.items() %} @@ -55,22 +51,22 @@ set_root {{ TOP }} {% 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 %} +{% if INCLUDES or DEFINES or PARAMS %}# Synopsys configuration configure_tool -name {SYNTHESIZE} -params {SYNPLIFY_OPTIONS: -{% if INCLUDES %} +{% if INCLUDES %}# Verilog Includes (Synopsys) {% for INCLUDE in INCLUDES %} set_option -include_path "{{ INCLUDE }}" {% endfor %} {% endif %} -{% if DEFINES %} +{% if DEFINES %}# Verilog Defines (Synopsys) {% for key, value in DEFINES.items() %} set_option -hdl_define -set {{ key }}={{ value }} {% endfor %} {% endif %} -{% if PARAMS %} +{% if PARAMS %}# Verilog Parameters / VHDL Generics (Synopsys) {% for key, value in PARAMS.items() %} set_option -hdl_param -set {{ key }}={{ value }} {% endfor %} @@ -85,17 +81,11 @@ close_project {% endif %} -{# ------------------------------------------------------------------------- #} -{# Design flow #} -{# ------------------------------------------------------------------------- #} - -{% if SYN or PAR or BIT %} +{% if SYN or PAR or BIT %}# Design flow ----------------------------------------------------------------- open_project {{ PROJECT }}/{{ PROJECT }}.prjx -{# Synthesis --------------------------------------------------------------- #} - -{% if SYN %} +{% if SYN %}# Synthesis {{ PRESYN }} @@ -105,9 +95,7 @@ run_tool -name {SYNTHESIZE} {% endif %} -{# Place and Route --------------------------------------------------------- #} - -{% if PAR %} +{% if PAR %}# Place and Route {{ PREPAR }} @@ -118,9 +106,7 @@ run_tool -name {VERIFYTIMING} {% endif %} -{# Bitstream generation ---------------------------------------------------- #} - -{% if BIT %} +{% if BIT %}# Bitstream generation {{ PREBIT }} @@ -130,8 +116,6 @@ run_tool -name {GENERATEPROGRAMMINGFILE} {% endif %} -{# ------------------------------------------------------------------------- #} - close_project {% endif %} diff --git a/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja index 15a82f4a..02e57662 100644 --- a/pyfpga/templates/openflow.jinja +++ b/pyfpga/templates/openflow.jinja @@ -10,13 +10,7 @@ set -e DOCKER="docker run --user $(id -u):$(id -g) --rm -v $HOME:$HOME -w $PWD" -{# ------------------------------------------------------------------------- #} -{# Design flow #} -{# ------------------------------------------------------------------------- #} - -{# Synthesis --------------------------------------------------------------- #} - -{% if SYN %} +{% if SYN %}# Synthesis $DOCKER hdlc/ghdl:yosys /bin/bash -c " {{ PRESYN }} yosys -Q -m ghdl -p ' @@ -69,9 +63,7 @@ synth_{{ FAMILY }} -top {{ TOP }} -json {{ PROJECT }}.json #fi #} -{# Place and Route --------------------------------------------------------- #} - -{% if PAR %} +{% if PAR %}# Place and Route CONSTRAINTS="{{ CONSTRAINTS | join(' ') }}" @@ -104,9 +96,7 @@ nextpnr-ecp5 --{{ DEVICE }} --package {{ PACKAGE }} $CONSTRAINT --json {{ PROJEC {% endif %} -{# Bitstream generation ---------------------------------------------------- #} - -{% if BIT %} +{% if BIT %}# Bitstream generation {% if FAMILY == 'ice40' %} $DOCKER hdlc/icestorm /bin/bash -c " diff --git a/pyfpga/templates/quartus.jinja b/pyfpga/templates/quartus.jinja index 29826df1..dcbcb5d9 100644 --- a/pyfpga/templates/quartus.jinja +++ b/pyfpga/templates/quartus.jinja @@ -6,11 +6,7 @@ # #} -{# ------------------------------------------------------------------------- #} -{# Project configuration #} -{# ------------------------------------------------------------------------- #} - -{% if CFG %} +{% if CFG %}# Project configuration ------------------------------------------------------- package require ::quartus::project project_new {{ PROJECT }} -overwrite @@ -18,7 +14,7 @@ set_global_assignment -name DEVICE {{ PART }} {{ PRECFG }} -{% if FILES %} +{% 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 %} @@ -30,7 +26,7 @@ set_global_assignment -name SYSTEMVERILOG_FILE {{ name }} {% endfor %} {% endif %} -{% if CONSTRAINTS %} +{% if CONSTRAINTS %}# Constraints inclusion {% for name, attr in CONSTRAINTS.items() %} {% if name.endswith('.sdc') %} set_global_assignment -name SDC_FILE {{ name }} @@ -40,23 +36,23 @@ source {{ name }} {% endfor %} {% endif %} -{% if TOP %} +{% if TOP %}# Top-level specification set_global_assignment -name TOP_LEVEL_ENTITY {{ TOP }} {% endif %} -{% if INCLUDES %} +{% if INCLUDES %}# Verilog Includes {% for INCLUDE in INCLUDES %} set_global_assignment -name SEARCH_PATH {{ INCLUDE }} {% endfor %} {% endif %} -{% if DEFINES %} +{% if DEFINES %}# Verilog Defines {% for key, value in DEFINES.items() %} set_global_assignment -name VERILOG_MACRO {{ key }}={{ value }} {% endfor %} {% endif %} -{% if PARAMS %} +{% if PARAMS %}# Verilog Parameters / VHDL Generics {% for key, value in PARAMS.items() %} set_parameter -name {{ key }} {{ value }} {% endfor %} @@ -68,19 +64,13 @@ project_close {% endif %} -{# ------------------------------------------------------------------------- #} -{# Design flow #} -{# ------------------------------------------------------------------------- #} - -{% if SYN or PAR or BIT %} +{% if SYN or PAR or BIT %}# Design flow ----------------------------------------------------------------- package require ::quartus::flow project_open -force {{ PROJECT }}.qpf # set_global_assignment -name NUM_PARALLEL_PROCESSORS ALL -{# Synthesis --------------------------------------------------------------- #} - -{% if SYN %} +{% if SYN %}# Synthesis {{ PRESYN }} @@ -90,9 +80,7 @@ execute_module -tool map {% endif %} -{# Place and Route --------------------------------------------------------- #} - -{% if PAR %} +{% if PAR %}# Place and Route {{ PREPAR }} @@ -103,9 +91,7 @@ execute_module -tool sta {% endif %} -{# Bitstream generation ---------------------------------------------------- #} - -{% if BIT %} +{% if BIT %}# Bitstream generation {{ PREBIT }} @@ -115,8 +101,6 @@ execute_module -tool asm {% endif %} -{# ------------------------------------------------------------------------- #} - project_close {% endif %} diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index 5b80a7d5..19a53aee 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -6,11 +6,7 @@ # #} -{# ------------------------------------------------------------------------- #} -{# Project configuration #} -{# ------------------------------------------------------------------------- #} - -{% if CFG %} +{% if CFG %}# Project configuration ------------------------------------------------------- create_project -force {{ PROJECT }} set_property SOURCE_MGMT_MODE None [current_project] @@ -19,14 +15,14 @@ set_property PART {{ PART }} [current_project] {{ PRECFG }} -{% if FILES %} +{% 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 %} +{% if CONSTRAINTS %}# Constraints inclusion {% for name, attr in CONSTRAINTS.items() %} add_file -fileset constrs_1 {{ name }} {% if attr == "syn" %} @@ -38,19 +34,19 @@ set_property USED_IN_SYNTHESIS FALSE [get_files {{ name }}] {% endfor %} {% endif %} -{% if TOP %} +{% if TOP %}# Top-level specification set_property TOP {{ TOP }} [current_fileset] {% endif %} -{% if INCLUDES %} +{% if INCLUDES %}# Verilog Includes set_property INCLUDE_DIRS { {{ INCLUDES | join(' ') }} } [current_fileset] {% endif %} -{% if DEFINES %} +{% if DEFINES %}# Verilog Defines set_property VERILOG_DEFINE { {{ DEFINES.items() | map('join', '=') | join(' ') }} } [current_fileset] {% endif %} -{% if PARAMS %} +{% if PARAMS %}# Verilog Parameters / VHDL Generics set_property GENERIC { {{ PARAMS.items() | map('join', '=') | join(' ') }} } -objects [get_filesets sources_1] {% endif %} @@ -60,17 +56,11 @@ close_project {% endif %} -{# ------------------------------------------------------------------------- #} -{# Design flow #} -{# ------------------------------------------------------------------------- #} - -{% if SYN or PAR or BIT %} +{% if SYN or PAR or BIT %}# Design flow ----------------------------------------------------------------- open_project {{ PROJECT }} -{# Synthesis --------------------------------------------------------------- #} - -{% if SYN %} +{% if SYN %}# Synthesis {{ PRESYN }} @@ -87,9 +77,7 @@ if { [get_property STATUS [get_runs synth_1]] ne "synth_design Complete!" } { ex {% endif %} -{# Place and Route --------------------------------------------------------- #} - -{% if PAR %} +{% if PAR %}# Place and Route {{ PREPAR }} @@ -103,9 +91,7 @@ if { [get_property STATUS [get_runs impl_1]] ne "route_design Complete!" } { exi {% endif %} -{# Bitstream generation ---------------------------------------------------- #} - -{% if BIT %} +{% if BIT %}# Bitstream generation {{ PREBIT }} @@ -117,8 +103,6 @@ write_debug_probes -force -quiet {{ PROJECT }}.ltx {% endif %} -{# ------------------------------------------------------------------------- #} - close_project {% endif %} From a34381909804057fde54dd131e877737343a6644 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 8 Jul 2024 23:37:14 -0300 Subject: [PATCH 163/248] Change template variables from uppercase to lowercase --- pyfpga/ise.py | 40 +++++++-------- pyfpga/libero.py | 38 +++++++------- pyfpga/openflow.py | 38 +++++++------- pyfpga/project.py | 2 +- pyfpga/quartus.py | 34 ++++++------- pyfpga/templates/ise-prog.jinja | 32 ++++++------ pyfpga/templates/ise.jinja | 66 ++++++++++++------------ pyfpga/templates/libero.jinja | 76 ++++++++++++++-------------- pyfpga/templates/openflow-prog.jinja | 8 +-- pyfpga/templates/openflow.jinja | 66 ++++++++++++------------ pyfpga/templates/quartus-prog.jinja | 2 +- pyfpga/templates/quartus.jinja | 58 ++++++++++----------- pyfpga/templates/vivado-prog.jinja | 2 +- pyfpga/templates/vivado.jinja | 60 +++++++++++----------- pyfpga/vivado.py | 32 ++++++------ 15 files changed, 277 insertions(+), 277 deletions(-) diff --git a/pyfpga/ise.py b/pyfpga/ise.py index b8f33706..86e1e05a 100644 --- a/pyfpga/ise.py +++ b/pyfpga/ise.py @@ -23,29 +23,29 @@ class Ise(Project): def _make_prepare(self, steps): info = get_info(self.data.get('part', 'xc7k160t-3-fbg484')) context = { - 'PROJECT': self.name or 'ise', - 'FAMILY': info['family'], - 'DEVICE': info['device'], - 'SPEED': info['speed'], - 'PACKAGE': info['package'] + 'project': self.name or 'ise', + 'family': info['family'], + 'device': info['device'], + 'speed': info['speed'], + 'package': info['package'] } for step in steps: context[step] = 1 - context['INCLUDES'] = self.data.get('includes', None) - context['FILES'] = self.data.get('files', None) - context['CONSTRAINTS'] = self.data.get('constraints', None) - context['TOP'] = self.data.get('top', None) - context['DEFINES'] = self.data.get('defines', None) - context['PARAMS'] = self.data.get('params', None) + context['includes'] = self.data.get('includes', None) + context['files'] = self.data.get('files', None) + context['constraints'] = self.data.get('constraints', None) + context['top'] = self.data.get('top', None) + context['defines'] = self.data.get('defines', None) + context['params'] = self.data.get('params', None) if 'hooks' in self.data: - context['PRECFG'] = self.data['hooks'].get('precfg', None) - context['POSTCFG'] = self.data['hooks'].get('postcfg', None) - context['PRESYN'] = self.data['hooks'].get('presyn', None) - context['POSTSYN'] = self.data['hooks'].get('postsyn', None) - context['PREPAR'] = self.data['hooks'].get('prepar', None) - context['POSTPAR'] = self.data['hooks'].get('postpar', None) - context['PRESBIT'] = self.data['hooks'].get('prebit', None) - context['POSTBIT'] = self.data['hooks'].get('postbit', None) + context['precfg'] = self.data['hooks'].get('precfg', None) + context['postcfg'] = self.data['hooks'].get('postcfg', None) + context['presyn'] = self.data['hooks'].get('presyn', None) + context['postsyn'] = self.data['hooks'].get('postsyn', None) + context['prepar'] = self.data['hooks'].get('prepar', None) + context['postpar'] = self.data['hooks'].get('postpar', None) + context['presbit'] = self.data['hooks'].get('prebit', None) + context['postbit'] = self.data['hooks'].get('postbit', None) self._create_file('ise', 'tcl', context) return 'xtclsh ise.tcl' @@ -53,7 +53,7 @@ def _prog_prepare(self, bitstream, position): if not bitstream: basename = self.name or 'ise' bitstream = f'{basename}.bit' - context = {'BITSTREAM': bitstream, 'POSITION': position} + context = {'bitstream': bitstream, 'position': position} self._create_file('vivado-prog', 'tcl', context) return 'impact -batch impact-prog' diff --git a/pyfpga/libero.py b/pyfpga/libero.py index 7c07a6a1..45536a6d 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -23,29 +23,29 @@ class Libero(Project): def _make_prepare(self, steps): info = get_info(self.data.get('part', 'mpf100t-1-fcg484')) context = { - 'PROJECT': self.name or 'libero', - 'FAMILY': info['family'], - 'DEVICE': info['device'], - 'SPEED': info['speed'], - 'PACKAGE': info['package'] + 'project': self.name or 'libero', + 'family': info['family'], + 'device': info['device'], + 'speed': info['speed'], + 'package': info['package'] } for step in steps: context[step] = 1 - context['INCLUDES'] = self.data.get('includes', None) - context['FILES'] = self.data.get('files', None) - context['CONSTRAINTS'] = self.data.get('constraints', None) - context['TOP'] = self.data.get('top', None) - context['DEFINES'] = self.data.get('defines', None) - context['PARAMS'] = self.data.get('params', None) + context['includes'] = self.data.get('includes', None) + context['files'] = self.data.get('files', None) + context['constraints'] = self.data.get('constraints', None) + context['top'] = self.data.get('top', None) + context['defines'] = self.data.get('defines', None) + context['params'] = self.data.get('params', None) if 'hooks' in self.data: - context['PRECFG'] = self.data['hooks'].get('precfg', None) - context['POSTCFG'] = self.data['hooks'].get('postcfg', None) - context['PRESYN'] = self.data['hooks'].get('presyn', None) - context['POSTSYN'] = self.data['hooks'].get('postsyn', None) - context['PREPAR'] = self.data['hooks'].get('prepar', None) - context['POSTPAR'] = self.data['hooks'].get('postpar', None) - context['PRESBIT'] = self.data['hooks'].get('prebit', None) - context['POSTBIT'] = self.data['hooks'].get('postbit', None) + context['precfg'] = self.data['hooks'].get('precfg', None) + context['postcfg'] = self.data['hooks'].get('postcfg', None) + context['presyn'] = self.data['hooks'].get('presyn', None) + context['postsyn'] = self.data['hooks'].get('postsyn', None) + context['prepar'] = self.data['hooks'].get('prepar', None) + context['postpar'] = self.data['hooks'].get('postpar', None) + context['presbit'] = self.data['hooks'].get('prebit', None) + context['postbit'] = self.data['hooks'].get('postbit', None) self._create_file('libero', 'tcl', context) return 'libero SCRIPT:libero.tcl' diff --git a/pyfpga/openflow.py b/pyfpga/openflow.py index 63712a85..c8663277 100644 --- a/pyfpga/openflow.py +++ b/pyfpga/openflow.py @@ -21,28 +21,28 @@ class Openflow(Project): def _make_prepare(self, steps): info = get_info(self.data.get('part', 'hx8k-ct256')) context = { - 'PROJECT': self.name or 'openflow', - 'FAMILY': info['family'], - 'DEVICE': info['device'], - 'PACKAGE': info['package'] + 'project': self.name or 'openflow', + 'family': info['family'], + 'device': info['device'], + 'package': info['package'] } for step in steps: context[step] = 1 - context['INCLUDES'] = self.data.get('includes', None) - context['FILES'] = self.data.get('files', None) - context['CONSTRAINTS'] = self.data.get('constraints', None) - context['TOP'] = self.data.get('top', None) - context['DEFINES'] = self.data.get('defines', None) - context['PARAMS'] = self.data.get('params', None) + context['includes'] = self.data.get('includes', None) + context['files'] = self.data.get('files', None) + context['constraints'] = self.data.get('constraints', None) + context['top'] = self.data.get('top', None) + context['defines'] = self.data.get('defines', None) + context['params'] = self.data.get('params', None) if 'hooks' in self.data: - context['PRECFG'] = self.data['hooks'].get('precfg', None) - context['POSTCFG'] = self.data['hooks'].get('postcfg', None) - context['PRESYN'] = self.data['hooks'].get('presyn', None) - context['POSTSYN'] = self.data['hooks'].get('postsyn', None) - context['PREPAR'] = self.data['hooks'].get('prepar', None) - context['POSTPAR'] = self.data['hooks'].get('postpar', None) - context['PRESBIT'] = self.data['hooks'].get('prebit', None) - context['POSTBIT'] = self.data['hooks'].get('postbit', None) + context['precfg'] = self.data['hooks'].get('precfg', None) + context['postcfg'] = self.data['hooks'].get('postcfg', None) + context['presyn'] = self.data['hooks'].get('presyn', None) + context['postsyn'] = self.data['hooks'].get('postsyn', None) + context['prepar'] = self.data['hooks'].get('prepar', None) + context['postpar'] = self.data['hooks'].get('postpar', None) + context['presbit'] = self.data['hooks'].get('prebit', None) + context['postbit'] = self.data['hooks'].get('postbit', None) self._create_file('openflow', 'sh', context) return 'bash openflow.sh' @@ -51,7 +51,7 @@ def _prog_prepare(self, bitstream, position): if not bitstream: basename = self.name or 'openflow' bitstream = f'{basename}.bit' - context = {'BITSTREAM': bitstream} + context = {'bitstream': bitstream} self._create_file('openflow-prog', 'sh', context) return 'bash openflow-prog.sh' diff --git a/pyfpga/project.py b/pyfpga/project.py index 16ee090e..3ad42b71 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -226,7 +226,7 @@ def make(self, first='cfg', last='bit'): if first == last: message = steps[first] self.logger.info('Running %s', message) - selected = [step.upper() for step in keys[index[0]:index[1]+1]] + selected = keys[index[0]:index[1]+1] self._run(self._make_prepare(selected), 'make.log') def prog(self, bitstream=None, position=1): diff --git a/pyfpga/quartus.py b/pyfpga/quartus.py index 0cc5f93d..5ffa3c71 100644 --- a/pyfpga/quartus.py +++ b/pyfpga/quartus.py @@ -20,26 +20,26 @@ class Quartus(Project): def _make_prepare(self, steps): context = { - 'PROJECT': self.name or 'quartus', - 'PART': self.data.get('part', '10M50SCE144I7G') + 'project': self.name or 'quartus', + 'part': self.data.get('part', '10M50SCE144I7G') } for step in steps: context[step] = 1 - context['INCLUDES'] = self.data.get('includes', None) - context['FILES'] = self.data.get('files', None) - context['CONSTRAINTS'] = self.data.get('constraints', None) - context['TOP'] = self.data.get('top', None) - context['DEFINES'] = self.data.get('defines', None) - context['PARAMS'] = self.data.get('params', None) + context['includes'] = self.data.get('includes', None) + context['files'] = self.data.get('files', None) + context['constraints'] = self.data.get('constraints', None) + context['top'] = self.data.get('top', None) + context['defines'] = self.data.get('defines', None) + context['params'] = self.data.get('params', None) if 'hooks' in self.data: - context['PRECFG'] = self.data['hooks'].get('precfg', None) - context['POSTCFG'] = self.data['hooks'].get('postcfg', None) - context['PRESYN'] = self.data['hooks'].get('presyn', None) - context['POSTSYN'] = self.data['hooks'].get('postsyn', None) - context['PREPAR'] = self.data['hooks'].get('prepar', None) - context['POSTPAR'] = self.data['hooks'].get('postpar', None) - context['PRESBIT'] = self.data['hooks'].get('prebit', None) - context['POSTBIT'] = self.data['hooks'].get('postbit', None) + context['precfg'] = self.data['hooks'].get('precfg', None) + context['postcfg'] = self.data['hooks'].get('postcfg', None) + context['presyn'] = self.data['hooks'].get('presyn', None) + context['postsyn'] = self.data['hooks'].get('postsyn', None) + context['prepar'] = self.data['hooks'].get('prepar', None) + context['postpar'] = self.data['hooks'].get('postpar', None) + context['presbit'] = self.data['hooks'].get('prebit', None) + context['postbit'] = self.data['hooks'].get('postbit', None) self._create_file('quartus', 'tcl', context) return 'quartus_sh --script quartus.tcl' @@ -49,6 +49,6 @@ def _prog_prepare(self, bitstream, position): if not bitstream: basename = self.name or 'quartus' bitstream = f'{basename}.sof' - context = {'BITSTREAM': bitstream, 'POSITION': position} + context = {'bitstream': bitstream, 'position': position} self._create_file('quartus-prog', 'tcl', context) return 'bash quartus-prog.sh' diff --git a/pyfpga/templates/ise-prog.jinja b/pyfpga/templates/ise-prog.jinja index 1c2203ee..abf4cb54 100644 --- a/pyfpga/templates/ise-prog.jinja +++ b/pyfpga/templates/ise-prog.jinja @@ -10,51 +10,51 @@ cleancablelock {# ------------------------------------------------------------------------- #} -{% if FPGA %} +{% if fpga %} setMode -bs setCable -port auto Identify -inferir -assignFile -p {{ POSITION }} -file {{ BITSTREAM }} -Program -p {{ POSITION }} +assignFile -p {{ position }} -file {{ bitstream }} +Program -p {{ position }} {% endif %} {# ------------------------------------------------------------------------- #} -{% if SPI %} +{% if spi %} setMode -pff -addConfigDevice -name {{ NAME }} -path . +addConfigDevice -name {{ name }} -path . setSubmode -pffspi addDesign -version 0 -name 0 addDeviceChain -index 0 -addDevice -p 1 -file {{ BITSTREAM }} +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 +attachflash -position {{ position }} -spi {{ name }} +assignfiletoattachedflash -position {{ position }} -file ./{{ name }}.mcs +Program -p {{ position }} -dataWidth {{ width }} -spionly -e -v -loadfpga {% endif %} {# ------------------------------------------------------------------------- #} -{% if BPI %} +{% if bpi %} setMode -pff -addConfigDevice -name {{ NAME }} -path . +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 }} +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 +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 %} {# ------------------------------------------------------------------------- #} diff --git a/pyfpga/templates/ise.jinja b/pyfpga/templates/ise.jinja index 6af287ca..eec1c970 100644 --- a/pyfpga/templates/ise.jinja +++ b/pyfpga/templates/ise.jinja @@ -6,26 +6,26 @@ # #} -{% if CFG %}# Project configuration ------------------------------------------------------- +{% if cfg %}# 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 { [ 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 }} -{{ PRECFG }} +{{ precfg }} -{% if FILES %}# Files inclusion -{% for name, attr in FILES.items() %} +{% 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() %} +{% 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" @@ -33,35 +33,35 @@ project set "Synthesis Constraints File" "{{ name }}" -process "Synthesize - XST {% endfor %} {% endif %} -{% if TOP %}# Top-level specification -project set top {{ TOP }} +{% if top %}# Top-level specification +project set top {{ top }} {% endif %} -{% if INCLUDES %}# Verilog Includes -project set "Verilog Include Directories" "{{ INCLUDES | join('|') }}" -process "Synthesize - XST" +{% 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" +{% 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" +{% if params %}# Verilog Parameters / VHDL Generics +project set "Generics, Parameters" "{{ params.items() | map('join', '=') | join(' ') }}" -process "Synthesize - XST" {% endif %} -{{ POSTCFG }} +{{ postcfg }} project close {% endif %} -{% if SYN or PAR or BIT %}# Design flow ----------------------------------------------------------------- +{% if syn or par or bit %}# Design flow ----------------------------------------------------------------- -project open {{ PROJECT }}.xise +project open {{ project }}.xise -{% if SYN %}# Synthesis +{% if syn %}# Synthesis -{{ PRESYN }} +{{ presyn }} # PRESYNTH #project set top_level_module_type "EDIF" @@ -69,13 +69,13 @@ project clean process run "Synthesize" if { [process get "Synthesize" status] == "errors" } { exit 1 } -{{ POSTSYN }} +{{ postsyn }} {% endif %} -{% if PAR %}# Place and Route +{% if par %}# Place and Route -{{ PREPAR }} +{{ prepar }} process run "Translate" if { [process get "Translate" status] == "errors" } { exit 1 } @@ -84,19 +84,19 @@ if { [process get "Map" status] == "errors" } { exit 1 } process run "Place & Route" if { [process get "Place & Route" status] == "errors" } { exit 1 } -{{ POSTPAR }} +{{ postpar }} {% endif %} -{% if BIT %}# Bitstream generation +{% if bit %}# Bitstream generation -{{ PREBIT }} +{{ prebit }} process run "Generate Programming File" if { [process get "Generate Programming File" status] == "errors" } { exit 1 } -catch { file rename -force {{ TOP }}.bit {{ PROJECT }}.bit } +catch { file rename -force {{ top }}.bit {{ project }}.bit } -{{ POSTBIT }} +{{ postbit }} {% endif %} diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index a4be188f..77176050 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -6,40 +6,40 @@ # #} -{% if CFG %}# Project configuration ------------------------------------------------------- +{% if cfg %}# Project configuration ------------------------------------------------------- -if { [ file exists {{ PROJECT }} ] } { file delete -force -- {{ PROJECT }} } -new_project -name {{ PROJECT }} -location {libero} -hdl {VERILOG} -family {SmartFusion2} -set_device -family {{ FAMILY }} -die {{ DEVICE }} -package {{ PACKAGE }} -speed {{ SPEED }} +if { [ file exists {{ project }} ] } { file delete -force -- {{ project }} } +new_project -name {{ project }} -location {libero} -hdl {VERILOG} -family {SmartFusion2} +set_device -family {{ family }} -die {{ device }} -package {{ package }} -speed {{ speed }} -{{ PRECFG }} +{{ precfg }} -{% if INCLUDES %}# Verilog Includes (Libero) -set_global_include_path_order -paths "{{ INCLUDES | join(' ') }}" +{% if includes %}# Verilog Includes (Libero) +set_global_include_path_order -paths "{{ includes | join(' ') }}" {% endif %} -{% if FILES %}# Files inclusion -{% for name, attr in FILES.items() %} +{% 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() %} +{% 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 }} +{% if top %}# Top-level specification +set_root {{ top }} {% endif %} -{% if CONSTRAINTS %}# Constraints configuration +{% if constraints %}# Constraints configuration {% set sdc_files = [] %} {% set pdc_files = [] %} -{% for name, attr in CONSTRAINTS.items() %} +{% for name, attr in constraints.items() %} {% if name.endswith('.sdc') %} {% set _ = sdc_files.append(name) %} {% endif %} @@ -47,27 +47,27 @@ set_root {{ TOP }} {% 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 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 +{% 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 }}" +{% 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() %} +{% 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() %} +{% if params %}# Verilog Parameters / VHDL Generics (Synopsys) +{% for key, value in params.items() %} set_option -hdl_param -set {{ key }}={{ value }} {% endfor %} {% endif %} @@ -75,44 +75,44 @@ configure_tool -name {SYNTHESIZE} -params {SYNPLIFY_OPTIONS: } {% endif %} -{{ POSTCFG }} +{{ postcfg }} close_project {% endif %} -{% if SYN or PAR or BIT %}# Design flow ----------------------------------------------------------------- +{% if syn or par or bit %}# Design flow ----------------------------------------------------------------- -open_project {{ PROJECT }}/{{ PROJECT }}.prjx +open_project {{ project }}/{{ project }}.prjx -{% if SYN %}# Synthesis +{% if syn %}# Synthesis -{{ PRESYN }} +{{ presyn }} run_tool -name {SYNTHESIZE} -{{ POSTSYN }} +{{ postsyn }} {% endif %} -{% if PAR %}# Place and Route +{% if par %}# Place and Route -{{ PREPAR }} +{{ prepar }} run_tool -name {PLACEROUTE} run_tool -name {VERIFYTIMING} -{{ POSTPAR }} +{{ postpar }} {% endif %} -{% if BIT %}# Bitstream generation +{% if bit %}# Bitstream generation -{{ PREBIT }} +{{ prebit }} run_tool -name {GENERATEPROGRAMMINGFILE} -{{ POSTBIT }} +{{ postbit }} {% endif %} diff --git a/pyfpga/templates/openflow-prog.jinja b/pyfpga/templates/openflow-prog.jinja index 81942426..47fad80d 100644 --- a/pyfpga/templates/openflow-prog.jinja +++ b/pyfpga/templates/openflow-prog.jinja @@ -9,10 +9,10 @@ set -e DOCKER="docker run --user $(id -u):$(id -g) --rm -v $HOME:$HOME -w $PWD" -{% if FAMILY == 'ice40' %} -$DOCKER --device /dev/bus/usb hdlc/prog iceprog {{ PROJECT }}.bit +{% if family == 'ice40' %} +$DOCKER --device /dev/bus/usb hdlc/prog iceprog {{ project }}.bit {% endif %} -{% 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 {{ PROJECT }}.svf; exit" +{% 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 {{ project }}.svf; exit" {% endif %} diff --git a/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja index 02e57662..5aa9b29b 100644 --- a/pyfpga/templates/openflow.jinja +++ b/pyfpga/templates/openflow.jinja @@ -10,21 +10,21 @@ set -e DOCKER="docker run --user $(id -u):$(id -g) --rm -v $HOME:$HOME -w $PWD" -{% if SYN %}# Synthesis +{% if syn %}# Synthesis $DOCKER hdlc/ghdl:yosys /bin/bash -c " -{{ PRESYN }} +{{ presyn }} yosys -Q -m ghdl -p ' -{% if INCLUDES %} -verilog_defaults -add{% for path in INCLUDES %} -I{{ path }}{% endfor %} +{% if includes %} +verilog_defaults -add{% for path in includes %} -I{{ path }}{% endfor %} {% endif %} -{% if DEFINES %} -verilog_defines{% for key, value in DEFINES.items() %} -D{{ key }}={{ value }}{% endfor %} +{% if defines %} +verilog_defines{% for key, value in defines.items() %} -D{{ key }}={{ value }}{% endfor %} {% endif %} -{% if FILES %} -{% for name, attr in FILES.items() %} +{% if files %} +{% for name, attr in files.items() %} {% if attr.hdl == "vlog" %} read_verilog -defer {{ name }} {% elif attr.hdl == "slog" %} @@ -33,14 +33,14 @@ read_verilog -defer -sv {{ name }} {% endfor %} {% endif %} -{% if PARAMS %} -chparam{% for key, value in PARAMS.items() %} -set {{ key }} {{ value }}{% endfor %} +{% if params %} +chparam{% for key, value in params.items() %} -set {{ key }} {{ value }}{% endfor %} {% endif %} -synth -top {{ TOP }} -synth_{{ FAMILY }} -top {{ TOP }} -json {{ PROJECT }}.json +synth -top {{ top }} +synth_{{ family }} -top {{ top }} -json {{ project }}.json ' -{{ POSTSYN }} +{{ postsyn }} " {% endif %} @@ -63,54 +63,54 @@ synth_{{ FAMILY }} -top {{ TOP }} -json {{ PROJECT }}.json #fi #} -{% if PAR %}# Place and Route +{% if par %}# Place and Route -CONSTRAINTS="{{ CONSTRAINTS | join(' ') }}" +CONSTRAINTS="{{ constraints | join(' ') }}" -{% if FAMILY == 'ice40' %} +{% if family == 'ice40' %} if [ -n "$CONSTRAINTS" ]; then cat $CONSTRAINTS > constraints.pcf CONSTRAINT="--pcf constraints.pcf" fi $DOCKER hdlc/nextpnr:ice40 /bin/bash -c " -{{ PREPAR }} -nextpnr-ice40 --{{ DEVICE }} --package {{ PACKAGE }} $CONSTRAINT --json {{ PROJECT }}.json --asc {{ PROJECT }}.asc -{{ POSTPAR }} +{{ prepar }} +nextpnr-ice40 --{{ device }} --package {{ package }} $CONSTRAINT --json {{ project }}.json --asc {{ project }}.asc +{{ postpar }} " $DOCKER hdlc/icestorm /bin/bash -c " -icetime -d {{ DEVICE }} -mtr {{ PROJECT }}.rpt {{ PROJECT }}.asc +icetime -d {{ device }} -mtr {{ project }}.rpt {{ project }}.asc " {% endif %} -{% if FAMILY == 'ecp5' %} +{% if family == 'ecp5' %} if [ -n "$CONSTRAINTS" ]; then cat $CONSTRAINTS > constraints.lpf CONSTRAINT="--lpf constraints.lpf" fi $DOCKER hdlc/nextpnr:ecp5 /bin/bash -c " -{{ PREPAR }} -nextpnr-ecp5 --{{ DEVICE }} --package {{ PACKAGE }} $CONSTRAINT --json {{ PROJECT }}.json --textcfg {{ PROJECT }}.config -{{ POSTPAR }} +{{ prepar }} +nextpnr-ecp5 --{{ device }} --package {{ package }} $CONSTRAINT --json {{ project }}.json --textcfg {{ project }}.config +{{ postpar }} " {% endif %} {% endif %} -{% if BIT %}# Bitstream generation +{% if bit %}# Bitstream generation -{% if FAMILY == 'ice40' %} +{% if family == 'ice40' %} $DOCKER hdlc/icestorm /bin/bash -c " -{{ PREBIT }} -icepack {{ PROJECT }}.asc {{ PROJECT }}.bit -{{ POSTBIT }} +{{ prebit }} +icepack {{ project }}.asc {{ project }}.bit +{{ postbit }} " {% endif %} -{% if FAMILY == 'ecp5' %} +{% if family == 'ecp5' %} $DOCKER hdlc/prjtrellis /bin/bash -c " -{{ PREBIT }} -ecppack --svf {{ PROJECT }}.svf {{ PROJECT }}.config {{ PROJECT }}.bit -{{ POSTBIT }} +{{ prebit }} +ecppack --svf {{ project }}.svf {{ project }}.config {{ project }}.bit +{{ postbit }} " {% endif %} diff --git a/pyfpga/templates/quartus-prog.jinja b/pyfpga/templates/quartus-prog.jinja index 23786f22..0d7af942 100644 --- a/pyfpga/templates/quartus-prog.jinja +++ b/pyfpga/templates/quartus-prog.jinja @@ -12,4 +12,4 @@ 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 }}" +quartus_pgm -c $CABLE --mode jtag -o "p;{{ bitstream }}@{{ position }}" diff --git a/pyfpga/templates/quartus.jinja b/pyfpga/templates/quartus.jinja index dcbcb5d9..7ffb78a6 100644 --- a/pyfpga/templates/quartus.jinja +++ b/pyfpga/templates/quartus.jinja @@ -6,16 +6,16 @@ # #} -{% if CFG %}# Project configuration ------------------------------------------------------- +{% if cfg %}# Project configuration ------------------------------------------------------- package require ::quartus::project -project_new {{ PROJECT }} -overwrite -set_global_assignment -name DEVICE {{ PART }} +project_new {{ project }} -overwrite +set_global_assignment -name DEVICE {{ part }} -{{ PRECFG }} +{{ precfg }} -{% if FILES %}# Files inclusion -{% for name, attr in FILES.items() %} +{% 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" %} @@ -26,8 +26,8 @@ set_global_assignment -name SYSTEMVERILOG_FILE {{ name }} {% endfor %} {% endif %} -{% if CONSTRAINTS %}# Constraints inclusion -{% for name, attr in CONSTRAINTS.items() %} +{% if constraints %}# Constraints inclusion +{% for name, attr in constraints.items() %} {% if name.endswith('.sdc') %} set_global_assignment -name SDC_FILE {{ name }} {% else %} @@ -36,68 +36,68 @@ source {{ name }} {% endfor %} {% endif %} -{% if TOP %}# Top-level specification -set_global_assignment -name TOP_LEVEL_ENTITY {{ TOP }} +{% 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 }} +{% 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() %} +{% 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() %} +{% if params %}# Verilog Parameters / VHDL Generics +{% for key, value in params.items() %} set_parameter -name {{ key }} {{ value }} {% endfor %} {% endif %} -{{ POSTCFG }} +{{ postcfg }} project_close {% endif %} -{% if SYN or PAR or BIT %}# Design flow ----------------------------------------------------------------- +{% if syn or par or bit %}# Design flow ----------------------------------------------------------------- package require ::quartus::flow -project_open -force {{ PROJECT }}.qpf +project_open -force {{ project }}.qpf # set_global_assignment -name NUM_PARALLEL_PROCESSORS ALL -{% if SYN %}# Synthesis +{% if syn %}# Synthesis -{{ PRESYN }} +{{ presyn }} execute_module -tool map -{{ POSTSYN }} +{{ postsyn }} {% endif %} -{% if PAR %}# Place and Route +{% if par %}# Place and Route -{{ PREPAR }} +{{ prepar }} execute_module -tool fit execute_module -tool sta -{{ POSTPAR }} +{{ postpar }} {% endif %} -{% if BIT %}# Bitstream generation +{% if bit %}# Bitstream generation -{{ PREBIT }} +{{ prebit }} execute_module -tool asm -{{ POSTBIT }} +{{ postbit }} {% endif %} diff --git a/pyfpga/templates/vivado-prog.jinja b/pyfpga/templates/vivado-prog.jinja index 72ccb91d..eeeab0ca 100644 --- a/pyfpga/templates/vivado-prog.jinja +++ b/pyfpga/templates/vivado-prog.jinja @@ -11,5 +11,5 @@ 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 +set_property PROGRAM.FILE {{ bitstream }} $obj program_hw_devices $obj diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index 19a53aee..6aa4e456 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -6,24 +6,24 @@ # #} -{% if CFG %}# Project configuration ------------------------------------------------------- +{% if cfg %}# Project configuration ------------------------------------------------------- -create_project -force {{ PROJECT }} +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] +set_property PART {{ part }} [current_project] -{{ PRECFG }} +{{ precfg }} -{% if FILES %}# Files inclusion -{% for name, attr in FILES.items() %} +{% 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() %} +{% if constraints %}# Constraints inclusion +{% for name, attr in constraints.items() %} add_file -fileset constrs_1 {{ name }} {% if attr == "syn" %} set_property USED_IN_IMPLEMENTATION FALSE [get_files {{ name }}] @@ -34,35 +34,35 @@ set_property USED_IN_SYNTHESIS FALSE [get_files {{ name }}] {% endfor %} {% endif %} -{% if TOP %}# Top-level specification -set_property TOP {{ TOP }} [current_fileset] +{% if top %}# Top-level specification +set_property TOP {{ top }} [current_fileset] {% endif %} -{% if INCLUDES %}# Verilog Includes -set_property INCLUDE_DIRS { {{ INCLUDES | join(' ') }} } [current_fileset] +{% 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] +{% 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] +{% if params %}# Verilog Parameters / VHDL Generics +set_property GENERIC { {{ params.items() | map('join', '=') | join(' ') }} } -objects [get_filesets sources_1] {% endif %} -{{ POSTCFG }} +{{ postcfg }} close_project {% endif %} -{% if SYN or PAR or BIT %}# Design flow ----------------------------------------------------------------- +{% if syn or par or bit %}# Design flow ----------------------------------------------------------------- -open_project {{ PROJECT }} +open_project {{ project }} -{% if SYN %}# Synthesis +{% if syn %}# Synthesis -{{ PRESYN }} +{{ presyn }} # PRESYNTH # set_property DESIGN_MODE GateLvl [current_fileset] @@ -73,13 +73,13 @@ wait_on_run synth_1 if { [get_property STATUS [get_runs synth_1]] ne "synth_design Complete!" } { exit 1 } -{{ POSTSYN }} +{{ postsyn }} {% endif %} -{% if PAR %}# Place and Route +{% if par %}# Place and Route -{{ PREPAR }} +{{ prepar }} reset_run impl_1 launch_runs impl_1 @@ -87,19 +87,19 @@ 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 } -{{ POSTPAR }} +{{ postpar }} {% endif %} -{% if BIT %}# Bitstream generation +{% if bit %}# Bitstream generation -{{ PREBIT }} +{{ prebit }} open_run impl_1 -write_bitstream -force {{ PROJECT }} -write_debug_probes -force -quiet {{ PROJECT }}.ltx +write_bitstream -force {{ project }} +write_debug_probes -force -quiet {{ project }}.ltx -{{ POSTBIT }} +{{ postbit }} {% endif %} diff --git a/pyfpga/vivado.py b/pyfpga/vivado.py index 3cc39d6e..b5f59f4e 100644 --- a/pyfpga/vivado.py +++ b/pyfpga/vivado.py @@ -19,26 +19,26 @@ class Vivado(Project): def _make_prepare(self, steps): context = { - 'PROJECT': self.name or 'vivado', - 'PART': self.data.get('part', 'xc7k160t-3-fbg484') + 'project': self.name or 'vivado', + 'part': self.data.get('part', 'xc7k160t-3-fbg484') } for step in steps: context[step] = 1 - context['INCLUDES'] = self.data.get('includes', None) - context['FILES'] = self.data.get('files', None) - context['CONSTRAINTS'] = self.data.get('constraints', None) - context['TOP'] = self.data.get('top', None) - context['DEFINES'] = self.data.get('defines', None) - context['PARAMS'] = self.data.get('params', None) + context['includes'] = self.data.get('includes', None) + context['files'] = self.data.get('files', None) + context['constraints'] = self.data.get('constraints', None) + context['top'] = self.data.get('top', None) + context['defines'] = self.data.get('defines', None) + context['params'] = self.data.get('params', None) if 'hooks' in self.data: - context['PRECFG'] = self.data['hooks'].get('precfg', None) - context['POSTCFG'] = self.data['hooks'].get('postcfg', None) - context['PRESYN'] = self.data['hooks'].get('presyn', None) - context['POSTSYN'] = self.data['hooks'].get('postsyn', None) - context['PREPAR'] = self.data['hooks'].get('prepar', None) - context['POSTPAR'] = self.data['hooks'].get('postpar', None) - context['PRESBIT'] = self.data['hooks'].get('prebit', None) - context['POSTBIT'] = self.data['hooks'].get('postbit', None) + context['precfg'] = self.data['hooks'].get('precfg', None) + context['postcfg'] = self.data['hooks'].get('postcfg', None) + context['presyn'] = self.data['hooks'].get('presyn', None) + context['postsyn'] = self.data['hooks'].get('postsyn', None) + context['prepar'] = self.data['hooks'].get('prepar', None) + context['postpar'] = self.data['hooks'].get('postpar', None) + context['presbit'] = self.data['hooks'].get('prebit', None) + context['postbit'] = self.data['hooks'].get('postbit', None) self._create_file('vivado', 'tcl', context) return 'vivado -mode batch -notrace -quiet -source vivado.tcl' From 9e63f29f18f3b4935cf7596e612f7d42607af7d4 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Tue, 9 Jul 2024 00:25:15 -0300 Subject: [PATCH 164/248] Modify how the flow steps and hooks are used on the templates --- pyfpga/ise.py | 13 ++----------- pyfpga/libero.py | 13 ++----------- pyfpga/openflow.py | 13 ++----------- pyfpga/quartus.py | 13 ++----------- pyfpga/templates/ise.jinja | 26 +++++++++++++------------- pyfpga/templates/libero.jinja | 26 +++++++++++++------------- pyfpga/templates/openflow.jinja | 26 +++++++++++++------------- pyfpga/templates/quartus.jinja | 26 +++++++++++++------------- pyfpga/templates/vivado.jinja | 27 +++++++++++++-------------- pyfpga/vivado.py | 13 ++----------- 10 files changed, 75 insertions(+), 121 deletions(-) diff --git a/pyfpga/ise.py b/pyfpga/ise.py index 86e1e05a..b2d102a0 100644 --- a/pyfpga/ise.py +++ b/pyfpga/ise.py @@ -29,23 +29,14 @@ def _make_prepare(self, steps): 'speed': info['speed'], 'package': info['package'] } - for step in steps: - context[step] = 1 + context['steps'] = steps context['includes'] = self.data.get('includes', None) context['files'] = self.data.get('files', None) context['constraints'] = self.data.get('constraints', None) context['top'] = self.data.get('top', None) context['defines'] = self.data.get('defines', None) context['params'] = self.data.get('params', None) - if 'hooks' in self.data: - context['precfg'] = self.data['hooks'].get('precfg', None) - context['postcfg'] = self.data['hooks'].get('postcfg', None) - context['presyn'] = self.data['hooks'].get('presyn', None) - context['postsyn'] = self.data['hooks'].get('postsyn', None) - context['prepar'] = self.data['hooks'].get('prepar', None) - context['postpar'] = self.data['hooks'].get('postpar', None) - context['presbit'] = self.data['hooks'].get('prebit', None) - context['postbit'] = self.data['hooks'].get('postbit', None) + context['hooks'] = self.data.get('hooks', None) self._create_file('ise', 'tcl', context) return 'xtclsh ise.tcl' diff --git a/pyfpga/libero.py b/pyfpga/libero.py index 45536a6d..00014dc8 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -29,23 +29,14 @@ def _make_prepare(self, steps): 'speed': info['speed'], 'package': info['package'] } - for step in steps: - context[step] = 1 + context['steps'] = steps context['includes'] = self.data.get('includes', None) context['files'] = self.data.get('files', None) context['constraints'] = self.data.get('constraints', None) context['top'] = self.data.get('top', None) context['defines'] = self.data.get('defines', None) context['params'] = self.data.get('params', None) - if 'hooks' in self.data: - context['precfg'] = self.data['hooks'].get('precfg', None) - context['postcfg'] = self.data['hooks'].get('postcfg', None) - context['presyn'] = self.data['hooks'].get('presyn', None) - context['postsyn'] = self.data['hooks'].get('postsyn', None) - context['prepar'] = self.data['hooks'].get('prepar', None) - context['postpar'] = self.data['hooks'].get('postpar', None) - context['presbit'] = self.data['hooks'].get('prebit', None) - context['postbit'] = self.data['hooks'].get('postbit', None) + context['hooks'] = self.data.get('hooks', None) self._create_file('libero', 'tcl', context) return 'libero SCRIPT:libero.tcl' diff --git a/pyfpga/openflow.py b/pyfpga/openflow.py index c8663277..feb7a41e 100644 --- a/pyfpga/openflow.py +++ b/pyfpga/openflow.py @@ -26,23 +26,14 @@ def _make_prepare(self, steps): 'device': info['device'], 'package': info['package'] } - for step in steps: - context[step] = 1 + context['steps'] = steps context['includes'] = self.data.get('includes', None) context['files'] = self.data.get('files', None) context['constraints'] = self.data.get('constraints', None) context['top'] = self.data.get('top', None) context['defines'] = self.data.get('defines', None) context['params'] = self.data.get('params', None) - if 'hooks' in self.data: - context['precfg'] = self.data['hooks'].get('precfg', None) - context['postcfg'] = self.data['hooks'].get('postcfg', None) - context['presyn'] = self.data['hooks'].get('presyn', None) - context['postsyn'] = self.data['hooks'].get('postsyn', None) - context['prepar'] = self.data['hooks'].get('prepar', None) - context['postpar'] = self.data['hooks'].get('postpar', None) - context['presbit'] = self.data['hooks'].get('prebit', None) - context['postbit'] = self.data['hooks'].get('postbit', None) + context['hooks'] = self.data.get('hooks', None) self._create_file('openflow', 'sh', context) return 'bash openflow.sh' diff --git a/pyfpga/quartus.py b/pyfpga/quartus.py index 5ffa3c71..1f4cb2e5 100644 --- a/pyfpga/quartus.py +++ b/pyfpga/quartus.py @@ -23,23 +23,14 @@ def _make_prepare(self, steps): 'project': self.name or 'quartus', 'part': self.data.get('part', '10M50SCE144I7G') } - for step in steps: - context[step] = 1 + context['steps'] = steps context['includes'] = self.data.get('includes', None) context['files'] = self.data.get('files', None) context['constraints'] = self.data.get('constraints', None) context['top'] = self.data.get('top', None) context['defines'] = self.data.get('defines', None) context['params'] = self.data.get('params', None) - if 'hooks' in self.data: - context['precfg'] = self.data['hooks'].get('precfg', None) - context['postcfg'] = self.data['hooks'].get('postcfg', None) - context['presyn'] = self.data['hooks'].get('presyn', None) - context['postsyn'] = self.data['hooks'].get('postsyn', None) - context['prepar'] = self.data['hooks'].get('prepar', None) - context['postpar'] = self.data['hooks'].get('postpar', None) - context['presbit'] = self.data['hooks'].get('prebit', None) - context['postbit'] = self.data['hooks'].get('postbit', None) + context['hooks'] = self.data.get('hooks', None) self._create_file('quartus', 'tcl', context) return 'quartus_sh --script quartus.tcl' diff --git a/pyfpga/templates/ise.jinja b/pyfpga/templates/ise.jinja index eec1c970..b44e003a 100644 --- a/pyfpga/templates/ise.jinja +++ b/pyfpga/templates/ise.jinja @@ -6,7 +6,7 @@ # #} -{% if cfg %}# Project configuration ------------------------------------------------------- +{% if 'cfg' in steps %}# Project configuration ------------------------------------------------------- if { [ file exists {{ project }}.xise ] } { file delete {{ project }}.xise } project new {{ project }}.xise @@ -15,7 +15,7 @@ project set device {{ device }} project set package {{ package }} project set speed -{{ speed }} -{{ precfg }} +{{ hooks.precfg | join('\n') }} {% if files %}# Files inclusion {% for name, attr in files.items() %} @@ -49,19 +49,19 @@ project set "Verilog Macros" "{{ defines.items() | map('join', '=') | join(' | ' project set "Generics, Parameters" "{{ params.items() | map('join', '=') | join(' ') }}" -process "Synthesize - XST" {% endif %} -{{ postcfg }} +{{ hooks.postcfg | join('\n') }} project close {% endif %} -{% if syn or par or bit %}# Design flow ----------------------------------------------------------------- +{% if 'syn' in steps or 'par' in steps or 'bit' in steps %}# Design flow ----------------------------------------------------------------- project open {{ project }}.xise -{% if syn %}# Synthesis +{% if 'syn' in steps %}# Synthesis -{{ presyn }} +{{ hooks.presyn | join('\n') }} # PRESYNTH #project set top_level_module_type "EDIF" @@ -69,13 +69,13 @@ project clean process run "Synthesize" if { [process get "Synthesize" status] == "errors" } { exit 1 } -{{ postsyn }} +{{ hooks.postsyn | join('\n') }} {% endif %} -{% if par %}# Place and Route +{% if 'par' in steps %}# Place and Route -{{ prepar }} +{{ hooks.prepar | join('\n') }} process run "Translate" if { [process get "Translate" status] == "errors" } { exit 1 } @@ -84,19 +84,19 @@ if { [process get "Map" status] == "errors" } { exit 1 } process run "Place & Route" if { [process get "Place & Route" status] == "errors" } { exit 1 } -{{ postpar }} +{{ hooks.postpar | join('\n') }} {% endif %} -{% if bit %}# Bitstream generation +{% if 'bit' in steps %}# Bitstream generation -{{ prebit }} +{{ hooks.prebit | join('\n') }} process run "Generate Programming File" if { [process get "Generate Programming File" status] == "errors" } { exit 1 } catch { file rename -force {{ top }}.bit {{ project }}.bit } -{{ postbit }} +{{ hooks.postbit | join('\n') }} {% endif %} diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index 77176050..310807d5 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -6,13 +6,13 @@ # #} -{% if cfg %}# Project configuration ------------------------------------------------------- +{% if 'cfg' in steps %}# Project configuration ------------------------------------------------------- if { [ file exists {{ project }} ] } { file delete -force -- {{ project }} } new_project -name {{ project }} -location {libero} -hdl {VERILOG} -family {SmartFusion2} set_device -family {{ family }} -die {{ device }} -package {{ package }} -speed {{ speed }} -{{ precfg }} +{{ hooks.precfg | join('\n') }} {% if includes %}# Verilog Includes (Libero) set_global_include_path_order -paths "{{ includes | join(' ') }}" @@ -75,44 +75,44 @@ configure_tool -name {SYNTHESIZE} -params {SYNPLIFY_OPTIONS: } {% endif %} -{{ postcfg }} +{{ hooks.postcfg | join('\n') }} close_project {% endif %} -{% if syn or par or bit %}# Design flow ----------------------------------------------------------------- +{% if 'syn' in steps or 'par' in steps or 'bit' in steps %}# Design flow ----------------------------------------------------------------- open_project {{ project }}/{{ project }}.prjx -{% if syn %}# Synthesis +{% if 'syn' in steps %}# Synthesis -{{ presyn }} +{{ hooks.presyn | join('\n') }} run_tool -name {SYNTHESIZE} -{{ postsyn }} +{{ hooks.postsyn | join('\n') }} {% endif %} -{% if par %}# Place and Route +{% if 'par' in steps %}# Place and Route -{{ prepar }} +{{ hooks.prepar | join('\n') }} run_tool -name {PLACEROUTE} run_tool -name {VERIFYTIMING} -{{ postpar }} +{{ hooks.postpar | join('\n') }} {% endif %} -{% if bit %}# Bitstream generation +{% if 'bit' in steps %}# Bitstream generation -{{ prebit }} +{{ hooks.prebit | join('\n') }} run_tool -name {GENERATEPROGRAMMINGFILE} -{{ postbit }} +{{ hooks.postbit | join('\n') }} {% endif %} diff --git a/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja index 5aa9b29b..05d01344 100644 --- a/pyfpga/templates/openflow.jinja +++ b/pyfpga/templates/openflow.jinja @@ -10,9 +10,9 @@ set -e DOCKER="docker run --user $(id -u):$(id -g) --rm -v $HOME:$HOME -w $PWD" -{% if syn %}# Synthesis +{% if 'syn' in steps %}# Synthesis $DOCKER hdlc/ghdl:yosys /bin/bash -c " -{{ presyn }} +{{ hooks.presyn | join('\n') }} yosys -Q -m ghdl -p ' {% if includes %} @@ -40,7 +40,7 @@ chparam{% for key, value in params.items() %} -set {{ key }} {{ value }}{% endfo synth -top {{ top }} synth_{{ family }} -top {{ top }} -json {{ project }}.json ' -{{ postsyn }} +{{ hooks.postsyn | join('\n') }} " {% endif %} @@ -63,7 +63,7 @@ synth_{{ family }} -top {{ top }} -json {{ project }}.json #fi #} -{% if par %}# Place and Route +{% if 'par' in steps %}# Place and Route CONSTRAINTS="{{ constraints | join(' ') }}" @@ -73,9 +73,9 @@ if [ -n "$CONSTRAINTS" ]; then CONSTRAINT="--pcf constraints.pcf" fi $DOCKER hdlc/nextpnr:ice40 /bin/bash -c " -{{ prepar }} +{{ hooks.prepar | join('\n') }} nextpnr-ice40 --{{ device }} --package {{ package }} $CONSTRAINT --json {{ project }}.json --asc {{ project }}.asc -{{ postpar }} +{{ hooks.postpar | join('\n') }} " $DOCKER hdlc/icestorm /bin/bash -c " icetime -d {{ device }} -mtr {{ project }}.rpt {{ project }}.asc @@ -88,29 +88,29 @@ if [ -n "$CONSTRAINTS" ]; then CONSTRAINT="--lpf constraints.lpf" fi $DOCKER hdlc/nextpnr:ecp5 /bin/bash -c " -{{ prepar }} +{{ hooks.prepar | join('\n') }} nextpnr-ecp5 --{{ device }} --package {{ package }} $CONSTRAINT --json {{ project }}.json --textcfg {{ project }}.config -{{ postpar }} +{{ hooks.postpar | join('\n') }} " {% endif %} {% endif %} -{% if bit %}# Bitstream generation +{% if 'bit' in steps %}# Bitstream generation {% if family == 'ice40' %} $DOCKER hdlc/icestorm /bin/bash -c " -{{ prebit }} +{{ hooks.prebit | join('\n') }} icepack {{ project }}.asc {{ project }}.bit -{{ postbit }} +{{ hooks.postbit | join('\n') }} " {% endif %} {% if family == 'ecp5' %} $DOCKER hdlc/prjtrellis /bin/bash -c " -{{ prebit }} +{{ prebit | join('\n') }} ecppack --svf {{ project }}.svf {{ project }}.config {{ project }}.bit -{{ postbit }} +{{ postbit | join('\n') }} " {% endif %} diff --git a/pyfpga/templates/quartus.jinja b/pyfpga/templates/quartus.jinja index 7ffb78a6..468b88ca 100644 --- a/pyfpga/templates/quartus.jinja +++ b/pyfpga/templates/quartus.jinja @@ -6,13 +6,13 @@ # #} -{% if cfg %}# Project configuration ------------------------------------------------------- +{% if 'cfg' in steps %}# Project configuration ------------------------------------------------------- package require ::quartus::project project_new {{ project }} -overwrite set_global_assignment -name DEVICE {{ part }} -{{ precfg }} +{{ hooks.precfg | join('\n') }} {% if files %}# Files inclusion {% for name, attr in files.items() %} @@ -58,46 +58,46 @@ set_parameter -name {{ key }} {{ value }} {% endfor %} {% endif %} -{{ postcfg }} +{{ hooks.postcfg | join('\n') }} project_close {% endif %} -{% if syn or par or bit %}# Design flow ----------------------------------------------------------------- +{% 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 %}# Synthesis +{% if 'syn' in steps %}# Synthesis -{{ presyn }} +{{ hooks.presyn | join('\n') }} execute_module -tool map -{{ postsyn }} +{{ hooks.postsyn | join('\n') }} {% endif %} -{% if par %}# Place and Route +{% if 'par' in steps %}# Place and Route -{{ prepar }} +{{ hooks.prepar | join('\n') }} execute_module -tool fit execute_module -tool sta -{{ postpar }} +{{ hooks.postpar | join('\n') }} {% endif %} -{% if bit %}# Bitstream generation +{% if 'bit' in steps %}# Bitstream generation -{{ prebit }} +{{ hooks.prebit | join('\n') }} execute_module -tool asm -{{ postbit }} +{{ hooks.postbit | join('\n') }} {% endif %} diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index 6aa4e456..573a7fcb 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -6,14 +6,14 @@ # #} -{% if cfg %}# Project configuration ------------------------------------------------------- +{% 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] -{{ precfg }} +{{ hooks.precfg | join('\n') }} {% if files %}# Files inclusion {% for name, attr in files.items() %} @@ -50,19 +50,19 @@ set_property VERILOG_DEFINE { {{ defines.items() | map('join', '=') | join(' ') set_property GENERIC { {{ params.items() | map('join', '=') | join(' ') }} } -objects [get_filesets sources_1] {% endif %} -{{ postcfg }} +{{ hooks.postcfg | join('\n') }} close_project {% endif %} -{% if syn or par or bit %}# Design flow ----------------------------------------------------------------- +{% if 'syn' in steps or 'par' in steps or 'bit' in steps %}# Design flow ----------------------------------------------------------------- open_project {{ project }} -{% if syn %}# Synthesis +{% if 'syn' in steps %}# Synthesis -{{ presyn }} +{{ hooks.presyn | join('\n') }} # PRESYNTH # set_property DESIGN_MODE GateLvl [current_fileset] @@ -70,16 +70,15 @@ 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 } -{{ postsyn }} +{{ hooks.postsyn | join('\n') }} {% endif %} -{% if par %}# Place and Route +{% if 'par' in steps %}# Place and Route -{{ prepar }} +{{ hooks.prepar | join('\n') }} reset_run impl_1 launch_runs impl_1 @@ -87,19 +86,19 @@ 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 } -{{ postpar }} +{{ hooks.postpar | join('\n') }} {% endif %} -{% if bit %}# Bitstream generation +{% if 'bit' in steps %}# Bitstream generation -{{ prebit }} +{{ hooks.prebit | join('\n') }} open_run impl_1 write_bitstream -force {{ project }} write_debug_probes -force -quiet {{ project }}.ltx -{{ postbit }} +{{ hooks.postbit | join('\n') }} {% endif %} diff --git a/pyfpga/vivado.py b/pyfpga/vivado.py index b5f59f4e..e68916f6 100644 --- a/pyfpga/vivado.py +++ b/pyfpga/vivado.py @@ -22,23 +22,14 @@ def _make_prepare(self, steps): 'project': self.name or 'vivado', 'part': self.data.get('part', 'xc7k160t-3-fbg484') } - for step in steps: - context[step] = 1 + context['steps'] = steps context['includes'] = self.data.get('includes', None) context['files'] = self.data.get('files', None) context['constraints'] = self.data.get('constraints', None) context['top'] = self.data.get('top', None) context['defines'] = self.data.get('defines', None) context['params'] = self.data.get('params', None) - if 'hooks' in self.data: - context['precfg'] = self.data['hooks'].get('precfg', None) - context['postcfg'] = self.data['hooks'].get('postcfg', None) - context['presyn'] = self.data['hooks'].get('presyn', None) - context['postsyn'] = self.data['hooks'].get('postsyn', None) - context['prepar'] = self.data['hooks'].get('prepar', None) - context['postpar'] = self.data['hooks'].get('postpar', None) - context['presbit'] = self.data['hooks'].get('prebit', None) - context['postbit'] = self.data['hooks'].get('postbit', None) + context['hooks'] = self.data.get('hooks', None) self._create_file('vivado', 'tcl', context) return 'vivado -mode batch -notrace -quiet -source vivado.tcl' From f4e9c73684a4ab3d26415337e2f3a8a8f9a566da Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Tue, 9 Jul 2024 19:51:48 -0300 Subject: [PATCH 165/248] Refactor tool classes to simplify implementation --- pyfpga/ise.py | 46 ++++++++++------------------ pyfpga/libero.py | 40 ++++++++++-------------- pyfpga/openflow.py | 43 ++++++++------------------ pyfpga/project.py | 29 ++++++++++++------ pyfpga/quartus.py | 39 +++++++---------------- pyfpga/templates/ise.jinja | 16 +++++----- pyfpga/templates/libero.jinja | 16 +++++----- pyfpga/templates/openflow-prog.jinja | 6 ++-- pyfpga/templates/openflow.jinja | 20 ++++++------ pyfpga/templates/quartus.jinja | 16 +++++----- pyfpga/templates/vivado.jinja | 16 +++++----- pyfpga/vivado.py | 38 ++++++++--------------- 12 files changed, 133 insertions(+), 192 deletions(-) diff --git a/pyfpga/ise.py b/pyfpga/ise.py index b2d102a0..03eb8707 100644 --- a/pyfpga/ise.py +++ b/pyfpga/ise.py @@ -8,10 +8,6 @@ Implements support for ISE. """ -# pylint: disable=too-many-locals -# pylint: disable=too-many-branches -# pylint: disable=duplicate-code - import re from pyfpga.project import Project @@ -20,39 +16,29 @@ class Ise(Project): """Class to support ISE projects.""" - def _make_prepare(self, steps): - info = get_info(self.data.get('part', 'xc7k160t-3-fbg484')) - context = { - 'project': self.name or 'ise', - 'family': info['family'], - 'device': info['device'], - 'speed': info['speed'], - 'package': info['package'] - } - context['steps'] = steps - context['includes'] = self.data.get('includes', None) - context['files'] = self.data.get('files', None) - context['constraints'] = self.data.get('constraints', None) - context['top'] = self.data.get('top', None) - context['defines'] = self.data.get('defines', None) - context['params'] = self.data.get('params', None) - context['hooks'] = self.data.get('hooks', None) - self._create_file('ise', 'tcl', context) - return 'xtclsh ise.tcl' + 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 _prog_prepare(self, bitstream, position): - if not bitstream: - basename = self.name or 'ise' - bitstream = f'{basename}.bit' - context = {'bitstream': bitstream, 'position': position} - self._create_file('vivado-prog', 'tcl', context) - return 'impact -batch impact-prog' + 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. diff --git a/pyfpga/libero.py b/pyfpga/libero.py index 00014dc8..2f1e4bc4 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -8,10 +8,6 @@ Implements support for Libero. """ -# pylint: disable=too-many-locals -# pylint: disable=too-many-branches -# pylint: disable=duplicate-code - import re from pyfpga.project import Project @@ -20,30 +16,28 @@ class Libero(Project): """Class to support Libero.""" - def _make_prepare(self, steps): + 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'] = None + self.conf['prog_cmd'] = None + self.conf['prog_ext'] = None + + def _make_custom(self): info = get_info(self.data.get('part', 'mpf100t-1-fcg484')) - context = { - 'project': self.name or 'libero', - 'family': info['family'], - 'device': info['device'], - 'speed': info['speed'], - 'package': info['package'] - } - context['steps'] = steps - context['includes'] = self.data.get('includes', None) - context['files'] = self.data.get('files', None) - context['constraints'] = self.data.get('constraints', None) - context['top'] = self.data.get('top', None) - context['defines'] = self.data.get('defines', None) - context['params'] = self.data.get('params', None) - context['hooks'] = self.data.get('hooks', None) - self._create_file('libero', 'tcl', context) - return 'libero SCRIPT:libero.tcl' + self.data['family'] = info['family'] + self.data['device'] = info['device'] + self.data['speed'] = info['speed'] + self.data['package'] = info['package'] - def _prog_prepare(self, bitstream, position): + def _prog_custom(self): raise NotImplementedError('Libero programming not supported') +# pylint: disable=duplicate-code + def get_info(part): """Get info about the FPGA part. diff --git a/pyfpga/openflow.py b/pyfpga/openflow.py index feb7a41e..07cefac4 100644 --- a/pyfpga/openflow.py +++ b/pyfpga/openflow.py @@ -8,43 +8,26 @@ Implements support for an Open Source development flow. """ -# pylint: disable=too-many-locals -# pylint: disable=too-many-branches -# pylint: disable=duplicate-code - from pyfpga.project import Project class Openflow(Project): """Class to support Open Source tools.""" - def _make_prepare(self, steps): - info = get_info(self.data.get('part', 'hx8k-ct256')) - context = { - 'project': self.name or 'openflow', - 'family': info['family'], - 'device': info['device'], - 'package': info['package'] - } - context['steps'] = steps - context['includes'] = self.data.get('includes', None) - context['files'] = self.data.get('files', None) - context['constraints'] = self.data.get('constraints', None) - context['top'] = self.data.get('top', None) - context['defines'] = self.data.get('defines', None) - context['params'] = self.data.get('params', None) - context['hooks'] = self.data.get('hooks', None) - self._create_file('openflow', 'sh', context) - return 'bash openflow.sh' + 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'] = 'bit' + self.conf['prog_cmd'] = f'bash {tool}-prog.sh' + self.conf['prog_ext'] = 'sh' - def _prog_prepare(self, bitstream, position): - _ = position # Not needed - if not bitstream: - basename = self.name or 'openflow' - bitstream = f'{basename}.bit' - context = {'bitstream': bitstream} - self._create_file('openflow-prog', 'sh', context) - return 'bash openflow-prog.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 get_info(part): diff --git a/pyfpga/project.py b/pyfpga/project.py index 3ad42b71..1cdceaa0 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -29,8 +29,10 @@ class Project: def __init__(self, name=None, odir='results'): """Class constructor.""" + self.conf = {} self.data = {} - self.name = name + self._configure() + self.data['project'] = name or self.conf['tool'] self.odir = odir # logging config self.logger = logging.getLogger(self.__class__.__name__) @@ -226,8 +228,10 @@ def make(self, first='cfg', last='bit'): if first == last: message = steps[first] self.logger.info('Running %s', message) - selected = keys[index[0]:index[1]+1] - self._run(self._make_prepare(selected), 'make.log') + 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 @@ -243,20 +247,27 @@ def prog(self, bitstream=None, position=1): if position not in range(1, 9): raise ValueError('Invalid position.') self.logger.info('Programming') - self._run(self._prog_prepare(bitstream, position), 'prog.log') + if not bitstream: + bitstream = f'{self.data["project"]}.{self.conf["prog_bit"]}' + self._prog_custom() + self._create_file(f'{self.conf["tool"]}-prog', self.conf['prog_ext']) + self._run(self.conf['prog_cmd'], 'prog.log') - def _make_prepare(self, steps): + def _configure(self): raise NotImplementedError('Tool-dependent') - def _prog_prepare(self, bitstream, position): - raise NotImplementedError('Tool-dependent') + def _make_custom(self): + pass + + def _prog_custom(self): + pass - def _create_file(self, basename, extension, context): + 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(context) + content = jinja_template.render(self.data) directory = Path(self.odir) directory.mkdir(parents=True, exist_ok=True) filename = f'{basename}.{extension}' diff --git a/pyfpga/quartus.py b/pyfpga/quartus.py index 1f4cb2e5..8cd8f66a 100644 --- a/pyfpga/quartus.py +++ b/pyfpga/quartus.py @@ -8,38 +8,21 @@ Implements support for Quartus. """ -# pylint: disable=too-many-locals -# pylint: disable=too-many-branches -# pylint: disable=duplicate-code - from pyfpga.project import Project class Quartus(Project): """Class to support Quartus projects.""" - def _make_prepare(self, steps): - context = { - 'project': self.name or 'quartus', - 'part': self.data.get('part', '10M50SCE144I7G') - } - context['steps'] = steps - context['includes'] = self.data.get('includes', None) - context['files'] = self.data.get('files', None) - context['constraints'] = self.data.get('constraints', None) - context['top'] = self.data.get('top', None) - context['defines'] = self.data.get('defines', None) - context['params'] = self.data.get('params', None) - context['hooks'] = self.data.get('hooks', None) - self._create_file('quartus', 'tcl', context) - return 'quartus_sh --script quartus.tcl' + 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' + self.conf['prog_cmd'] = f'bash {tool}-prog.tcl' + self.conf['prog_ext'] = 'tcl' - def _prog_prepare(self, bitstream, position): - # sof: SRAM Object File - # pof: Programming Object File - if not bitstream: - basename = self.name or 'quartus' - bitstream = f'{basename}.sof' - context = {'bitstream': bitstream, 'position': position} - self._create_file('quartus-prog', 'tcl', context) - return 'bash quartus-prog.sh' + def _make_custom(self): + if 'part' not in self.data: + self.data['part'] = '10M50SCE144I7G' diff --git a/pyfpga/templates/ise.jinja b/pyfpga/templates/ise.jinja index b44e003a..c90f2d5e 100644 --- a/pyfpga/templates/ise.jinja +++ b/pyfpga/templates/ise.jinja @@ -15,7 +15,7 @@ project set device {{ device }} project set package {{ package }} project set speed -{{ speed }} -{{ hooks.precfg | join('\n') }} +{% if hooks %}{{ hooks.precfg | join('\n') }}{% endif %} {% if files %}# Files inclusion {% for name, attr in files.items() %} @@ -49,7 +49,7 @@ project set "Verilog Macros" "{{ defines.items() | map('join', '=') | join(' | ' project set "Generics, Parameters" "{{ params.items() | map('join', '=') | join(' ') }}" -process "Synthesize - XST" {% endif %} -{{ hooks.postcfg | join('\n') }} +{% if hooks %}{{ hooks.postcfg | join('\n') }}{% endif %} project close @@ -61,7 +61,7 @@ project open {{ project }}.xise {% if 'syn' in steps %}# Synthesis -{{ hooks.presyn | join('\n') }} +{% if hooks %}{{ hooks.presyn | join('\n') }}{% endif %} # PRESYNTH #project set top_level_module_type "EDIF" @@ -69,13 +69,13 @@ project clean process run "Synthesize" if { [process get "Synthesize" status] == "errors" } { exit 1 } -{{ hooks.postsyn | join('\n') }} +{% if hooks %}{{ hooks.postsyn | join('\n') }}{% endif %} {% endif %} {% if 'par' in steps %}# Place and Route -{{ hooks.prepar | join('\n') }} +{% if hooks %}{{ hooks.prepar | join('\n') }}{% endif %} process run "Translate" if { [process get "Translate" status] == "errors" } { exit 1 } @@ -84,19 +84,19 @@ if { [process get "Map" status] == "errors" } { exit 1 } process run "Place & Route" if { [process get "Place & Route" status] == "errors" } { exit 1 } -{{ hooks.postpar | join('\n') }} +{% if hooks %}{{ hooks.postpar | join('\n') }}{% endif %} {% endif %} {% if 'bit' in steps %}# Bitstream generation -{{ hooks.prebit | join('\n') }} +{% if hooks %}{{ hooks.prebit | join('\n') }}{% endif %} process run "Generate Programming File" if { [process get "Generate Programming File" status] == "errors" } { exit 1 } catch { file rename -force {{ top }}.bit {{ project }}.bit } -{{ hooks.postbit | join('\n') }} +{% if hooks %}{{ hooks.postbit | join('\n') }}{% endif %} {% endif %} diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index 310807d5..6962f97d 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -12,7 +12,7 @@ if { [ file exists {{ project }} ] } { file delete -force -- {{ project }} } new_project -name {{ project }} -location {libero} -hdl {VERILOG} -family {SmartFusion2} set_device -family {{ family }} -die {{ device }} -package {{ package }} -speed {{ speed }} -{{ hooks.precfg | join('\n') }} +{% if hooks %}{{ hooks.precfg | join('\n') }}{% endif %} {% if includes %}# Verilog Includes (Libero) set_global_include_path_order -paths "{{ includes | join(' ') }}" @@ -75,7 +75,7 @@ configure_tool -name {SYNTHESIZE} -params {SYNPLIFY_OPTIONS: } {% endif %} -{{ hooks.postcfg | join('\n') }} +{% if hooks %}{{ hooks.postcfg | join('\n') }}{% endif %} close_project @@ -87,32 +87,32 @@ open_project {{ project }}/{{ project }}.prjx {% if 'syn' in steps %}# Synthesis -{{ hooks.presyn | join('\n') }} +{% if hooks %}{{ hooks.presyn | join('\n') }}{% endif %} run_tool -name {SYNTHESIZE} -{{ hooks.postsyn | join('\n') }} +{% if hooks %}{{ hooks.postsyn | join('\n') }}{% endif %} {% endif %} {% if 'par' in steps %}# Place and Route -{{ hooks.prepar | join('\n') }} +{% if hooks %}{{ hooks.prepar | join('\n') }}{% endif %} run_tool -name {PLACEROUTE} run_tool -name {VERIFYTIMING} -{{ hooks.postpar | join('\n') }} +{% if hooks %}{{ hooks.postpar | join('\n') }}{% endif %} {% endif %} {% if 'bit' in steps %}# Bitstream generation -{{ hooks.prebit | join('\n') }} +{% if hooks %}{{ hooks.prebit | join('\n') }}{% endif %} run_tool -name {GENERATEPROGRAMMINGFILE} -{{ hooks.postbit | join('\n') }} +{% if hooks %}{{ hooks.postbit | join('\n') }}{% endif %} {% endif %} diff --git a/pyfpga/templates/openflow-prog.jinja b/pyfpga/templates/openflow-prog.jinja index 47fad80d..7956b2b3 100644 --- a/pyfpga/templates/openflow-prog.jinja +++ b/pyfpga/templates/openflow-prog.jinja @@ -9,10 +9,8 @@ set -e DOCKER="docker run --user $(id -u):$(id -g) --rm -v $HOME:$HOME -w $PWD" -{% if family == 'ice40' %} -$DOCKER --device /dev/bus/usb hdlc/prog iceprog {{ project }}.bit -{% endif %} - {% 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 {{ project }}.svf; exit" +{% else %} +$DOCKER --device /dev/bus/usb hdlc/prog iceprog {{ project }}.bit {% endif %} diff --git a/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja index 05d01344..175a68a7 100644 --- a/pyfpga/templates/openflow.jinja +++ b/pyfpga/templates/openflow.jinja @@ -12,7 +12,7 @@ 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 " -{{ hooks.presyn | join('\n') }} +{% if hooks %}{{ hooks.presyn | join('\n') }}{% endif %} yosys -Q -m ghdl -p ' {% if includes %} @@ -40,7 +40,7 @@ chparam{% for key, value in params.items() %} -set {{ key }} {{ value }}{% endfo synth -top {{ top }} synth_{{ family }} -top {{ top }} -json {{ project }}.json ' -{{ hooks.postsyn | join('\n') }} +{% if hooks %}{{ hooks.postsyn | join('\n') }}{% endif %} " {% endif %} @@ -73,9 +73,9 @@ if [ -n "$CONSTRAINTS" ]; then CONSTRAINT="--pcf constraints.pcf" fi $DOCKER hdlc/nextpnr:ice40 /bin/bash -c " -{{ hooks.prepar | join('\n') }} +{% if hooks %}{{ hooks.prepar | join('\n') }}{% endif %} nextpnr-ice40 --{{ device }} --package {{ package }} $CONSTRAINT --json {{ project }}.json --asc {{ project }}.asc -{{ hooks.postpar | join('\n') }} +{% if hooks %}{{ hooks.postpar | join('\n') }}{% endif %} " $DOCKER hdlc/icestorm /bin/bash -c " icetime -d {{ device }} -mtr {{ project }}.rpt {{ project }}.asc @@ -88,9 +88,9 @@ if [ -n "$CONSTRAINTS" ]; then CONSTRAINT="--lpf constraints.lpf" fi $DOCKER hdlc/nextpnr:ecp5 /bin/bash -c " -{{ hooks.prepar | join('\n') }} +{% if hooks %}{{ hooks.prepar | join('\n') }}{% endif %} nextpnr-ecp5 --{{ device }} --package {{ package }} $CONSTRAINT --json {{ project }}.json --textcfg {{ project }}.config -{{ hooks.postpar | join('\n') }} +{% if hooks %}{{ hooks.postpar | join('\n') }}{% endif %} " {% endif %} @@ -100,17 +100,17 @@ nextpnr-ecp5 --{{ device }} --package {{ package }} $CONSTRAINT --json {{ projec {% if family == 'ice40' %} $DOCKER hdlc/icestorm /bin/bash -c " -{{ hooks.prebit | join('\n') }} +{% if hooks %}{{ hooks.prebit | join('\n') }}{% endif %} icepack {{ project }}.asc {{ project }}.bit -{{ hooks.postbit | join('\n') }} +{% if hooks %}{{ hooks.postbit | join('\n') }}{% endif %} " {% endif %} {% if family == 'ecp5' %} $DOCKER hdlc/prjtrellis /bin/bash -c " -{{ prebit | join('\n') }} +{% if hooks %}{{ prebit | join('\n') }}{% endif %} ecppack --svf {{ project }}.svf {{ project }}.config {{ project }}.bit -{{ postbit | join('\n') }} +{% if hooks %}{{ postbit | join('\n') }}{% endif %} " {% endif %} diff --git a/pyfpga/templates/quartus.jinja b/pyfpga/templates/quartus.jinja index 468b88ca..5013e1f8 100644 --- a/pyfpga/templates/quartus.jinja +++ b/pyfpga/templates/quartus.jinja @@ -12,7 +12,7 @@ package require ::quartus::project project_new {{ project }} -overwrite set_global_assignment -name DEVICE {{ part }} -{{ hooks.precfg | join('\n') }} +{% if hooks %}{{ hooks.precfg | join('\n') }}{% endif %} {% if files %}# Files inclusion {% for name, attr in files.items() %} @@ -58,7 +58,7 @@ set_parameter -name {{ key }} {{ value }} {% endfor %} {% endif %} -{{ hooks.postcfg | join('\n') }} +{% if hooks %}{{ hooks.postcfg | join('\n') }}{% endif %} project_close @@ -72,32 +72,32 @@ project_open -force {{ project }}.qpf {% if 'syn' in steps %}# Synthesis -{{ hooks.presyn | join('\n') }} +{% if hooks %}{{ hooks.presyn | join('\n') }}{% endif %} execute_module -tool map -{{ hooks.postsyn | join('\n') }} +{% if hooks %}{{ hooks.postsyn | join('\n') }}{% endif %} {% endif %} {% if 'par' in steps %}# Place and Route -{{ hooks.prepar | join('\n') }} +{% if hooks %}{{ hooks.prepar | join('\n') }}{% endif %} execute_module -tool fit execute_module -tool sta -{{ hooks.postpar | join('\n') }} +{% if hooks %}{{ hooks.postpar | join('\n') }}{% endif %} {% endif %} {% if 'bit' in steps %}# Bitstream generation -{{ hooks.prebit | join('\n') }} +{% if hooks %}{{ hooks.prebit | join('\n') }}{% endif %} execute_module -tool asm -{{ hooks.postbit | join('\n') }} +{% if hooks %}{{ hooks.postbit | join('\n') }}{% endif %} {% endif %} diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index 573a7fcb..babbf3b6 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -13,7 +13,7 @@ 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] -{{ hooks.precfg | join('\n') }} +{% if hooks %}{{ hooks.precfg | join('\n') }}{% endif %} {% if files %}# Files inclusion {% for name, attr in files.items() %} @@ -50,7 +50,7 @@ set_property VERILOG_DEFINE { {{ defines.items() | map('join', '=') | join(' ') set_property GENERIC { {{ params.items() | map('join', '=') | join(' ') }} } -objects [get_filesets sources_1] {% endif %} -{{ hooks.postcfg | join('\n') }} +{% if hooks %}{{ hooks.postcfg | join('\n') }}{% endif %} close_project @@ -62,7 +62,7 @@ open_project {{ project }} {% if 'syn' in steps %}# Synthesis -{{ hooks.presyn | join('\n') }} +{% if hooks %}{{ hooks.presyn | join('\n') }}{% endif %} # PRESYNTH # set_property DESIGN_MODE GateLvl [current_fileset] @@ -72,13 +72,13 @@ 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 } -{{ hooks.postsyn | join('\n') }} +{% if hooks %}{{ hooks.postsyn | join('\n') }}{% endif %} {% endif %} {% if 'par' in steps %}# Place and Route -{{ hooks.prepar | join('\n') }} +{% if hooks %}{{ hooks.prepar | join('\n') }}{% endif %} reset_run impl_1 launch_runs impl_1 @@ -86,19 +86,19 @@ 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 } -{{ hooks.postpar | join('\n') }} +{% if hooks %}{{ hooks.postpar | join('\n') }}{% endif %} {% endif %} {% if 'bit' in steps %}# Bitstream generation -{{ hooks.prebit | join('\n') }} +{% if hooks %}{{ hooks.prebit | join('\n') }}{% endif %} open_run impl_1 write_bitstream -force {{ project }} write_debug_probes -force -quiet {{ project }}.ltx -{{ hooks.postbit | join('\n') }} +{% if hooks %}{{ hooks.postbit | join('\n') }}{% endif %} {% endif %} diff --git a/pyfpga/vivado.py b/pyfpga/vivado.py index e68916f6..0391a17f 100644 --- a/pyfpga/vivado.py +++ b/pyfpga/vivado.py @@ -8,36 +8,22 @@ Implements support for Vivado. """ -# pylint: disable=too-many-locals -# pylint: disable=too-many-branches - from pyfpga.project import Project class Vivado(Project): """Class to support Vivado projects.""" - def _make_prepare(self, steps): - context = { - 'project': self.name or 'vivado', - 'part': self.data.get('part', 'xc7k160t-3-fbg484') - } - context['steps'] = steps - context['includes'] = self.data.get('includes', None) - context['files'] = self.data.get('files', None) - context['constraints'] = self.data.get('constraints', None) - context['top'] = self.data.get('top', None) - context['defines'] = self.data.get('defines', None) - context['params'] = self.data.get('params', None) - context['hooks'] = self.data.get('hooks', None) - self._create_file('vivado', 'tcl', context) - return 'vivado -mode batch -notrace -quiet -source vivado.tcl' + 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 _prog_prepare(self, bitstream, position): - _ = position # Not needed - if not bitstream: - basename = self.name or 'vivado' - bitstream = f'{basename}.bit' - context = {'BITSTREAM': bitstream} - self._create_file('vivado-prog', 'tcl', context) - return 'vivado -mode batch -notrace -quiet -source vivado-prog.tcl' + def _make_custom(self): + if 'part' not in self.data: + self.data['part'] = 'xc7k160t-3-fbg484' From 8e3de7579a5b885e1d156223d6daf435b15691c5 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Tue, 9 Jul 2024 21:30:28 -0300 Subject: [PATCH 166/248] Fix after last changes --- tests/test_data.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_data.py b/tests/test_data.py index 34f4c633..3ba4e4a9 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -1,8 +1,9 @@ from pathlib import Path -from pyfpga.project import Project +from pyfpga.vivado import Vivado pattern = { + 'project': 'EXAMPLE', 'part': 'PARTNAME', 'includes': [ Path('fakedata/dir1').resolve().as_posix(), @@ -61,7 +62,7 @@ def test_data(): - prj = Project() + prj = Vivado('EXAMPLE') prj.set_part('PARTNAME') prj.set_top('TOPNAME') prj.add_include('fakedata/dir1') From 52985d91f257ed72bf434b0127c559313efef419 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Tue, 9 Jul 2024 21:30:53 -0300 Subject: [PATCH 167/248] Fix missing check of top --- pyfpga/templates/openflow.jinja | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja index 175a68a7..2cafa31d 100644 --- a/pyfpga/templates/openflow.jinja +++ b/pyfpga/templates/openflow.jinja @@ -37,7 +37,10 @@ read_verilog -defer -sv {{ name }} chparam{% for key, value in params.items() %} -set {{ key }} {{ value }}{% endfor %} {% endif %} +{% if top%} synth -top {{ top }} +{% endif %} + synth_{{ family }} -top {{ top }} -json {{ project }}.json ' {% if hooks %}{{ hooks.postsyn | join('\n') }}{% endif %} From 6cd70eb3df590ba6b173a95c769648f28647fa45 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Tue, 9 Jul 2024 21:31:45 -0300 Subject: [PATCH 168/248] Renamed (expanded) test_vivado.py as test_tools.py --- tests/test_tools.py | 71 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_vivado.py | 28 ----------------- 2 files changed, 71 insertions(+), 28 deletions(-) create mode 100644 tests/test_tools.py delete mode 100644 tests/test_vivado.py diff --git a/tests/test_tools.py b/tests/test_tools.py new file mode 100644 index 00000000..d7924dd7 --- /dev/null +++ b/tests/test_tools.py @@ -0,0 +1,71 @@ +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 = { + 'ise': Ise, + 'libero': Libero, + 'openflow': Openflow, + 'quartus': Quartus, + 'vivado': Vivado +} + +def test_ise(): + generate('ise') + +def test_libero(): + generate('libero') + +def test_openflow(): + generate('openflow') + +def test_quartus(): + generate('quartus') + +def test_vivado(): + generate('vivado') + +def generate(tool): + prj = tools[tool](odir=f'results/{tool}') + prj.set_part('PARTNAME') + prj.set_top('TOPNAME') + prj.add_include('fakedata/dir1') + prj.add_include('fakedata/dir2') + if tool != 'ise': + prj.add_slog('fakedata/**/*.sv') + prj.add_vhdl('fakedata/**/*.vhdl', 'LIB') + prj.add_vlog('fakedata/**/*.v') + prj.add_cons('fakedata/cons/all.xdc') + prj.add_cons('fakedata/cons/syn.xdc', 'syn') + prj.add_cons('fakedata/cons/par.xdc', 'par') + 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 Exception: + pass + try: + prj.prog() + except Exception: + pass diff --git a/tests/test_vivado.py b/tests/test_vivado.py deleted file mode 100644 index 57781a09..00000000 --- a/tests/test_vivado.py +++ /dev/null @@ -1,28 +0,0 @@ -from pyfpga.vivado import Vivado - - -def test_vivado(): - prj = Vivado() - prj.set_part('PARTNAME') - prj.set_top('TOPNAME') - prj.add_include('fakedata/dir1') - prj.add_include('fakedata/dir2') - prj.add_slog('fakedata/**/*.sv') - prj.add_vhdl('fakedata/**/*.vhdl', 'LIB') - prj.add_vlog('fakedata/**/*.v') - prj.add_cons('fakedata/cons/all.xdc') - prj.add_cons('fakedata/cons/syn.xdc', 'syn') - prj.add_cons('fakedata/cons/par.xdc', 'par') - prj.add_param('PAR1', 'VAL1') - prj.add_param('PAR2', 'VAL2') - prj.add_define('DEF1', 'VAL1') - prj.add_define('DEF2', 'VAL2') - prj.add_hook('precfg', 'PRECFG_HOOK') - prj.add_hook('postcfg', 'POSTCFG_HOOK') - prj.add_hook('presyn', 'PRESYN_HOOK') - prj.add_hook('postsyn', 'POSTSYN_HOOK') - prj.add_hook('prepar', 'PREPAR_HOOK') - prj.add_hook('postpar', 'POSTPAR_HOOK') - prj.add_hook('prebit', 'PREBIT_HOOK') - prj.add_hook('postbit', 'POSTBIT_HOOK') - prj.make() From 1629745c855b44d77a68ebeee88f01af3fdb3e87 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Tue, 9 Jul 2024 21:36:12 -0300 Subject: [PATCH 169/248] Fix lint issue --- tests/test_tools.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_tools.py b/tests/test_tools.py index d7924dd7..3eb7cc0d 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -13,21 +13,27 @@ 'vivado': Vivado } + def test_ise(): generate('ise') + def test_libero(): generate('libero') + def test_openflow(): generate('openflow') + def test_quartus(): generate('quartus') + def test_vivado(): generate('vivado') + def generate(tool): prj = tools[tool](odir=f'results/{tool}') prj.set_part('PARTNAME') From 664e26b7d0eb263c02a1b06e3017e67ce4513b30 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Tue, 9 Jul 2024 22:04:56 -0300 Subject: [PATCH 170/248] Add to check if resulting files exist --- tests/test_tools.py | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/tests/test_tools.py b/tests/test_tools.py index 3eb7cc0d..56895931 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -1,3 +1,4 @@ +from pathlib import Path from pyfpga.ise import Ise from pyfpga.libero import Libero from pyfpga.openflow import Openflow @@ -15,28 +16,47 @@ def test_ise(): - generate('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(): - generate('libero') + tool = 'libero' + generate(tool, 'DEVICE-PACKAGE-SPEED') + base = f'results/{tool}/{tool}' + assert Path(f'{base}.tcl').exists(), 'file not found' def test_openflow(): - generate('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(): - generate('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(): - generate('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): +def generate(tool, part): prj = tools[tool](odir=f'results/{tool}') - prj.set_part('PARTNAME') + prj.set_part(part) prj.set_top('TOPNAME') prj.add_include('fakedata/dir1') prj.add_include('fakedata/dir2') From c6c655808823656d2569c74cec5779d93eb9fcf8 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 10 Jul 2024 20:33:08 -0300 Subject: [PATCH 171/248] Renamed parameter name as project --- pyfpga/project.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index 1cdceaa0..212fafbe 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -21,18 +21,18 @@ class Project: """Base class to manage an FPGA project. - :param name: project name (tool name when nothing specified) - :type name: str, optional + :param project: project name (tool name when nothing specified) + :type project: str, optional :param odir: output directory :type odir: str, optional """ - def __init__(self, name=None, odir='results'): + def __init__(self, project=None, odir='results'): """Class constructor.""" self.conf = {} self.data = {} self._configure() - self.data['project'] = name or self.conf['tool'] + self.data['project'] = project or self.conf['tool'] self.odir = odir # logging config self.logger = logging.getLogger(self.__class__.__name__) From 16799d8449278b5cac02e8bd010fed53abe4f80b Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 10 Jul 2024 20:41:45 -0300 Subject: [PATCH 172/248] Fix the name of a similar project --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cfc2e872..c3e12f21 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # PyFPGA [![License](https://img.shields.io/badge/License-GPL--3.0-darkgreen?style=flat-square)](LICENSE) -![Vivado](https://img.shields.io/badge/Vivado-2022.1-blue.svg?style=flat-square) -![Quartus](https://img.shields.io/badge/Quartus--Prime-23.1-blue.svg?style=flat-square) -![Libero](https://img.shields.io/badge/Libero--Soc-2024.1-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-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) + ![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) PyFPGA is an abstraction layer for working with FPGA development tools in a vendor-agnostic, programmatic way. It is a Python package that provides: @@ -72,4 +73,4 @@ pip install -e . * [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. -* [tsfpa](https://github.com/tsfpga/tsfpga): a flexible and scalable development platform for modern FPGA projects. +* [tsfpga](https://github.com/tsfpga/tsfpga): a flexible and scalable development platform for modern FPGA projects. From a2e816de348f90aecea6a49e403f897be469d12f Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 12 Jul 2024 21:49:25 -0300 Subject: [PATCH 173/248] Add a class to deal with multiple tools in the same project --- pyfpga/factory.py | 40 +++++++++++++++++++++++++++++++++++++++ tests/projects/support.py | 37 ++++++++++++------------------------ tests/test_tools.py | 17 ++--------------- 3 files changed, 54 insertions(+), 40 deletions(-) create mode 100644 pyfpga/factory.py diff --git a/pyfpga/factory.py b/pyfpga/factory.py new file mode 100644 index 00000000..197b69d3 --- /dev/null +++ b/pyfpga/factory.py @@ -0,0 +1,40 @@ +# +# Copyright (C) 2024 Rodrigo A. Melo +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +""" +A factory class to create FPGA projects. +""" + +# pylint: disable=too-few-public-methods + +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 = { + '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/tests/projects/support.py b/tests/projects/support.py index 4aa0d009..2e26e3b5 100644 --- a/tests/projects/support.py +++ b/tests/projects/support.py @@ -3,20 +3,7 @@ import argparse import sys -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 = { - 'ise': Ise, - 'libero': Libero, - 'openflow': Openflow, - 'quartus': Quartus, - 'vivado': Vivado -} +from pyfpga.factory import Factory parser = argparse.ArgumentParser() parser.add_argument( @@ -28,13 +15,13 @@ print(f'INFO: the Tool Under Test is {args.tool}') print('INFO: checking basic Verilog Support') -prj = tools[args.tool]() +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 = tools[args.tool]() +prj = Factory(args.tool) prj.add_vlog('../../examples/sources/vlog/*.v') prj.set_top('Top') prj.add_include('../../examples/sources/vlog/include1') @@ -47,7 +34,7 @@ try: print('INFO: checking Verilog Includes Support') - prj = tools[args.tool]() + prj = Factory(args.tool) prj.add_vlog('../../examples/sources/vlog/*.v') prj.set_top('Top') prj.add_define('DEFINE1', '1') @@ -63,7 +50,7 @@ try: print('INFO: checking Verilog Defines Support') - prj = tools[args.tool]() + prj = Factory(args.tool) prj.add_vlog('../../examples/sources/vlog/*.v') prj.set_top('Top') prj.add_include('../../examples/sources/vlog/include1') @@ -79,7 +66,7 @@ try: print('INFO: checking Verilog Parameters Support') - prj = tools[args.tool]() + prj = Factory(args.tool) prj.add_vlog('../../examples/sources/vlog/*.v') prj.set_top('Top') prj.add_include('../../examples/sources/vlog/include1') @@ -95,13 +82,13 @@ if args.tool not in ['ise']: print('INFO: checking basic System Verilog Support') - prj = tools[args.tool]() + 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 = tools[args.tool]() + prj = Factory(args.tool) prj.add_slog('../../examples/sources/slog/*.sv') prj.set_top('Top') prj.add_include('../../examples/sources/slog/include1') @@ -114,13 +101,13 @@ if args.tool not in ['openflow']: print('* INFO: checking basic VHDL Support') - prj = tools[args.tool]() + 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 = tools[args.tool]() + 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') @@ -130,7 +117,7 @@ try: print('INFO: checking VHDL Generics') - prj = tools[args.tool]() + 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') @@ -141,4 +128,4 @@ except Exception: pass -print(f'INFO: Tool Under Test works as expected') +print(f'INFO: {args.tool} support works as expected') diff --git a/tests/test_tools.py b/tests/test_tools.py index 56895931..7c23c4a1 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -1,18 +1,5 @@ from pathlib import Path -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 = { - 'ise': Ise, - 'libero': Libero, - 'openflow': Openflow, - 'quartus': Quartus, - 'vivado': Vivado -} +from pyfpga.factory import Factory def test_ise(): @@ -55,7 +42,7 @@ def test_vivado(): def generate(tool, part): - prj = tools[tool](odir=f'results/{tool}') + prj = Factory(tool, odir=f'results/{tool}') prj.set_part(part) prj.set_top('TOPNAME') prj.add_include('fakedata/dir1') From 0657ee2a1ffafd008e9ac2c36bdc5bf3287ae296 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 12 Jul 2024 21:51:42 -0300 Subject: [PATCH 174/248] Remove arch specification --- docs/internals.rst | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/internals.rst b/docs/internals.rst index bcaa371f..fd388ae4 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -9,26 +9,25 @@ Underlying tool steps create project config project part - precfg hook + precfg (hook) params defines includes files - arch top - postcfg hook + postcfg (hook) close project open project - presyn hook + presyn (hook) synthesis - postsyn hook - prepar hook + postsyn (hook) + prepar (hook) place_and_route - postpar hook - prebit hook + postpar (hook) + prebit (hook) bitstream - postbit hook + postbit (hook) close project Internal data structure From 727ba516eb6adf4bcfc6431b56249af1b0a296e9 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 13 Jul 2024 20:35:06 -0300 Subject: [PATCH 175/248] Make STEPS accessible for external access --- pyfpga/project.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index 212fafbe..4f4d9de0 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -18,6 +18,14 @@ 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. @@ -210,23 +218,17 @@ def make(self, first='cfg', last='bit'): self.logger.debug('Executing make') if 'part' not in self.data: self.logger.info('Using the default PART') - steps = { - 'cfg': 'Project Creation', - 'syn': 'Synthesis', - 'par': 'Place and Route', - 'bit': 'Bitstream generation' - } - if last not in steps: + if last not in STEPS: raise ValueError('Invalid last step.') - if first not in steps: + if first not in STEPS: raise ValueError('Invalid first step.') - keys = list(steps.keys()) + 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]}' + message = f'from {STEPS[first]} to {STEPS[last]}' if first == last: - message = steps[first] + message = STEPS[first] self.logger.info('Running %s', message) self.data['steps'] = keys[index[0]:index[1]+1] self._make_custom() From 3f9f8b419589280d7d805390fa0ac29ccd36d105 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 13 Jul 2024 20:35:18 -0300 Subject: [PATCH 176/248] Make TOOLS accessible for external access --- pyfpga/factory.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyfpga/factory.py b/pyfpga/factory.py index 197b69d3..80782183 100644 --- a/pyfpga/factory.py +++ b/pyfpga/factory.py @@ -17,7 +17,7 @@ from pyfpga.vivado import Vivado -tools = { +TOOLS = { 'ise': Ise, 'libero': Libero, 'openflow': Openflow, @@ -31,9 +31,9 @@ class Factory: def __init__(self, tool='vivado', project=None, odir='results'): """Class constructor.""" - if tool not in tools: + if tool not in TOOLS: raise NotImplementedError(f'{tool} is unsupported') - self._instance = tools[tool](project, odir) + self._instance = TOOLS[tool](project, odir) def __getattr__(self, name): """Delegate attribute access to the tool instance.""" From 6661d219a577f93cc3ea39656d306ba93b16238b Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 15 Jul 2024 00:01:08 -0300 Subject: [PATCH 177/248] hdl2bit and prj2bit reimplemented --- pyfpga/helpers/hdl2bit.py | 123 +++++++++++++++++++++++--------------- pyfpga/helpers/prj2bit.py | 68 +++++++++------------ 2 files changed, 103 insertions(+), 88 deletions(-) diff --git a/pyfpga/helpers/hdl2bit.py b/pyfpga/helpers/hdl2bit.py index 8b6d9173..13d36507 100644 --- a/pyfpga/helpers/hdl2bit.py +++ b/pyfpga/helpers/hdl2bit.py @@ -1,38 +1,35 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 PyFPGA Project +# Copyright (C) 2020-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # """ -A CLI helper utility to go from FPGA design files to a bitstream. +A CLI helper utility to transform HDL design files into 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 +from pathlib import Path +from pyfpga import __version__ as version +from pyfpga.factory import Factory, TOOLS +from pyfpga.project import STEPS -logging.basicConfig() -logging.getLogger('fpga.project').level = logging.INFO +tools = list(TOOLS.keys()) +steps = list(STEPS.keys()) -EPILOGUE = """ +EPILOGUE = f""" Supported values of arguments with choices: -* TOOL = {} -* TASK = {} +* 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. -""".format( - " | ".join(TOOLS), - " | ".join(TASKS[1:len(TASKS)]) -) +""" def main(): @@ -49,28 +46,22 @@ def main(): parser.add_argument( '-v', '--version', action='version', - version='v{}'.format(version) - ) - - parser.add_argument( - 'top', - metavar='TOPFILE', - help='a top-level file' + version=f'v{version}' ) parser.add_argument( '-t', '--tool', metavar='TOOL', default='vivado', - choices=TOOLS, + choices=tools, help='backend tool to be used [vivado]' ) parser.add_argument( - '-o', '--outdir', + '-o', '--odir', metavar='PATH', - default='temp', - help='where to generate files [temp]' + default='results', + help='where to generate files [results]' ) parser.add_argument( @@ -81,67 +72,105 @@ def main(): parser.add_argument( '-f', '--file', - metavar='FILE[,PACKAGE]', + metavar='FILE[,LIBRARY]', action='append', - help='add a design file (specifying an optional VHDL package)' + help='add a design file (optionally specifying a VHDL library)' ) parser.add_argument( '-i', '--include', metavar='PATH', action='append', - help='specify where to search Verilog included files' + 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=('GENERIC/PARAMETER', 'VALUE'), + metavar=('PARAMETER', 'VALUE'), action='append', nargs=2, - help='set the value of a generic/parameter of the top-level' + help='set the value of a Generic/Parameter of the top-level' ) parser.add_argument( - '--run', - metavar='TASK', - choices=TASKS[1:len(TASKS)], + '--project', + metavar='PROJECT', + help='optional PROJECT name' + ) + + parser.add_argument( + '--last', + metavar='STEP', + choices=steps, default='bit', - help='task to perform [{}]'.format('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 + # ------------------------------------------------------------------------- - prj = Project(args.tool, relative_to_script=False) - prj.set_outdir(args.outdir) + 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_vlog_include(include) + prj.add_include(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]) + 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: - prj.add_files(file[0]) + 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.add_files(args.top) - prj.set_top(args.top) + prj.set_top(args.toplevel) try: - prj.generate(args.run) - except RuntimeError: - logging.error('{} not found'.format(args.tool)) + prj.make(last=args.last) except Exception as e: sys.exit('{} ({})'.format(type(e).__name__, e)) diff --git a/pyfpga/helpers/prj2bit.py b/pyfpga/helpers/prj2bit.py index 4a0a6886..1124e452 100644 --- a/pyfpga/helpers/prj2bit.py +++ b/pyfpga/helpers/prj2bit.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 PyFPGA Project +# Copyright (C) 2020-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # @@ -10,16 +10,14 @@ """ import argparse -import logging -import os import sys -from fpga import __version__ as version -from fpga.project import Project -from fpga.tool import TASKS +from pathlib import Path +from pyfpga import __version__ as version +from pyfpga.factory import Factory, TOOLS +from pyfpga.project import STEPS -logging.basicConfig() -logging.getLogger('fpga.project').level = logging.INFO +steps = list(STEPS.keys())[1:len(STEPS)] def main(): @@ -34,29 +32,21 @@ def main(): parser.add_argument( '-v', '--version', action='version', - version='v{}'.format(version) + version=f'v{version}' ) parser.add_argument( - 'project', - metavar='PRJFILE', - help='a vendor project file' - ) - - parser.add_argument( - '--run', - metavar='TASK', - choices=TASKS[1:len(TASKS)], + '--last', + metavar='STEP', + choices=steps, default='bit', - help='task to perform [{}] ({})'.format( - 'bit', " | ".join(TASKS[1:len(TASKS)]) - ) + help=f'last step to perform [{steps[-1]}] ({"|".join(steps)})' ) parser.add_argument( - '--clean', - action='store_true', - help='clean the generated project files' + 'prjfile', + metavar='PRJFILE', + help='a vendor Project File' ) args = parser.parse_args() @@ -70,34 +60,30 @@ def main(): '.xpr': 'vivado' } - if not os.path.exists(args.project): - sys.exit('Project file not found') + prjfile = Path(args.prjfile) + + if not prjfile.exists(): + sys.exit('file not found.') - outdir = os.path.dirname(args.project) - project, extension = os.path.splitext(args.project) - project = os.path.basename(project) + directory = prjfile.parent + base_name = prjfile.stem + extension = prjfile.suffix tool = '' if extension in tool_per_ext: tool = tool_per_ext[extension] - print('{} Project file found.'.format(tool)) + print(f'* {tool} project file found.') else: - sys.exit('Unknown Project file extension') + 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) + prj = Factory(tool, base_name, directory) try: - if args.clean: - prj.clean() - else: - prj.generate(args.run, 'syn') - except RuntimeError: - logging.error('{} not found'.format(tool)) + prj.make('syn', args.last) except Exception as e: sys.exit('{} ({})'.format(type(e).__name__, e)) From 0df825dbab08398ab749c2f42f4a78e14473c48a Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 15 Jul 2024 00:01:47 -0300 Subject: [PATCH 178/248] Remove info message not compatible with prj2bit --- pyfpga/project.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index 4f4d9de0..72e7edc0 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -216,8 +216,6 @@ def make(self, first='cfg', last='bit'): .. note:: valid steps are ``cfg``, ``syn``, ``par`` and ``bit``. """ self.logger.debug('Executing make') - if 'part' not in self.data: - self.logger.info('Using the default PART') if last not in STEPS: raise ValueError('Invalid last step.') if first not in STEPS: From 2f2cdf3a54ac6cba9294e2887276ab8ce97c1021 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 15 Jul 2024 00:41:08 -0300 Subject: [PATCH 179/248] Implemented new tests for helpers Closes #38 --- examples/helpers/Makefile | 39 ---- examples/helpers/ise.sh | 18 ++ examples/helpers/ise.xise | 353 ----------------------------------- examples/helpers/libero.sh | 18 ++ examples/helpers/openflow.sh | 11 ++ examples/helpers/quartus.qpf | 3 - examples/helpers/quartus.qsf | 24 --- examples/helpers/quartus.sh | 18 ++ examples/helpers/vivado.sh | 18 ++ examples/helpers/vivado.xpr | 204 -------------------- 10 files changed, 83 insertions(+), 623 deletions(-) delete mode 100644 examples/helpers/Makefile create mode 100644 examples/helpers/ise.sh delete mode 100644 examples/helpers/ise.xise create mode 100644 examples/helpers/libero.sh create mode 100644 examples/helpers/openflow.sh delete mode 100644 examples/helpers/quartus.qpf delete mode 100644 examples/helpers/quartus.qsf create mode 100644 examples/helpers/quartus.sh create mode 100644 examples/helpers/vivado.sh delete mode 100644 examples/helpers/vivado.xpr 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..11e73ba8 --- /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/clk.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/clk.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 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no" ?> -<project xmlns="http://www.xilinx.com/XMLSchema" xmlns:xil_pn="http://www.xilinx.com/XMLSchema"> - - <version xil_pn:ise_version="14.7" xil_pn:schema_version="2"/> - - <files> - <file xil_pn:name="../../hdl/blinking.vhdl" xil_pn:type="FILE_VHDL"> - <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="1"/> - <association xil_pn:name="Implementation" xil_pn:seqID="2"/> - <library xil_pn:name="examples"/> - </file> - <file xil_pn:name="../../hdl/examples_pkg.vhdl" xil_pn:type="FILE_VHDL"> - <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="2"/> - <association xil_pn:name="Implementation" xil_pn:seqID="1"/> - <library xil_pn:name="examples"/> - </file> - <file xil_pn:name="../../hdl/top.vhdl" xil_pn:type="FILE_VHDL"> - <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="3"/> - <association xil_pn:name="Implementation" xil_pn:seqID="3"/> - </file> - <file xil_pn:name="../../examples/ise/s6micro.ucf" xil_pn:type="FILE_UCF"> - <association xil_pn:name="Implementation" xil_pn:seqID="0"/> - </file> - </files> - - <properties> - <property xil_pn:name="AES Initial Vector spartan6" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="AES Key (Hex String) spartan6" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Add I/O Buffers" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Allow Logic Optimization Across Hierarchy" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Allow SelectMAP Pins to Persist" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Allow Unexpanded Blocks" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Allow Unmatched LOC Constraints" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Allow Unmatched Timing Group Constraints" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Analysis Effort Level" xil_pn:value="Standard" xil_pn:valueState="default"/> - <property xil_pn:name="Asynchronous To Synchronous" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Auto Implementation Compile Order" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Auto Implementation Top" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Automatic BRAM Packing" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Automatically Insert glbl Module in the Netlist" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Automatically Run Generate Target PROM/ACE File" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="BRAM Utilization Ratio" xil_pn:value="100" xil_pn:valueState="default"/> - <property xil_pn:name="Bring Out Global Set/Reset Net as a Port" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Bring Out Global Tristate Net as a Port" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Bus Delimiter" xil_pn:value="<>" xil_pn:valueState="default"/> - <property xil_pn:name="Case" xil_pn:value="Maintain" xil_pn:valueState="default"/> - <property xil_pn:name="Case Implementation Style" xil_pn:value="None" xil_pn:valueState="default"/> - <property xil_pn:name="Change Device Speed To" xil_pn:value="-2" xil_pn:valueState="default"/> - <property xil_pn:name="Change Device Speed To Post Trace" xil_pn:value="-2" xil_pn:valueState="default"/> - <property xil_pn:name="Combinatorial Logic Optimization" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Compile EDK Simulation Library" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Compile SIMPRIM (Timing) Simulation Library" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Compile UNISIM (Functional) Simulation Library" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Compile XilinxCoreLib (CORE Generator) Simulation Library" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Compile for HDL Debugging" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Configuration Clk (Configuration Pins)" xil_pn:value="Pull Up" xil_pn:valueState="default"/> - <property xil_pn:name="Configuration Pin Done" xil_pn:value="Pull Up" xil_pn:valueState="default"/> - <property xil_pn:name="Configuration Pin M0" xil_pn:value="Pull Up" xil_pn:valueState="default"/> - <property xil_pn:name="Configuration Pin M1" xil_pn:value="Pull Up" xil_pn:valueState="default"/> - <property xil_pn:name="Configuration Pin M2" xil_pn:value="Pull Up" xil_pn:valueState="default"/> - <property xil_pn:name="Configuration Pin Program" xil_pn:value="Pull Up" xil_pn:valueState="default"/> - <property xil_pn:name="Configuration Rate spartan6" xil_pn:value="2" xil_pn:valueState="default"/> - <property xil_pn:name="Correlate Output to Input Design" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Create ASCII Configuration File" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Create Binary Configuration File" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Create Bit File" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Create I/O Pads from Ports" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Create IEEE 1532 Configuration File spartan6" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Create Logic Allocation File" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Create Mask File" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Create ReadBack Data Files" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Cross Clock Analysis" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="DSP Utilization Ratio" xil_pn:value="100" xil_pn:valueState="default"/> - <property xil_pn:name="Delay Values To Be Read from SDF" xil_pn:value="Setup Time" xil_pn:valueState="default"/> - <property xil_pn:name="Device" xil_pn:value="xc6slx16" xil_pn:valueState="non-default"/> - <property xil_pn:name="Device Family" xil_pn:value="Spartan6" xil_pn:valueState="non-default"/> - <property xil_pn:name="Device Speed Grade/Select ABS Minimum" xil_pn:value="-2" xil_pn:valueState="default"/> - <property xil_pn:name="Disable Detailed Package Model Insertion" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Do Not Escape Signal and Instance Names in Netlist" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Done (Output Events)" xil_pn:value="Default (4)" xil_pn:valueState="default"/> - <property xil_pn:name="Drive Awake Pin During Suspend/Wake Sequence spartan6" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Drive Done Pin High" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Enable BitStream Compression" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Enable Cyclic Redundancy Checking (CRC) spartan6" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Enable Debugging of Serial Mode BitStream" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Enable External Master Clock spartan6" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Enable Internal Done Pipe" xil_pn:value="true" xil_pn:valueState="non-default"/> - <property xil_pn:name="Enable Message Filtering" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Enable Multi-Pin Wake-Up Suspend Mode spartan6" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Enable Multi-Threading" xil_pn:value="Off" xil_pn:valueState="default"/> - <property xil_pn:name="Enable Multi-Threading par spartan6" xil_pn:value="Off" xil_pn:valueState="default"/> - <property xil_pn:name="Enable Outputs (Output Events)" xil_pn:value="Default (5)" xil_pn:valueState="default"/> - <property xil_pn:name="Enable Suspend/Wake Global Set/Reset spartan6" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Encrypt Bitstream spartan6" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Encrypt Key Select spartan6" xil_pn:value="BBRAM" xil_pn:valueState="default"/> - <property xil_pn:name="Equivalent Register Removal Map" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Equivalent Register Removal XST" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Essential Bits" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Evaluation Development Board" xil_pn:value="None Specified" xil_pn:valueState="default"/> - <property xil_pn:name="Exclude Compilation of Deprecated EDK Cores" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Exclude Compilation of EDK Sub-Libraries" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Extra Cost Tables Map" xil_pn:value="0" xil_pn:valueState="default"/> - <property xil_pn:name="Extra Effort (Highest PAR level only)" xil_pn:value="None" xil_pn:valueState="default"/> - <property xil_pn:name="FPGA Start-Up Clock" xil_pn:value="CCLK" xil_pn:valueState="default"/> - <property xil_pn:name="FSM Encoding Algorithm" xil_pn:value="Auto" xil_pn:valueState="default"/> - <property xil_pn:name="FSM Style" xil_pn:value="LUT" xil_pn:valueState="default"/> - <property xil_pn:name="Filter Files From Compile Order" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Flatten Output Netlist" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Functional Model Target Language ArchWiz" xil_pn:value="Verilog" xil_pn:valueState="default"/> - <property xil_pn:name="Functional Model Target Language Coregen" xil_pn:value="Verilog" xil_pn:valueState="default"/> - <property xil_pn:name="Functional Model Target Language Schematic" xil_pn:value="Verilog" xil_pn:valueState="default"/> - <property xil_pn:name="GTS Cycle During Suspend/Wakeup Sequence spartan6" xil_pn:value="4" xil_pn:valueState="default"/> - <property xil_pn:name="GWE Cycle During Suspend/Wakeup Sequence spartan6" xil_pn:value="5" xil_pn:valueState="default"/> - <property xil_pn:name="Generate Architecture Only (No Entity Declaration)" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Generate Asynchronous Delay Report" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Generate Clock Region Report" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Generate Constraints Interaction Report" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Generate Constraints Interaction Report Post Trace" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Generate Datasheet Section" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Generate Datasheet Section Post Trace" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Generate Detailed MAP Report" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Generate Multiple Hierarchical Netlist Files" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Generate Post-Place & Route Power Report" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Generate Post-Place & Route Simulation Model" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Generate RTL Schematic" xil_pn:value="Yes" xil_pn:valueState="default"/> - <property xil_pn:name="Generate SAIF File for Power Optimization/Estimation Par" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Generate Testbench File" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Generate Timegroups Section" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Generate Timegroups Section Post Trace" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Generics, Parameters" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Global Optimization Goal" xil_pn:value="AllClockNets" xil_pn:valueState="default"/> - <property xil_pn:name="Global Optimization map spartan6" xil_pn:value="Off" xil_pn:valueState="default"/> - <property xil_pn:name="Global Set/Reset Port Name" xil_pn:value="GSR_PORT" xil_pn:valueState="default"/> - <property xil_pn:name="Global Tristate Port Name" xil_pn:value="GTS_PORT" xil_pn:valueState="default"/> - <property xil_pn:name="Hierarchy Separator" xil_pn:value="/" xil_pn:valueState="default"/> - <property xil_pn:name="ISim UUT Instance Name" xil_pn:value="UUT" xil_pn:valueState="default"/> - <property xil_pn:name="Ignore User Timing Constraints Map" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Ignore User Timing Constraints Par" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Implementation Top" xil_pn:value="Architecture|Top|Structural" xil_pn:valueState="non-default"/> - <property xil_pn:name="Implementation Top File" xil_pn:value="../../hdl/top.vhdl" xil_pn:valueState="non-default"/> - <property xil_pn:name="Implementation Top Instance Path" xil_pn:value="/Top" xil_pn:valueState="non-default"/> - <property xil_pn:name="Include 'uselib Directive in Verilog File" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Include SIMPRIM Models in Verilog File" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Include UNISIM Models in Verilog File" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Include sdf_annotate task in Verilog File" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Incremental Compilation" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Insert Buffers to Prevent Pulse Swallowing" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Instantiation Template Target Language Xps" xil_pn:value="Verilog" xil_pn:valueState="default"/> - <property xil_pn:name="JTAG Pin TCK" xil_pn:value="Pull Up" xil_pn:valueState="default"/> - <property xil_pn:name="JTAG Pin TDI" xil_pn:value="Pull Up" xil_pn:valueState="default"/> - <property xil_pn:name="JTAG Pin TDO" xil_pn:value="Pull Up" xil_pn:valueState="default"/> - <property xil_pn:name="JTAG Pin TMS" xil_pn:value="Pull Up" xil_pn:valueState="default"/> - <property xil_pn:name="Keep Hierarchy" xil_pn:value="No" xil_pn:valueState="default"/> - <property xil_pn:name="LUT Combining Map" xil_pn:value="Off" xil_pn:valueState="default"/> - <property xil_pn:name="LUT Combining Xst" xil_pn:value="Auto" xil_pn:valueState="default"/> - <property xil_pn:name="Language" xil_pn:value="VHDL" xil_pn:valueState="default"/> - <property xil_pn:name="Last Applied Goal" xil_pn:value="Balanced" xil_pn:valueState="default"/> - <property xil_pn:name="Last Applied Strategy" xil_pn:value="Xilinx Default (unlocked)" xil_pn:valueState="default"/> - <property xil_pn:name="Last Unlock Status" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Launch SDK after Export" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Library for Verilog Sources" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Load glbl" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Manual Implementation Compile Order" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Map Slice Logic into Unused Block RAMs" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Mask Pins for Multi-Pin Wake-Up Suspend Mode spartan6" xil_pn:value="0x00" xil_pn:valueState="default"/> - <property xil_pn:name="Max Fanout" xil_pn:value="100000" xil_pn:valueState="default"/> - <property xil_pn:name="Maximum Compression" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Maximum Number of Lines in Report" xil_pn:value="1000" xil_pn:valueState="default"/> - <property xil_pn:name="Maximum Signal Name Length" xil_pn:value="20" xil_pn:valueState="default"/> - <property xil_pn:name="Move First Flip-Flop Stage" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Move Last Flip-Flop Stage" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="MultiBoot: Insert IPROG CMD in the Bitfile spartan6" xil_pn:value="Enable" xil_pn:valueState="default"/> - <property xil_pn:name="MultiBoot: Next Configuration Mode spartan6" xil_pn:value="001" xil_pn:valueState="default"/> - <property xil_pn:name="MultiBoot: Starting Address for Golden Configuration spartan6" xil_pn:value="0x00000000" xil_pn:valueState="default"/> - <property xil_pn:name="MultiBoot: Starting Address for Next Configuration spartan6" xil_pn:value="0x00000000" xil_pn:valueState="default"/> - <property xil_pn:name="MultiBoot: Use New Mode for Next Configuration spartan6" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="MultiBoot: User-Defined Register for Failsafe Scheme spartan6" xil_pn:value="0x0000" xil_pn:valueState="default"/> - <property xil_pn:name="Netlist Hierarchy" xil_pn:value="As Optimized" xil_pn:valueState="default"/> - <property xil_pn:name="Netlist Translation Type" xil_pn:value="Timestamp" xil_pn:valueState="default"/> - <property xil_pn:name="Number of Clock Buffers" xil_pn:value="16" xil_pn:valueState="default"/> - <property xil_pn:name="Number of Paths in Error/Verbose Report" xil_pn:value="3" xil_pn:valueState="default"/> - <property xil_pn:name="Number of Paths in Error/Verbose Report Post Trace" xil_pn:value="3" xil_pn:valueState="default"/> - <property xil_pn:name="Optimization Effort spartan6" xil_pn:value="Normal" xil_pn:valueState="default"/> - <property xil_pn:name="Optimization Goal" xil_pn:value="Speed" xil_pn:valueState="default"/> - <property xil_pn:name="Optimize Instantiated Primitives" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Other Bitgen Command Line Options" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Other Bitgen Command Line Options spartan6" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Other Compiler Options" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Other Compiler Options Map" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Other Compiler Options Par" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Other Compiler Options Translate" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Other Compxlib Command Line Options" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Other Map Command Line Options" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Other NETGEN Command Line Options" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Other Ngdbuild Command Line Options" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Other Place & Route Command Line Options" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Other Simulator Commands Behavioral" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Other Simulator Commands Post-Map" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Other Simulator Commands Post-Route" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Other Simulator Commands Post-Translate" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Other XPWR Command Line Options" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Other XST Command Line Options" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Output Extended Identifiers" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Output File Name" xil_pn:value="Top" xil_pn:valueState="default"/> - <property xil_pn:name="Overwrite Compiled Libraries" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Pack I/O Registers into IOBs" xil_pn:value="Auto" xil_pn:valueState="default"/> - <property xil_pn:name="Pack I/O Registers/Latches into IOBs" xil_pn:value="Off" xil_pn:valueState="default"/> - <property xil_pn:name="Package" xil_pn:value="csg324" xil_pn:valueState="default"/> - <property xil_pn:name="Perform Advanced Analysis" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Perform Advanced Analysis Post Trace" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Perform Timing-Driven Packing and Placement" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Place & Route Effort Level (Overall)" xil_pn:value="High" xil_pn:valueState="default"/> - <property xil_pn:name="Place And Route Mode" xil_pn:value="Normal Place and Route" xil_pn:valueState="default"/> - <property xil_pn:name="Place MultiBoot Settings into Bitstream spartan6" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Placer Effort Level Map" xil_pn:value="High" xil_pn:valueState="default"/> - <property xil_pn:name="Placer Extra Effort Map" xil_pn:value="None" xil_pn:valueState="default"/> - <property xil_pn:name="Port to be used" xil_pn:value="Auto - default" xil_pn:valueState="default"/> - <property xil_pn:name="Post Map Simulation Model Name" xil_pn:value="Top_map.v" xil_pn:valueState="default"/> - <property xil_pn:name="Post Place & Route Simulation Model Name" xil_pn:value="Top_timesim.v" xil_pn:valueState="default"/> - <property xil_pn:name="Post Synthesis Simulation Model Name" xil_pn:value="Top_synthesis.v" xil_pn:valueState="default"/> - <property xil_pn:name="Post Translate Simulation Model Name" xil_pn:value="Top_translate.v" xil_pn:valueState="default"/> - <property xil_pn:name="Power Reduction Map spartan6" xil_pn:value="Off" xil_pn:valueState="default"/> - <property xil_pn:name="Power Reduction Par" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Power Reduction Xst" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Preferred Language" xil_pn:value="Verilog" xil_pn:valueState="default"/> - <property xil_pn:name="Produce Verbose Report" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Project Description" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Property Specification in Project File" xil_pn:value="Store all values" xil_pn:valueState="default"/> - <property xil_pn:name="RAM Extraction" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="RAM Style" xil_pn:value="Auto" xil_pn:valueState="default"/> - <property xil_pn:name="ROM Extraction" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="ROM Style" xil_pn:value="Auto" xil_pn:valueState="default"/> - <property xil_pn:name="Read Cores" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Reduce Control Sets" xil_pn:value="Auto" xil_pn:valueState="default"/> - <property xil_pn:name="Regenerate Core" xil_pn:value="Under Current Project Setting" xil_pn:valueState="default"/> - <property xil_pn:name="Register Balancing" xil_pn:value="No" xil_pn:valueState="default"/> - <property xil_pn:name="Register Duplication Map" xil_pn:value="Off" xil_pn:valueState="default"/> - <property xil_pn:name="Register Duplication Xst" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Register Ordering spartan6" xil_pn:value="4" xil_pn:valueState="default"/> - <property xil_pn:name="Release Write Enable (Output Events)" xil_pn:value="Default (6)" xil_pn:valueState="default"/> - <property xil_pn:name="Rename Design Instance in Testbench File to" xil_pn:value="UUT" xil_pn:valueState="default"/> - <property xil_pn:name="Rename Top Level Architecture To" xil_pn:value="Structure" xil_pn:valueState="default"/> - <property xil_pn:name="Rename Top Level Entity to" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Rename Top Level Module To" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Report Fastest Path(s) in Each Constraint" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Report Fastest Path(s) in Each Constraint Post Trace" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Report Paths by Endpoint" xil_pn:value="3" xil_pn:valueState="default"/> - <property xil_pn:name="Report Paths by Endpoint Post Trace" xil_pn:value="3" xil_pn:valueState="default"/> - <property xil_pn:name="Report Type" xil_pn:value="Verbose Report" xil_pn:valueState="default"/> - <property xil_pn:name="Report Type Post Trace" xil_pn:value="Verbose Report" xil_pn:valueState="default"/> - <property xil_pn:name="Report Unconstrained Paths" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Report Unconstrained Paths Post Trace" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Reset On Configuration Pulse Width" xil_pn:value="100" xil_pn:valueState="default"/> - <property xil_pn:name="Resource Sharing" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Retain Hierarchy" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Retry Configuration if CRC Error Occurs spartan6" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Revision Select" xil_pn:value="00" xil_pn:valueState="default"/> - <property xil_pn:name="Revision Select Tristate" xil_pn:value="Disable" xil_pn:valueState="default"/> - <property xil_pn:name="Run Design Rules Checker (DRC)" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Run for Specified Time" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Run for Specified Time Map" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Run for Specified Time Par" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Run for Specified Time Translate" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Safe Implementation" xil_pn:value="No" xil_pn:valueState="default"/> - <property xil_pn:name="Security" xil_pn:value="Enable Readback and Reconfiguration" xil_pn:valueState="default"/> - <property xil_pn:name="Selected Simulation Root Source Node Behavioral" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Selected Simulation Root Source Node Post-Map" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Selected Simulation Root Source Node Post-Route" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Selected Simulation Root Source Node Post-Translate" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Selected Simulation Source Node" xil_pn:value="UUT" xil_pn:valueState="default"/> - <property xil_pn:name="Set SPI Configuration Bus Width spartan6" xil_pn:value="1" xil_pn:valueState="default"/> - <property xil_pn:name="Setup External Master Clock Division spartan6" xil_pn:value="1" xil_pn:valueState="default"/> - <property xil_pn:name="Shift Register Extraction" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Shift Register Minimum Size spartan6" xil_pn:value="2" xil_pn:valueState="default"/> - <property xil_pn:name="Show All Models" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Simulation Model Target" xil_pn:value="Verilog" xil_pn:valueState="default"/> - <property xil_pn:name="Simulation Run Time ISim" xil_pn:value="1000 ns" xil_pn:valueState="default"/> - <property xil_pn:name="Simulation Run Time Map" xil_pn:value="1000 ns" xil_pn:valueState="default"/> - <property xil_pn:name="Simulation Run Time Par" xil_pn:value="1000 ns" xil_pn:valueState="default"/> - <property xil_pn:name="Simulation Run Time Translate" xil_pn:value="1000 ns" xil_pn:valueState="default"/> - <property xil_pn:name="Simulator" xil_pn:value="ISim (VHDL/Verilog)" xil_pn:valueState="default"/> - <property xil_pn:name="Slice Utilization Ratio" xil_pn:value="100" xil_pn:valueState="default"/> - <property xil_pn:name="Specify 'define Macro Name and Value" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Specify Top Level Instance Names Behavioral" xil_pn:value="Default" xil_pn:valueState="default"/> - <property xil_pn:name="Specify Top Level Instance Names Post-Map" xil_pn:value="Default" xil_pn:valueState="default"/> - <property xil_pn:name="Specify Top Level Instance Names Post-Route" xil_pn:value="Default" xil_pn:valueState="default"/> - <property xil_pn:name="Specify Top Level Instance Names Post-Translate" xil_pn:value="Default" xil_pn:valueState="default"/> - <property xil_pn:name="Speed Grade" xil_pn:value="-2" xil_pn:valueState="non-default"/> - <property xil_pn:name="Starting Placer Cost Table (1-100) Map spartan6" xil_pn:value="1" xil_pn:valueState="default"/> - <property xil_pn:name="Synthesis Tool" xil_pn:value="XST (VHDL/Verilog)" xil_pn:valueState="default"/> - <property xil_pn:name="Target Simulator" xil_pn:value="Please Specify" xil_pn:valueState="default"/> - <property xil_pn:name="Timing Mode Map" xil_pn:value="Performance Evaluation" xil_pn:valueState="default"/> - <property xil_pn:name="Timing Mode Par" xil_pn:value="Performance Evaluation" xil_pn:valueState="default"/> - <property xil_pn:name="Top-Level Module Name in Output Netlist" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Top-Level Source Type" xil_pn:value="HDL" xil_pn:valueState="default"/> - <property xil_pn:name="Trim Unconnected Signals" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Tristate On Configuration Pulse Width" xil_pn:value="0" xil_pn:valueState="default"/> - <property xil_pn:name="Unused IOB Pins" xil_pn:value="Pull Down" xil_pn:valueState="default"/> - <property xil_pn:name="Use 64-bit PlanAhead on 64-bit Systems" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Use Clock Enable" xil_pn:value="Auto" xil_pn:valueState="default"/> - <property xil_pn:name="Use Custom Project File Behavioral" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Use Custom Project File Post-Map" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Use Custom Project File Post-Route" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Use Custom Project File Post-Translate" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Use Custom Simulation Command File Behavioral" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Use Custom Simulation Command File Map" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Use Custom Simulation Command File Par" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Use Custom Simulation Command File Translate" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Use Custom Waveform Configuration File Behav" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Use Custom Waveform Configuration File Map" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Use Custom Waveform Configuration File Par" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Use Custom Waveform Configuration File Translate" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Use DSP Block spartan6" xil_pn:value="Auto" xil_pn:valueState="default"/> - <property xil_pn:name="Use LOC Constraints" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="Use RLOC Constraints" xil_pn:value="Yes" xil_pn:valueState="default"/> - <property xil_pn:name="Use Smart Guide" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Use Synchronous Reset" xil_pn:value="Auto" xil_pn:valueState="default"/> - <property xil_pn:name="Use Synchronous Set" xil_pn:value="Auto" xil_pn:valueState="default"/> - <property xil_pn:name="Use Synthesis Constraints File" xil_pn:value="true" xil_pn:valueState="default"/> - <property xil_pn:name="User Browsed Strategy Files" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="UserID Code (8 Digit Hexadecimal)" xil_pn:value="0xFFFFFFFF" xil_pn:valueState="default"/> - <property xil_pn:name="VCCAUX Voltage Level spartan6" xil_pn:value="2.5V" xil_pn:valueState="default"/> - <property xil_pn:name="VHDL Source Analysis Standard" xil_pn:value="VHDL-93" xil_pn:valueState="default"/> - <property xil_pn:name="Value Range Check" xil_pn:value="false" xil_pn:valueState="default"/> - <property xil_pn:name="Verilog Macros" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="Wait for DCM and PLL Lock (Output Events) spartan6" xil_pn:value="Default (NoWait)" xil_pn:valueState="default"/> - <property xil_pn:name="Wakeup Clock spartan6" xil_pn:value="Startup Clock" xil_pn:valueState="default"/> - <property xil_pn:name="Watchdog Timer Value spartan6" xil_pn:value="0xFFFF" xil_pn:valueState="default"/> - <property xil_pn:name="Working Directory" xil_pn:value="." xil_pn:valueState="non-default"/> - <property xil_pn:name="Write Timing Constraints" xil_pn:value="false" xil_pn:valueState="default"/> - <!-- --> - <!-- The following properties are for internal use only. These should not be modified.--> - <!-- --> - <property xil_pn:name="PROP_BehavioralSimTop" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="PROP_DesignName" xil_pn:value="ise" xil_pn:valueState="non-default"/> - <property xil_pn:name="PROP_DevFamilyPMName" xil_pn:value="spartan6" xil_pn:valueState="default"/> - <property xil_pn:name="PROP_FPGAConfiguration" xil_pn:value="FPGAConfiguration" xil_pn:valueState="default"/> - <property xil_pn:name="PROP_PostMapSimTop" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="PROP_PostParSimTop" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="PROP_PostSynthSimTop" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="PROP_PostXlateSimTop" xil_pn:value="" xil_pn:valueState="default"/> - <property xil_pn:name="PROP_PreSynthesis" xil_pn:value="PreSynthesis" xil_pn:valueState="default"/> - <property xil_pn:name="PROP_intProjectCreationTimestamp" xil_pn:value="2020-03-16T16:18:39" xil_pn:valueState="non-default"/> - <property xil_pn:name="PROP_intWbtProjectID" xil_pn:value="B648D1B8F4BEACF2D00ECCB3CF3FDEAA" xil_pn:valueState="non-default"/> - <property xil_pn:name="PROP_intWorkingDirLocWRTProjDir" xil_pn:value="Same" xil_pn:valueState="non-default"/> - <property xil_pn:name="PROP_intWorkingDirUsed" xil_pn:value="No" xil_pn:valueState="non-default"/> - </properties> - - <libraries> - <library xil_pn:name="examples"/> - </libraries> - -</project> diff --git a/examples/helpers/libero.sh b/examples/helpers/libero.sh new file mode 100644 index 00000000..1a56da93 --- /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/clk.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/clk.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..b2ae4437 --- /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/clk.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/clk.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 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Product Version: Vivado v2019.2 (64-bit) --> -<!-- --> -<!-- Copyright 1986-2019 Xilinx, Inc. All Rights Reserved. --> - -<Project Version="7" Minor="44" Path="./vivado.xpr"> - <DefaultLaunch Dir="$PRUNDIR"/> - <Configuration> - <Option Name="Id" Val="22631844048a4353bd9203eebcbe9e6d"/> - <Option Name="Part" Val="xc7z010clg400-1"/> - <Option Name="CompiledLibDir" Val="$PCACHEDIR/compile_simlib"/> - <Option Name="CompiledLibDirXSim" Val=""/> - <Option Name="CompiledLibDirModelSim" Val="$PCACHEDIR/compile_simlib/modelsim"/> - <Option Name="CompiledLibDirQuesta" Val="$PCACHEDIR/compile_simlib/questa"/> - <Option Name="CompiledLibDirIES" Val="$PCACHEDIR/compile_simlib/ies"/> - <Option Name="CompiledLibDirXcelium" Val="$PCACHEDIR/compile_simlib/xcelium"/> - <Option Name="CompiledLibDirVCS" Val="$PCACHEDIR/compile_simlib/vcs"/> - <Option Name="CompiledLibDirRiviera" Val="$PCACHEDIR/compile_simlib/riviera"/> - <Option Name="CompiledLibDirActivehdl" Val="$PCACHEDIR/compile_simlib/activehdl"/> - <Option Name="BoardPart" Val=""/> - <Option Name="ActiveSimSet" Val="sim_1"/> - <Option Name="DefaultLib" Val="xil_defaultlib"/> - <Option Name="ProjectType" Val="Default"/> - <Option Name="IPOutputRepo" Val="$PCACHEDIR/ip"/> - <Option Name="IPCachePermission" Val="read"/> - <Option Name="IPCachePermission" Val="write"/> - <Option Name="EnableCoreContainer" Val="FALSE"/> - <Option Name="CreateRefXciForCoreContainers" Val="FALSE"/> - <Option Name="IPUserFilesDir" Val="$PIPUSERFILESDIR"/> - <Option Name="IPStaticSourceDir" Val="$PIPUSERFILESDIR/ipstatic"/> - <Option Name="EnableBDX" Val="FALSE"/> - <Option Name="WTXSimLaunchSim" Val="0"/> - <Option Name="WTModelSimLaunchSim" Val="0"/> - <Option Name="WTQuestaLaunchSim" Val="0"/> - <Option Name="WTIesLaunchSim" Val="0"/> - <Option Name="WTVcsLaunchSim" Val="0"/> - <Option Name="WTRivieraLaunchSim" Val="0"/> - <Option Name="WTActivehdlLaunchSim" Val="0"/> - <Option Name="WTXSimExportSim" Val="0"/> - <Option Name="WTModelSimExportSim" Val="0"/> - <Option Name="WTQuestaExportSim" Val="0"/> - <Option Name="WTIesExportSim" Val="0"/> - <Option Name="WTVcsExportSim" Val="0"/> - <Option Name="WTRivieraExportSim" Val="0"/> - <Option Name="WTActivehdlExportSim" Val="0"/> - <Option Name="GenerateIPUpgradeLog" Val="TRUE"/> - <Option Name="XSimRadix" Val="hex"/> - <Option Name="XSimTimeUnit" Val="ns"/> - <Option Name="XSimArrayDisplayLimit" Val="1024"/> - <Option Name="XSimTraceLimit" Val="65536"/> - <Option Name="SimTypes" Val="rtl"/> - <Option Name="SimTypes" Val="bfm"/> - <Option Name="SimTypes" Val="tlm"/> - <Option Name="SimTypes" Val="tlm_dpi"/> - <Option Name="MEMEnableMemoryMapGeneration" Val="TRUE"/> - <Option Name="DcpsUptoDate" Val="TRUE"/> - </Configuration> - <FileSets Version="1" Minor="31"> - <FileSet Name="sources_1" Type="DesignSrcs" RelSrcDir="$PSRCDIR/sources_1"> - <Filter Type="Srcs"/> - <File Path="$PPRDIR/../../hdl/blinking.vhdl"> - <FileInfo> - <Attr Name="Library" Val="examples"/> - <Attr Name="UsedIn" Val="synthesis"/> - <Attr Name="UsedIn" Val="simulation"/> - </FileInfo> - </File> - <File Path="$PPRDIR/../../hdl/examples_pkg.vhdl"> - <FileInfo> - <Attr Name="Library" Val="examples"/> - <Attr Name="UsedIn" Val="synthesis"/> - <Attr Name="UsedIn" Val="simulation"/> - </FileInfo> - </File> - <File Path="$PPRDIR/../../hdl/top.vhdl"> - <FileInfo> - <Attr Name="UsedIn" Val="synthesis"/> - <Attr Name="UsedIn" Val="simulation"/> - </FileInfo> - </File> - <Config> - <Option Name="DesignMode" Val="RTL"/> - <Option Name="TopModule" Val="Top"/> - <Option Name="TopAutoSet" Val="TRUE"/> - </Config> - </FileSet> - <FileSet Name="constrs_1" Type="Constrs" RelSrcDir="$PSRCDIR/constrs_1"> - <Filter Type="Constrs"/> - <File Path="$PPRDIR/../../examples/vivado/zybo.xdc"> - <FileInfo> - <Attr Name="UsedIn" Val="synthesis"/> - <Attr Name="UsedIn" Val="implementation"/> - </FileInfo> - </File> - <Config> - <Option Name="ConstrsType" Val="XDC"/> - </Config> - </FileSet> - <FileSet Name="sim_1" Type="SimulationSrcs" RelSrcDir="$PSRCDIR/sim_1"> - <Config> - <Option Name="DesignMode" Val="RTL"/> - <Option Name="TopModule" Val="Top"/> - <Option Name="TopLib" Val="xil_defaultlib"/> - <Option Name="TopAutoSet" Val="TRUE"/> - <Option Name="TransportPathDelay" Val="0"/> - <Option Name="TransportIntDelay" Val="0"/> - <Option Name="SelectedSimModel" Val="rtl"/> - <Option Name="SrcSet" Val="sources_1"/> - </Config> - </FileSet> - <FileSet Name="utils_1" Type="Utils" RelSrcDir="$PSRCDIR/utils_1"> - <Filter Type="Utils"/> - <Config> - <Option Name="TopAutoSet" Val="TRUE"/> - </Config> - </FileSet> - </FileSets> - <Simulators> - <Simulator Name="XSim"> - <Option Name="Description" Val="Vivado Simulator"/> - <Option Name="CompiledLib" Val="0"/> - </Simulator> - <Simulator Name="ModelSim"> - <Option Name="Description" Val="ModelSim Simulator"/> - </Simulator> - <Simulator Name="Questa"> - <Option Name="Description" Val="Questa Advanced Simulator"/> - </Simulator> - <Simulator Name="IES"> - <Option Name="Description" Val="Incisive Enterprise Simulator (IES)"/> - </Simulator> - <Simulator Name="Xcelium"> - <Option Name="Description" Val="Xcelium Parallel Simulator"/> - </Simulator> - <Simulator Name="VCS"> - <Option Name="Description" Val="Verilog Compiler Simulator (VCS)"/> - </Simulator> - <Simulator Name="Riviera"> - <Option Name="Description" Val="Riviera-PRO Simulator"/> - </Simulator> - </Simulators> - <Runs Version="1" Minor="11"> - <Run Id="synth_1" Type="Ft3:Synth" SrcSet="sources_1" Part="xc7z010clg400-1" ConstrsSet="constrs_1" Description="Vivado Synthesis Defaults" AutoIncrementalCheckpoint="false" WriteIncrSynthDcp="false" State="current" IncludeInArchive="true"> - <Strategy Version="1" Minor="2"> - <StratHandle Name="Vivado Synthesis Defaults" Flow="Vivado Synthesis 2019"> - <Desc>Vivado Synthesis Defaults</Desc> - </StratHandle> - <Step Id="synth_design"/> - </Strategy> - <ReportStrategy Name="Vivado Synthesis Default Reports" Flow="Vivado Synthesis 2019"/> - <Report Name="ROUTE_DESIGN.REPORT_METHODOLOGY" Enabled="1"/> - <RQSFiles/> - </Run> - <Run Id="impl_1" Type="Ft2:EntireDesign" Part="xc7z010clg400-1" ConstrsSet="constrs_1" Description="Default settings for Implementation." AutoIncrementalCheckpoint="false" WriteIncrSynthDcp="false" State="current" SynthRun="synth_1" IncludeInArchive="true" GenFullBitstream="true"> - <Strategy Version="1" Minor="2"> - <StratHandle Name="Vivado Implementation Defaults" Flow="Vivado Implementation 2019"> - <Desc>Default settings for Implementation.</Desc> - </StratHandle> - <Step Id="init_design"/> - <Step Id="opt_design"/> - <Step Id="power_opt_design"/> - <Step Id="place_design"/> - <Step Id="post_place_power_opt_design"/> - <Step Id="phys_opt_design" EnableStepBool="1"/> - <Step Id="route_design"/> - <Step Id="post_route_phys_opt_design"/> - <Step Id="write_bitstream"/> - </Strategy> - <ReportStrategy Name="Vivado Implementation Default Reports" Flow="Vivado Implementation 2019"/> - <Report Name="ROUTE_DESIGN.REPORT_METHODOLOGY" Enabled="1"/> - <RQSFiles/> - </Run> - </Runs> - <Board/> - <DashboardSummary Version="1" Minor="0"> - <Dashboards> - <Dashboard Name="default_dashboard"> - <Gadgets> - <Gadget Name="drc_1" Type="drc" Version="1" Row="2" Column="0"> - <GadgetParam Name="REPORTS" Type="string_list" Value="impl_1#impl_1_route_report_drc_0 "/> - </Gadget> - <Gadget Name="methodology_1" Type="methodology" Version="1" Row="2" Column="1"> - <GadgetParam Name="REPORTS" Type="string_list" Value="impl_1#impl_1_route_report_methodology_0 "/> - </Gadget> - <Gadget Name="power_1" Type="power" Version="1" Row="1" Column="0"> - <GadgetParam Name="REPORTS" Type="string_list" Value="impl_1#impl_1_route_report_power_0 "/> - </Gadget> - <Gadget Name="timing_1" Type="timing" Version="1" Row="0" Column="1"> - <GadgetParam Name="REPORTS" Type="string_list" Value="impl_1#impl_1_route_report_timing_summary_0 "/> - </Gadget> - <Gadget Name="utilization_1" Type="utilization" Version="1" Row="0" Column="0"> - <GadgetParam Name="REPORTS" Type="string_list" Value="synth_1#synth_1_synth_report_utilization_0 "/> - <GadgetParam Name="RUN.STEP" Type="string" Value="synth_design"/> - <GadgetParam Name="RUN.TYPE" Type="string" Value="synthesis"/> - </Gadget> - <Gadget Name="utilization_2" Type="utilization" Version="1" Row="1" Column="1"> - <GadgetParam Name="REPORTS" Type="string_list" Value="impl_1#impl_1_place_report_utilization_0 "/> - </Gadget> - </Gadgets> - </Dashboard> - <CurrentDashboard>default_dashboard</CurrentDashboard> - </Dashboards> - </DashboardSummary> -</Project> From 79bb0b4d81c57760615f24332735275192423f89 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 15 Jul 2024 01:10:09 -0300 Subject: [PATCH 180/248] Add workaround for prj2bit --- pyfpga/templates/libero.jinja | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index 6962f97d..aaddbf9b 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -83,7 +83,9 @@ close_project {% if 'syn' in steps or 'par' in steps or 'bit' in steps %}# Design flow ----------------------------------------------------------------- -open_project {{ project }}/{{ project }}.prjx +if { [catch {open_project {{ project }}/{{ project }}.prjx} ] } { + open_project {{ project }}.prjx +} {% if 'syn' in steps %}# Synthesis From d9b02da7709e5819bf843be5c4bd84a5d0b2cced Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 15 Jul 2024 01:49:45 -0300 Subject: [PATCH 181/248] docs: added helpers --- docs/Makefile | 6 +++++- docs/helpers.rst | 12 ++++++++++++ docs/index.rst | 1 + docs/tools.rst | 13 ++++--------- 4 files changed, 22 insertions(+), 10 deletions(-) create mode 100644 docs/helpers.rst diff --git a/docs/Makefile b/docs/Makefile index d4bb2cbb..48576179 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -14,7 +14,11 @@ help: .PHONY: help Makefile +$(BUILDDIR)/hdl2bit $(BUILDDIR)/prj2bit: + @mkdir -p $(@D) + @python3 ../pyfpga/helpers/$(@F).py -h > $@ + # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile +%: Makefile $(BUILDDIR)/hdl2bit $(BUILDDIR)/prj2bit @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/helpers.rst b/docs/helpers.rst new file mode 100644 index 00000000..e5623c92 --- /dev/null +++ b/docs/helpers.rst @@ -0,0 +1,12 @@ +Helpers +======= + +hdl2bit +------- + +.. literalinclude:: _build/hdl2bit + +prj2bit +------- + +.. literalinclude:: _build/prj2bit diff --git a/docs/index.rst b/docs/index.rst index 88c75aa4..ad539fe4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -17,5 +17,6 @@ PyFPGA's documentation basic advanced api + helpers tools internals diff --git a/docs/tools.rst b/docs/tools.rst index 0bbbcc5a..a7c6dc55 100644 --- a/docs/tools.rst +++ b/docs/tools.rst @@ -1,25 +1,20 @@ Tools support ============= -.. ATTENTION:: - - (2024-05-31) To be updated. - +---------------+-----------+---------+-----+-----------------------------------------------+ | 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) | +| Libero-SoC | Microsemi | 2024.1 | 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) | +| Openflow | | | | | +---------------+-----------+---------+-----+-----------------------------------------------+ -| Vivado | Xilinx | 2019.1 | 8.5 | Introduced in 2012, it superseded ISE | +| Quartus Prime | Intel | 23.1 | 8.6 | Known as Quartus II until version 15.0 (2015) | +---------------+-----------+---------+-----+-----------------------------------------------+ -| Yosys | | 0.9-dev | 8.6 | The open-source synthesizer | +| Vivado | Xilinx | 2022.1 | 8.5 | Introduced in 2012, it superseded ISE | +---------------+-----------+---------+-----+-----------------------------------------------+ - * 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. From d713c90793c9b71fdb1b5d9c7a212813adc11557 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 15 Jul 2024 01:56:50 -0300 Subject: [PATCH 182/248] ci: add pyfpga as dependency --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 26c51962..0f17d958 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -12,7 +12,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Install dependencies - run: pip install sphinx sphinx-rtd-theme + run: pip install . && pip install sphinx sphinx-rtd-theme - name: Build documentation run: make docs - name: Deploy to GitHub Pages From 42fe56d3ea1a223202aa3de0c445921130543dbd Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 17 Jul 2024 20:00:23 -0300 Subject: [PATCH 183/248] bitprog reimplemented --- pyfpga/helpers/bitprog.py | 107 +++++++++++++++----------------------- 1 file changed, 42 insertions(+), 65 deletions(-) diff --git a/pyfpga/helpers/bitprog.py b/pyfpga/helpers/bitprog.py index 0282f263..6af85884 100644 --- a/pyfpga/helpers/bitprog.py +++ b/pyfpga/helpers/bitprog.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 PyFPGA Project +# Copyright (C) 2020-2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # @@ -10,34 +10,25 @@ """ import argparse -import logging import sys -from fpga import __version__ as version -from fpga.project import Project, TOOLS -from fpga.tool import MEMWIDTHS +from pathlib import Path +from pyfpga import __version__ as version +from pyfpga.factory import Factory, TOOLS +from pyfpga.project import STEPS -logging.basicConfig() -logging.getLogger('fpga.project').level = logging.INFO +tools = list(TOOLS.keys()) +devs = ['fpga', 'spi', 'bpi'] +positions = range(1, 10) +widths = [2**i for i in range(6)] -DEVS = ['fpga', 'spi', 'bpi'] -POSITIONS = range(1, 10) -ACTIONS = ['program', 'detect', 'unlock'] - -EPILOGUE = """ +EPILOGUE = f""" 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) -) +* TOOL = {'|'.join(tools)} +* TYPE = {'|'.join(devs)} +* POSITION = {'|'.join(map(str, positions))} +* WIDTH = {'|'.join(map(str, widths))} +""" def main(): @@ -54,43 +45,36 @@ def main(): parser.add_argument( '-v', '--version', action='version', - version='v{}'.format(version) - ) - - parser.add_argument( - 'bit', - metavar='BITFILE', - nargs='?', - help='a bitstream file' + version=f'v{version}' ) parser.add_argument( '-t', '--tool', metavar='TOOL', default='vivado', - choices=TOOLS, + choices=tools, help='backend tool to be used [vivado]' ) parser.add_argument( - '-o', '--outdir', + '-o', '--odir', metavar='PATH', - default='temp', - help='where to generate files [temp]' + default='results', + help='where to generate files [results]' ) parser.add_argument( '-d', '--device', - metavar='DEVTYPE', - choices=DEVS, - default=DEVS[0], - help='the target device type [{}]'.format(DEVS[0]) + metavar='TYPE', + choices=devs, + default=devs[0], + help=f'the target device type [{devs[0]}]' ) parser.add_argument( '-p', '--position', metavar='POSITION', - choices=POSITIONS, + choices=positions, type=int, default=1, help='the device position into the JTAG chain [1]' @@ -98,47 +82,40 @@ def main(): parser.add_argument( '-m', '--memname', - metavar='MEMNAME', - default='', - help='memory name if spi or bpi selected' + metavar='NAME', + help='memory name for SPI or BPI devices [None]' ) parser.add_argument( '-w', '--width', - metavar='MEMWIDTH', - choices=MEMWIDTHS, + metavar='WIDTH', + choices=widths, type=int, default=1, - help='memory width if spi or bpi selected [1]' + help='memory width for SPI or BPI devices [1]' ) parser.add_argument( - '--run', - metavar='ACTION', - choices=ACTIONS, - default=ACTIONS[0], - help='action to perform [{}]'.format(ACTIONS[0]) + 'bit', + metavar='BITFILE', + help='a bitstream file' ) 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' + prj = Factory(args.tool, odir=args.odir) try: - prj.transfer(devtype, args.position, args.memname, args.width) - except RuntimeError: - logging.error('{} not found'.format(args.tool)) + 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)) From 995eae3b119c3a2af4c87195c571ee78547198f3 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 17 Jul 2024 20:02:37 -0300 Subject: [PATCH 184/248] docs: add bitprog help --- docs/Makefile | 13 +++---------- docs/helpers.rst | 5 +++++ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/docs/Makefile b/docs/Makefile index 48576179..a9325d75 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,24 +1,17 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build SOURCEDIR = . BUILDDIR = _build +HELPERS = $(BUILDDIR)/hdl2bit $(BUILDDIR)/prj2bit $(BUILDDIR)/bitprog -# Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile -$(BUILDDIR)/hdl2bit $(BUILDDIR)/prj2bit: +$(HELPERS): @mkdir -p $(@D) @python3 ../pyfpga/helpers/$(@F).py -h > $@ -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile $(BUILDDIR)/hdl2bit $(BUILDDIR)/prj2bit +%: Makefile $(HELPERS) @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/helpers.rst b/docs/helpers.rst index e5623c92..542deb27 100644 --- a/docs/helpers.rst +++ b/docs/helpers.rst @@ -10,3 +10,8 @@ prj2bit ------- .. literalinclude:: _build/prj2bit + +bitprog +------- + +.. literalinclude:: _build/bitprog From 6220dbdd07cb1181bcccb577dffe88b3a1c47bd7 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 5 Aug 2024 20:51:40 -0300 Subject: [PATCH 185/248] Modified to use TOOLS for choices --- tests/projects/support.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/projects/support.py b/tests/projects/support.py index 2e26e3b5..68bc88b1 100644 --- a/tests/projects/support.py +++ b/tests/projects/support.py @@ -3,12 +3,13 @@ import argparse import sys -from pyfpga.factory import Factory +from pyfpga.factory import Factory, TOOLS + parser = argparse.ArgumentParser() parser.add_argument( '--tool', default='openflow', - choices=['ise', 'libero', 'quartus', 'openflow', 'vivado'] + choices=list(TOOLS.keys()) ) args = parser.parse_args() From cc89caf82268d7769e6c3a3717cef7f899ded4f3 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 5 Aug 2024 21:59:23 -0300 Subject: [PATCH 186/248] Modify tests to be able of run pytest from project root --- Makefile | 2 +- tests/test_data.py | 72 ++++++++++++++++++++++++++++----------------- tests/test_tools.py | 18 +++++++----- 3 files changed, 56 insertions(+), 36 deletions(-) diff --git a/Makefile b/Makefile index f9eda4cb..fb528da7 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ lint: git diff --check --cached test: - cd tests; pytest + pytest clean: py3clean . diff --git a/tests/test_data.py b/tests/test_data.py index 3ba4e4a9..a8e2af19 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -2,41 +2,59 @@ from pyfpga.vivado import Vivado +tdir = Path(__file__).parent.resolve() + pattern = { 'project': 'EXAMPLE', 'part': 'PARTNAME', 'includes': [ - Path('fakedata/dir1').resolve().as_posix(), - Path('fakedata/dir2').resolve().as_posix(), - Path('fakedata/dir3').resolve().as_posix() + Path(tdir / 'fakedata/dir1').resolve().as_posix(), + Path(tdir / 'fakedata/dir2').resolve().as_posix(), + Path(tdir / 'fakedata/dir3').resolve().as_posix() ], 'files': { - Path('fakedata/vhdl0.vhdl').resolve().as_posix(): { + Path(tdir / 'fakedata/vhdl0.vhdl').resolve().as_posix(): { 'hdl': 'vhdl', 'lib': 'LIB' }, - Path('fakedata/dir1/vhdl1.vhdl').resolve().as_posix(): { + Path(tdir / 'fakedata/dir1/vhdl1.vhdl').resolve().as_posix(): { 'hdl': 'vhdl', 'lib': 'LIB' }, - Path('fakedata/dir2/vhdl2.vhdl').resolve().as_posix(): { + Path(tdir / 'fakedata/dir2/vhdl2.vhdl').resolve().as_posix(): { 'hdl': 'vhdl', 'lib': 'LIB' }, - Path('fakedata/dir3/vhdl3.vhdl').resolve().as_posix(): { + Path(tdir / 'fakedata/dir3/vhdl3.vhdl').resolve().as_posix(): { 'hdl': 'vhdl', 'lib': 'LIB' }, - Path('fakedata/vlog0.v').resolve().as_posix(): {'hdl': 'vlog'}, - Path('fakedata/dir1/vlog1.v').resolve().as_posix(): {'hdl': 'vlog'}, - Path('fakedata/dir2/vlog2.v').resolve().as_posix(): {'hdl': 'vlog'}, - Path('fakedata/dir3/vlog3.v').resolve().as_posix(): {'hdl': 'vlog'}, - Path('fakedata/slog0.sv').resolve().as_posix(): {'hdl': 'slog'}, - Path('fakedata/dir1/slog1.sv').resolve().as_posix(): {'hdl': 'slog'}, - Path('fakedata/dir2/slog2.sv').resolve().as_posix(): {'hdl': 'slog'}, - Path('fakedata/dir3/slog3.sv').resolve().as_posix(): {'hdl': 'slog'} + 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('fakedata/cons/all.xdc').resolve().as_posix(): 'all', - Path('fakedata/cons/syn.xdc').resolve().as_posix(): 'syn', - Path('fakedata/cons/par.xdc').resolve().as_posix(): 'par' + Path(tdir / 'fakedata/cons/all.xdc').resolve().as_posix(): 'all', + Path(tdir / 'fakedata/cons/syn.xdc').resolve().as_posix(): 'syn', + Path(tdir / 'fakedata/cons/par.xdc').resolve().as_posix(): 'par' }, 'params': { 'PAR1': 'VAL1', @@ -65,15 +83,15 @@ def test_data(): prj = Vivado('EXAMPLE') prj.set_part('PARTNAME') prj.set_top('TOPNAME') - prj.add_include('fakedata/dir1') - prj.add_include('fakedata/dir2') - prj.add_include('fakedata/dir3') - prj.add_slog('fakedata/**/*.sv') - prj.add_vhdl('fakedata/**/*.vhdl', 'LIB') - prj.add_vlog('fakedata/**/*.v') - prj.add_cons('fakedata/cons/all.xdc') - prj.add_cons('fakedata/cons/syn.xdc', 'syn') - prj.add_cons('fakedata/cons/par.xdc', 'par') + 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'), 'syn') + prj.add_cons(str(tdir / 'fakedata/cons/par.xdc'), 'par') prj.add_param('PAR1', 'VAL1') prj.add_param('PAR2', 'VAL2') prj.add_param('PAR3', 'VAL3') diff --git a/tests/test_tools.py b/tests/test_tools.py index 7c23c4a1..45f972ed 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -1,6 +1,8 @@ from pathlib import Path from pyfpga.factory import Factory +tdir = Path(__file__).parent.resolve() + def test_ise(): tool = 'ise' @@ -45,15 +47,15 @@ def generate(tool, part): prj = Factory(tool, odir=f'results/{tool}') prj.set_part(part) prj.set_top('TOPNAME') - prj.add_include('fakedata/dir1') - prj.add_include('fakedata/dir2') + prj.add_include(str(tdir / 'fakedata/dir1')) + prj.add_include(str(tdir / 'fakedata/dir2')) if tool != 'ise': - prj.add_slog('fakedata/**/*.sv') - prj.add_vhdl('fakedata/**/*.vhdl', 'LIB') - prj.add_vlog('fakedata/**/*.v') - prj.add_cons('fakedata/cons/all.xdc') - prj.add_cons('fakedata/cons/syn.xdc', 'syn') - prj.add_cons('fakedata/cons/par.xdc', 'par') + 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'), 'syn') + prj.add_cons(str(tdir / 'fakedata/cons/par.xdc'), 'par') prj.add_param('PAR1', 'VAL1') prj.add_param('PAR2', 'VAL2') prj.add_define('DEF1', 'VAL1') From 6fecb1d17c5d0ee247911625fa86f23b64f65141 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 8 Aug 2024 22:01:21 -0300 Subject: [PATCH 187/248] docs: added new sections --- docs/extending.rst | 2 ++ docs/index.rst | 3 ++- docs/tools.rst | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 docs/extending.rst diff --git a/docs/extending.rst b/docs/extending.rst new file mode 100644 index 00000000..20d48463 --- /dev/null +++ b/docs/extending.rst @@ -0,0 +1,2 @@ +Extending +========= diff --git a/docs/index.rst b/docs/index.rst index ad539fe4..e9241b3e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -16,7 +16,8 @@ PyFPGA's documentation intro basic advanced - api helpers + api tools internals + extending diff --git a/docs/tools.rst b/docs/tools.rst index a7c6dc55..ffd3d97b 100644 --- a/docs/tools.rst +++ b/docs/tools.rst @@ -1,5 +1,5 @@ -Tools support -============= +Tools +===== +---------------+-----------+---------+-----+-----------------------------------------------+ | Tools | Vendor | Version | Tcl | Comment | From e36413d6c41b9483cbcce375251320c288525699 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 8 Aug 2024 22:39:46 -0300 Subject: [PATCH 188/248] docs: complete introduction section --- docs/basic.rst | 18 +----------------- docs/intro.rst | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/docs/basic.rst b/docs/basic.rst index 824aea8d..b178c0fc 100644 --- a/docs/basic.rst +++ b/docs/basic.rst @@ -3,7 +3,7 @@ Basic usage .. ATTENTION:: - (2024-05-31) To be updated. + (2024-08-08) To be updated. Project Creation ---------------- @@ -18,22 +18,6 @@ name is used when *no name* is provided). prj = Project('vivado', 'projectName') -.. NOTE:: - - The supported tool are: ``ghdl``, ``ise``, ``libero``, ``openflow``, - ``quartus``, ``vivado``, ``yosys``, ``yosys-ise`` and ``yosys-vivado``. - -.. 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). - 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. diff --git a/docs/intro.rst b/docs/intro.rst index c516b331..abc7e945 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -1,2 +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). From 908533866584ce4c62fd81afda5b017a11b2df5a Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 11 Aug 2024 17:21:09 -0300 Subject: [PATCH 189/248] Add Vivado hooks examples --- examples/hooks/vivado.py | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 examples/hooks/vivado.py diff --git a/examples/hooks/vivado.py b/examples/hooks/vivado.py new file mode 100644 index 00000000..8621ae11 --- /dev/null +++ b/examples/hooks/vivado.py @@ -0,0 +1,41 @@ +"""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 +''') + +prj.add_hook('postcfg', f''' +set_property USED_IN_SYNTHESIS FALSE [get_files {Path('../sources/cons/ZYBO/clk.xdc').resolve()}] +set_property USED_IN_SYNTHESIS FALSE [get_files {Path('../sources/cons/ZYBO/led.xdc').resolve()}] +''') + +prj.make() From e1c6ebf875c74282b95a76c9ed6bd2f96dd4589c Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 11 Aug 2024 17:41:52 -0300 Subject: [PATCH 190/248] Remove the 'when' parameter from 'add_cons' (Vivado-specific) --- docs/internals.rst | 12 ++++++------ examples/hooks/vivado.py | 5 +++-- examples/projects/ise.py | 12 ++++++------ examples/projects/libero.py | 6 +++--- examples/projects/openflow.py | 16 ++++++++-------- examples/projects/quartus.py | 6 +++--- examples/projects/vivado.py | 12 ++++++------ pyfpga/project.py | 9 +++------ pyfpga/templates/vivado.jinja | 5 ----- tests/test_data.py | 10 +++++----- tests/test_tools.py | 4 ++-- 11 files changed, 45 insertions(+), 52 deletions(-) diff --git a/docs/internals.rst b/docs/internals.rst index fd388ae4..4da687d2 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -39,15 +39,15 @@ Internal data structure 'part': 'PARTNAME', 'includes': ['DIR1', 'DIR2', 'DIR3'], 'files': { - 'FILE1': {'hdl': 'vhdl', 'lib': 'LIB1'} - 'FILE2': {'hdl': 'vlog'}, - 'FILE3': {'hdl': 'slog'} + 'FILE1': {'hdl': 'vhdl', 'lib': 'LIB1', 'opt': 'OPTS'}, + 'FILE2': {'hdl': 'vlog', 'opt': 'OPTS'}, + 'FILE3': {'hdl': 'slog', 'opt': 'OPTS'} }, 'top': 'TOPNAME', 'constraints': { - 'FILE1': 'all', - 'FILE2': 'syn', - 'FILE3': 'par' + 'FILE1': {'opt': 'OPTS'}, + 'FILE2': {'opt': 'OPTS'}, + 'FILE3': {'opt': 'OPTS'} }, 'params': { 'PAR1': 'VAL1', diff --git a/examples/hooks/vivado.py b/examples/hooks/vivado.py index 8621ae11..9d239247 100644 --- a/examples/hooks/vivado.py +++ b/examples/hooks/vivado.py @@ -33,9 +33,10 @@ 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('../sources/cons/ZYBO/clk.xdc').resolve()}] -set_property USED_IN_SYNTHESIS FALSE [get_files {Path('../sources/cons/ZYBO/led.xdc').resolve()}] +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/projects/ise.py b/examples/projects/ise.py index a1b9dba0..a81082a2 100644 --- a/examples/projects/ise.py +++ b/examples/projects/ise.py @@ -22,15 +22,15 @@ if args.board == 's6micro': prj.set_part('xc6slx9-2-csg324') prj.add_param('FREQ', '125000000') - prj.add_cons('../sources/cons/s6micro/clk.xcf', 'syn') - prj.add_cons('../sources/cons/s6micro/clk.ucf', 'par') - prj.add_cons('../sources/cons/s6micro/led.ucf', 'par') + prj.add_cons('../sources/cons/s6micro/clk.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/clk.xcf', 'syn') - prj.add_cons('../sources/cons/nexys3/clk.ucf', 'par') - prj.add_cons('../sources/cons/nexys3/led.ucf', 'par') + prj.add_cons('../sources/cons/nexys3/clk.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': diff --git a/examples/projects/libero.py b/examples/projects/libero.py index 4f4360d7..e9e2697c 100644 --- a/examples/projects/libero.py +++ b/examples/projects/libero.py @@ -21,9 +21,9 @@ if args.board == 'maker': prj.set_part('m2s010-1-tq144') prj.add_param('FREQ', '125000000') - prj.add_cons('../sources/cons/maker/clk.sdc', 'syn') - prj.add_cons('../sources/cons/maker/clk.pdc', 'par') - prj.add_cons('../sources/cons/maker/led.pdc', 'par') + prj.add_cons('../sources/cons/maker/clk.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': diff --git a/examples/projects/openflow.py b/examples/projects/openflow.py index b40f6c96..0b1242b3 100644 --- a/examples/projects/openflow.py +++ b/examples/projects/openflow.py @@ -23,23 +23,23 @@ if args.board == 'icestick': prj.set_part('hx1k-tq144') prj.add_param('FREQ', '100000000') - prj.add_cons('../sources/cons/icestick/clk.pcf', 'par') - prj.add_cons('../sources/cons/icestick/led.pcf', 'par') + 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', 'par') - prj.add_cons('../sources/cons/edu-ciaa/led.pcf', 'par') + 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', 'par') - prj.add_cons('../sources/cons/orangecrab/led.lpf', 'par') + 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', 'par') - prj.add_cons('../sources/cons/ecp5evn/led.lpf', 'par') + 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': diff --git a/examples/projects/quartus.py b/examples/projects/quartus.py index 07cfedc4..cccbb372 100644 --- a/examples/projects/quartus.py +++ b/examples/projects/quartus.py @@ -22,9 +22,9 @@ if args.board == 'de10nano': prj.set_part('5CSEBA6U23I7') prj.add_param('FREQ', '125000000') - prj.add_cons('../sources/cons/de10nano/clk.sdc', 'syn') - prj.add_cons('../sources/cons/de10nano/clk.tcl', 'par') - prj.add_cons('../sources/cons/de10nano/led.tcl', 'par') + prj.add_cons('../sources/cons/de10nano/clk.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': diff --git a/examples/projects/vivado.py b/examples/projects/vivado.py index 367f2861..35ccecc7 100644 --- a/examples/projects/vivado.py +++ b/examples/projects/vivado.py @@ -22,15 +22,15 @@ if args.board == 'zybo': prj.set_part('xc7z010-1-clg400') prj.add_param('FREQ', '125000000') - prj.add_cons('../sources/cons/ZYBO/timing.xdc', 'syn') - prj.add_cons('../sources/cons/ZYBO/clk.xdc', 'par') - prj.add_cons('../sources/cons/ZYBO/led.xdc', 'par') + 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', 'syn') - prj.add_cons('../sources/cons/arty_a7_35t/clk.xdc', 'par') - prj.add_cons('../sources/cons/arty_a7_35t/led.xdc', 'par') + 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': diff --git a/pyfpga/project.py b/pyfpga/project.py index 72e7edc0..e6ba24fe 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -123,22 +123,19 @@ def add_vlog(self, pathname): self.logger.debug('Executing add_vlog') self._add_file(pathname, 'vlog') - def add_cons(self, path, when='all'): + def add_cons(self, path): """Add a constraint file. :param pathname: path of a file :type pathname: str - :param when: always ('all'), synthesis ('syn') or P&R ('par') - :type only: str, optional :raises FileNotFoundError: if path is not found """ self.logger.debug('Executing add_cons') path = Path(path).resolve() if not path.is_file(): raise FileNotFoundError(path) - if when not in ['all', 'syn', 'par']: - raise ValueError('Invalid only.') - self.data.setdefault('constraints', {})[path.as_posix()] = when + attr = {} + self.data.setdefault('constraints', {})[path.as_posix()] = attr def add_param(self, name, value): """Add a Parameter/Generic Value. diff --git a/pyfpga/templates/vivado.jinja b/pyfpga/templates/vivado.jinja index babbf3b6..e214cfb4 100644 --- a/pyfpga/templates/vivado.jinja +++ b/pyfpga/templates/vivado.jinja @@ -25,11 +25,6 @@ add_file {{ name }} {% if constraints %}# Constraints inclusion {% for name, attr in constraints.items() %} add_file -fileset constrs_1 {{ name }} -{% if attr == "syn" %} -set_property USED_IN_IMPLEMENTATION FALSE [get_files {{ name }}] -{% elif attr == "par" %} -set_property USED_IN_SYNTHESIS FALSE [get_files {{ name }}] -{% endif %} {% if loop.first %}set_property TARGET_CONSTRS_FILE {{ name }} [current_fileset -constrset]{% endif %} {% endfor %} {% endif %} diff --git a/tests/test_data.py b/tests/test_data.py index a8e2af19..49b0645b 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -52,9 +52,9 @@ }, 'top': 'TOPNAME', 'constraints': { - Path(tdir / 'fakedata/cons/all.xdc').resolve().as_posix(): 'all', - Path(tdir / 'fakedata/cons/syn.xdc').resolve().as_posix(): 'syn', - Path(tdir / 'fakedata/cons/par.xdc').resolve().as_posix(): 'par' + 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': 'VAL1', @@ -90,8 +90,8 @@ def test_data(): 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'), 'syn') - prj.add_cons(str(tdir / 'fakedata/cons/par.xdc'), 'par') + 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_param('PAR3', 'VAL3') diff --git a/tests/test_tools.py b/tests/test_tools.py index 45f972ed..a2950019 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -54,8 +54,8 @@ def generate(tool, part): 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'), 'syn') - prj.add_cons(str(tdir / 'fakedata/cons/par.xdc'), 'par') + 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') From e0943bdb50d426a4e2db1b4ded2ebcb93c8836e3 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 11 Aug 2024 18:37:19 -0300 Subject: [PATCH 191/248] Add a Vivado Elaboration example (using hooks) Closes #39 --- examples/hooks/vivado-elab.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 examples/hooks/vivado-elab.py 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() From 83d1e59e413600cbd13f37e74e5c18eeec49d811 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 11 Aug 2024 22:09:21 -0300 Subject: [PATCH 192/248] Adds set_debug() --- pyfpga/project.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyfpga/project.py b/pyfpga/project.py index e6ba24fe..86d9f267 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -200,6 +200,10 @@ def add_hook(self, stage, hook): 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. From 0103506a38d2d50ff4b95c89d3c96aec597670a5 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 11 Aug 2024 22:10:01 -0300 Subject: [PATCH 193/248] docs: rewritten basic usage --- docs/basic.rst | 163 ++++++++++++++++++++++--------------------------- docs/index.rst | 5 -- 2 files changed, 74 insertions(+), 94 deletions(-) diff --git a/docs/basic.rst b/docs/basic.rst index b178c0fc..5efcfccd 100644 --- a/docs/basic.rst +++ b/docs/basic.rst @@ -1,148 +1,133 @@ Basic usage =========== -.. ATTENTION:: +Project Configuration +--------------------- - (2024-08-08) To be updated. +The first steps involve importing the necessary module to support the desired tool and instantiating the corresponding *class*: -Project Creation ----------------- +.. code-block:: python + + from pyfpga.vivado import Vivado + + prj = Vivado('PRJNAME', odir='OUTDIR') -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). +In the example, we are using Vivado, specifying the optional parameter *project name* (*tool name* if omitted) and *output directory* (*results* by default). + +Next step is to specify the target FPGA device: .. code-block:: python - from fpga.project import Project + prj.set_part('xc7k160t-3-fbg484') + +.. note:: - prj = Project('vivado', 'projectName') + Default parts are provided for each supported tool. -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. +HDL source files are added using one of the following methods: .. code-block:: python - prj.set_outdir('../temp') + 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') -Next, the FPGA part would be specified: +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. -.. code-block:: python +For `add_vhdl`, you can also optionally specify a library name where the files will be included. - prj.set_part('xc7k160t-3-fbg484') +.. note:: + + Internally, the methods that specify files use `glob`_ to support wildcards and `Path`_ to obtain absolute paths. -.. NOTE:: + .. _glob: https://docs.python.org/3/library/glob.html + .. _Path: https://docs.python.org/3/library/pathlib.html - 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): +Generics/parameters can be specified with: + +.. code-block:: python - * **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*) + prj.add_param('PARAMNAME', 'PARAMVALUE') -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. +For Verilog and SystemVerilog, the following methods are also available: .. 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') + prj.add_include('PATH_TO_A_DIRECTORY') + prj.add_define('DEFNAME', 'DEFVALUE') -.. NOTE:: +Constraint source files are included using the following: - * 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_vlog_include`` can be used to specify where to - search for included files. +.. code-block:: python + + prj.add_cons('PATH_TO_FILES_GLOB_COMPATIBLE') -Finally, the top-level must be specified: +Finally, the top-level can be specified as follows: .. code-block:: python prj.set_top('Top') -.. NOTE:: +.. note:: - A relative path to a valid VHDL/Verilog file is also accepted by ``set_top``, - to automatically extract the top-level name. + The order of the methods described in this section is not significant. + They will be arranged in the required order by the underlying template. -Project generation ------------------- +Bitstream generation +-------------------- -Next step if to generate the project. In the most basic form, you can run the -following to get a bitstream: +After configuring the project, you can run the following to generate a bitstream: .. code-block:: python - prj.generate() + prj.make() -Additionally, you can specify which task to perform: +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.generate('syn') - -.. NOTE:: - - The valid values are: + prj.make(first='syn', last='par') - * ``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. +.. note:: -Bitstream transfer ------------------- + Valid values are: -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: + * ``cfg``: generates the project file + * ``syn``: performs synthesis + * ``par``: performs place and route + * ``bit``: performs bitstream generation -.. code-block:: python +.. note:: - prj.transfer(devtype, position, part, width) + After executing this method, you will find the file `<TOOL>.tcl` (or `sh` in some cases) in the output directory. + For debugging purposes, if things do not work as expected, you can review this file. -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. +Bitstream programming +--------------------- -.. NOTE:: +The final step is programming the FPGA: - * 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). +.. code-block:: python -Logging capabilities --------------------- + prj.prog('BITSTREAM', 'POSITION') -PyFPGA uses the `logging <https://docs.python.org/3/library/logging.html>`_ -module, with a *NULL* handler and the *INFO* level by default. -Messages can be enabled with: +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). -.. code-block:: python +.. note:: - import logging + After executing this method, you will find the file `<TOOL>prog.tcl` (or `sh` in some cases) in the output directory. + For debugging purposes, if things do not work as expected, you can review this file. - logging.basicConfig() +Debugging +--------- -You can enable *DEBUG* messages adding: +Under the hood, `logging`_ is employed. To enable debug messages, you can use: .. code-block:: python - logging.getLogger('fpga.project').level = logging.DEBUG + prj.set_debug() + +.. _logging: https://docs.python.org/3/library/logging.html diff --git a/docs/index.rst b/docs/index.rst index e9241b3e..3e8dfcb0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,11 +6,6 @@ PyFPGA's documentation :align: center :target: https://github.com/PyFPGA/pyfpga -.. ATTENTION:: - - (2024-05-31) PyFPGA is in the process of being strongly rewritten/simplified. - Most changes are internal, but the API will also change. - .. toctree:: intro From e552a69fa8aba9a736dc7ae2b7717cd9f1b8c772 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 11 Aug 2024 23:29:57 -0300 Subject: [PATCH 194/248] docs: rewritten (WIP) advanced usage --- docs/advanced.rst | 184 +++++++++++----------------------------------- 1 file changed, 42 insertions(+), 142 deletions(-) diff --git a/docs/advanced.rst b/docs/advanced.rst index 245f4c08..44762476 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -1,161 +1,61 @@ Advanced usage ============== -.. ATTENTION:: - - (2024-05-31) To be updated. - -Multi project managment ------------------------ - -.. code-block:: python +PyFPGA offers advanced features for more customized and flexible control over FPGA project management. +This section covers two key advanced features: - PROJECTS = { - '<NAME1>': Project( - '<TOOLNAME>', - '<PROJECTNAME>', - { - 'outdir': '<DIRNAME>', - 'part': '<PARTNAME>' - 'paths': [ - '<PATHNAME1>', - ... - '<PATHNAMEn>' - ], - 'vhdl': [ - ['<FILENAME1>', '<LIBRARYNAME1>'], - '<FILENAME2>', - ... - '<FILENAMEn>' - ], - 'verilog': [ - '<FILENAME1>', - ... - '<FILENAMEn>' - ], - 'constraint': [ - '<FILENAME1>', - ... - '<FILENAMEn>' - ], - 'params': { - '<PARAMNAME1>': '<VALUE1>', - ... - '<PARAMNAMEn>': '<VALUEn>' - }, - 'top': '<TOPNAME>' - } - ) - '<NAME2>': Project( - ... - ) - } +1. **Hooks**: These are points in the code where you can insert custom code to extend or modify the behavior of the tool. +Hooks provide a way to integrate additional functionality or perform specific actions at predefined stages of the project lifecycle. -.. _hooks: +2. **Options**: This feature allows you to specify additional options to fine-tune the tool's behavior. +Options provide greater control over the tool's operation and enable you to customize the processing according to your specific requirements. 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 | Place and Route | -+--------------------------+----------------------+ -| Parameters specification | **postpar** 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``, ``postpar`` 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.add_param('param1', value1) - ... - prj.add_param('paramN', valueN) - -Generate options ----------------- - -The method ``generate`` (previously seen at the end of -[Basic usage](#basic-usage) section) has optional parameters: +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.generate(to_task, from_task, capture) + prj.add_hook('presyn', 'COMMAND1') + prj.add_hook('presyn', 'COMMAND2') + prj.add_hook('presyn', 'COMMAND3') -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``, ``par`` 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: +Or in a multi-line format: .. code-block:: python - prj.generate(to_task='syn', from_task='prj') - #Some other Python commands here - prj.generate(to_task='bit', from_task='syn') + prj.add_hook('presyn', """ + COMMAND1 + COMMAND2 + COMMAND3 + """) -In case of *capture*, it is useful to catch execution messages to be -post-processed or saved to a file: - -.. code-block:: python +Options +------- - 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)) +.. ATTENTION:: -And wait for the backend Tool to accomplish its task. + WIP feature. From fe6937afec60b92dc27c224ac0f8f1450b43831f Mon Sep 17 00:00:00 2001 From: Markus Koch <markus@notsyncing.net> Date: Thu, 1 Aug 2024 07:53:32 +0200 Subject: [PATCH 195/248] Implement support for Lattice Diamond --- README.md | 1 + examples/hooks/diamond.py | 52 +++++++++++++ examples/projects/diamond.py | 51 +++++++++++++ examples/sources/cons/brevia2/clk.lpf | 3 + examples/sources/cons/brevia2/io.lpf | 4 + pyfpga/diamond.py | 30 ++++++++ pyfpga/factory.py | 2 + pyfpga/templates/diamond-prog.jinja | 19 +++++ pyfpga/templates/diamond.jinja | 105 ++++++++++++++++++++++++++ tests/mocks/diamondc | 38 ++++++++++ tests/test_tools.py | 7 ++ 11 files changed, 312 insertions(+) create mode 100644 examples/hooks/diamond.py create mode 100644 examples/projects/diamond.py create mode 100644 examples/sources/cons/brevia2/clk.lpf create mode 100644 examples/sources/cons/brevia2/io.lpf create mode 100644 pyfpga/diamond.py create mode 100644 pyfpga/templates/diamond-prog.jinja create mode 100644 pyfpga/templates/diamond.jinja create mode 100755 tests/mocks/diamondc diff --git a/README.md b/README.md index c3e12f21..28441fcf 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # PyFPGA [![License](https://img.shields.io/badge/License-GPL--3.0-darkgreen?style=flat-square)](LICENSE) +![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-2024.1-blue.svg?style=flat-square) ![Quartus](https://img.shields.io/badge/Quartus--Prime-23.1-blue.svg?style=flat-square) diff --git a/examples/hooks/diamond.py b/examples/hooks/diamond.py new file mode 100644 index 00000000..eac463fa --- /dev/null +++ b/examples/hooks/diamond.py @@ -0,0 +1,52 @@ +"""Diamond example hooks.""" + +from pyfpga.diamond import Diamond + +prj = Diamond(odir='../build/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', 'syn') +prj.add_cons('../sources/cons/brevia2/clk.lpf', 'par') +prj.add_cons('../sources/cons/brevia2/io.lpf', 'par') + +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/projects/diamond.py b/examples/projects/diamond.py new file mode 100644 index 00000000..9e602d5c --- /dev/null +++ b/examples/projects/diamond.py @@ -0,0 +1,51 @@ +"""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' +) +args = parser.parse_args() + +prj = Diamond(odir='../build/diamond') + +if args.board == 'brevia2': + prj.set_part('LFXP2-5E-5TN144C') + prj.add_param('FREQ', '50000000') + prj.add_cons('../sources/cons/brevia2/clk.lpf', 'syn') + prj.add_cons('../sources/cons/brevia2/clk.lpf', 'par') + prj.add_cons('../sources/cons/brevia2/io.lpf', 'par') +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') + +if args.action in ['make', 'all']: + prj.make() + +if args.action in ['prog', 'all']: + prj.prog() 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/io.lpf b/examples/sources/cons/brevia2/io.lpf new file mode 100644 index 00000000..9933563f --- /dev/null +++ b/examples/sources/cons/brevia2/io.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/pyfpga/diamond.py b/pyfpga/diamond.py new file mode 100644 index 00000000..a5b3e622 --- /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 index 80782183..753ce84d 100644 --- a/pyfpga/factory.py +++ b/pyfpga/factory.py @@ -10,6 +10,7 @@ # 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 @@ -18,6 +19,7 @@ TOOLS = { + 'diamond': Diamond, 'ise': Ise, 'libero': Libero, 'openflow': Openflow, 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..3a191524 --- /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 <constraints-file>` multiple times. +set fileId [open diamond.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/tests/mocks/diamondc b/tests/mocks/diamondc new file mode 100755 index 00000000..1beac9c1 --- /dev/null +++ b/tests/mocks/diamondc @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +import argparse +import subprocess +import sys + + +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/test_tools.py b/tests/test_tools.py index 45f972ed..ffaa7e5b 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -4,6 +4,13 @@ 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' + + def test_ise(): tool = 'ise' generate(tool, 'DEVICE-PACKAGE-SPEED') From 45995b8a93ed384b2173570a343d0e22a5049981 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 12 Aug 2024 19:50:32 -0300 Subject: [PATCH 196/248] Fix a linter issue --- examples/hooks/diamond.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/hooks/diamond.py b/examples/hooks/diamond.py index eac463fa..0e0b5d55 100644 --- a/examples/hooks/diamond.py +++ b/examples/hooks/diamond.py @@ -47,6 +47,6 @@ prj.set_top('Top') for hook_name, hook in hooks.items(): - prj.add_hook('postpar', hook) + prj.add_hook('postpar', hook) prj.make() From 95afdae1f2db803c09c872fb991d6b45d3d06e7f Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 12 Aug 2024 20:04:04 -0300 Subject: [PATCH 197/248] docs: add content to 'Extending' --- docs/extending.rst | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/docs/extending.rst b/docs/extending.rst index 20d48463..03c995cf 100644 --- a/docs/extending.rst +++ b/docs/extending.rst @@ -1,2 +1,40 @@ Extending ========= + +1. Add support for the new tool: + +.. code-block:: python + + pyfpga/templates/<NEWTOOL>.jinja + pyfpga/templates/<NEWTOOL>-prog.jinja + pyfpga/<NEWTOOL>.py + +2. Include the new tool on Factory: + +.. code-block:: python + + pyfpga/factory.py + +3. Add tests and a tool mock-up: + +.. code-block:: python + + tests/test_tools.py + tests/mocks/<NEWTOOL_EXECUTABLE> + +4. Updated the project's documentation: + +.. code-block:: python + + README.md + docs + +5. [OPTIONAL] Add examples: + +.. code-block:: python + + examples/sources/cons/<NEWBOARD>/timing.<EXT> + examples/sources/cons/<NEWBOARD>/clk.<EXT> + examples/sources/cons/<NEWBOARD>/led.<EXT> + examples/projects/<NEWTOOL>.py + examples/hooks/<NEWTOOL>.py From dd8e4de3b8ea10f9d3e3ac3aa4cd112be81ba105 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 12 Aug 2024 20:21:03 -0300 Subject: [PATCH 198/248] A few constraint files were renamed to maintain names coherency --- examples/helpers/ise.sh | 4 ++-- examples/helpers/libero.sh | 4 ++-- examples/helpers/quartus.sh | 4 ++-- examples/hooks/diamond.py | 5 ++--- examples/projects/diamond.py | 5 ++--- examples/projects/ise.py | 4 ++-- examples/projects/libero.py | 2 +- examples/projects/quartus.py | 2 +- examples/sources/cons/brevia2/{io.lpf => led.lpf} | 0 examples/sources/cons/de10nano/{clk.sdc => timing.sdc} | 0 examples/sources/cons/maker/{clk.sdc => timing.sdc} | 0 examples/sources/cons/nexys3/{clk.xcf => timing.xcf} | 0 examples/sources/cons/s6micro/{clk.xcf => timing.xcf} | 0 13 files changed, 14 insertions(+), 16 deletions(-) rename examples/sources/cons/brevia2/{io.lpf => led.lpf} (100%) rename examples/sources/cons/de10nano/{clk.sdc => timing.sdc} (100%) rename examples/sources/cons/maker/{clk.sdc => timing.sdc} (100%) rename examples/sources/cons/nexys3/{clk.xcf => timing.xcf} (100%) rename examples/sources/cons/s6micro/{clk.xcf => timing.xcf} (100%) diff --git a/examples/helpers/ise.sh b/examples/helpers/ise.sh index 11e73ba8..a366c6e9 100644 --- a/examples/helpers/ise.sh +++ b/examples/helpers/ise.sh @@ -7,12 +7,12 @@ 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/clk.xcf -f ../sources/cons/nexys3/clk.ucf -f ../sources/cons/nexys3/led.ucf \ + -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/clk.xcf -f ../sources/cons/nexys3/clk.ucf -f ../sources/cons/nexys3/led.ucf \ + -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/libero.sh b/examples/helpers/libero.sh index 1a56da93..8d270c48 100644 --- a/examples/helpers/libero.sh +++ b/examples/helpers/libero.sh @@ -7,12 +7,12 @@ 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/clk.sdc -f ../sources/cons/maker/clk.pdc -f ../sources/cons/maker/led.pdc \ + -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/clk.sdc -f ../sources/cons/maker/clk.pdc -f ../sources/cons/maker/led.pdc \ + -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/quartus.sh b/examples/helpers/quartus.sh index b2ae4437..b532ed83 100644 --- a/examples/helpers/quartus.sh +++ b/examples/helpers/quartus.sh @@ -7,12 +7,12 @@ 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/clk.sdc -f ../sources/cons/de10nano/clk.tcl -f ../sources/cons/de10nano/led.tcl \ + -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/clk.sdc -f ../sources/cons/de10nano/clk.tcl -f ../sources/cons/de10nano/led.tcl \ + -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/hooks/diamond.py b/examples/hooks/diamond.py index 0e0b5d55..389b9a60 100644 --- a/examples/hooks/diamond.py +++ b/examples/hooks/diamond.py @@ -33,9 +33,8 @@ prj.add_param('FREQ', '50000000') prj.add_param('SECS', '1') -prj.add_cons('../sources/cons/brevia2/clk.lpf', 'syn') -prj.add_cons('../sources/cons/brevia2/clk.lpf', 'par') -prj.add_cons('../sources/cons/brevia2/io.lpf', 'par') +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') diff --git a/examples/projects/diamond.py b/examples/projects/diamond.py index 9e602d5c..4f5a85de 100644 --- a/examples/projects/diamond.py +++ b/examples/projects/diamond.py @@ -22,9 +22,8 @@ if args.board == 'brevia2': prj.set_part('LFXP2-5E-5TN144C') prj.add_param('FREQ', '50000000') - prj.add_cons('../sources/cons/brevia2/clk.lpf', 'syn') - prj.add_cons('../sources/cons/brevia2/clk.lpf', 'par') - prj.add_cons('../sources/cons/brevia2/io.lpf', 'par') + 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': diff --git a/examples/projects/ise.py b/examples/projects/ise.py index a81082a2..38d886c1 100644 --- a/examples/projects/ise.py +++ b/examples/projects/ise.py @@ -22,13 +22,13 @@ if args.board == 's6micro': prj.set_part('xc6slx9-2-csg324') prj.add_param('FREQ', '125000000') - prj.add_cons('../sources/cons/s6micro/clk.xcf') + 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/clk.xcf') + 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') diff --git a/examples/projects/libero.py b/examples/projects/libero.py index e9e2697c..4edd978e 100644 --- a/examples/projects/libero.py +++ b/examples/projects/libero.py @@ -21,7 +21,7 @@ if args.board == 'maker': prj.set_part('m2s010-1-tq144') prj.add_param('FREQ', '125000000') - prj.add_cons('../sources/cons/maker/clk.sdc') + 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') diff --git a/examples/projects/quartus.py b/examples/projects/quartus.py index cccbb372..02ed76d3 100644 --- a/examples/projects/quartus.py +++ b/examples/projects/quartus.py @@ -22,7 +22,7 @@ if args.board == 'de10nano': prj.set_part('5CSEBA6U23I7') prj.add_param('FREQ', '125000000') - prj.add_cons('../sources/cons/de10nano/clk.sdc') + 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') diff --git a/examples/sources/cons/brevia2/io.lpf b/examples/sources/cons/brevia2/led.lpf similarity index 100% rename from examples/sources/cons/brevia2/io.lpf rename to examples/sources/cons/brevia2/led.lpf diff --git a/examples/sources/cons/de10nano/clk.sdc b/examples/sources/cons/de10nano/timing.sdc similarity index 100% rename from examples/sources/cons/de10nano/clk.sdc rename to examples/sources/cons/de10nano/timing.sdc diff --git a/examples/sources/cons/maker/clk.sdc b/examples/sources/cons/maker/timing.sdc similarity index 100% rename from examples/sources/cons/maker/clk.sdc rename to examples/sources/cons/maker/timing.sdc diff --git a/examples/sources/cons/nexys3/clk.xcf b/examples/sources/cons/nexys3/timing.xcf similarity index 100% rename from examples/sources/cons/nexys3/clk.xcf rename to examples/sources/cons/nexys3/timing.xcf diff --git a/examples/sources/cons/s6micro/clk.xcf b/examples/sources/cons/s6micro/timing.xcf similarity index 100% rename from examples/sources/cons/s6micro/clk.xcf rename to examples/sources/cons/s6micro/timing.xcf From d169a7b20e44dc43da80e09141cf695bcb82a17d Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 12 Aug 2024 20:26:51 -0300 Subject: [PATCH 199/248] Update copyright --- pyfpga/factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpga/factory.py b/pyfpga/factory.py index 753ce84d..6da4029a 100644 --- a/pyfpga/factory.py +++ b/pyfpga/factory.py @@ -1,5 +1,5 @@ # -# Copyright (C) 2024 Rodrigo A. Melo +# Copyright (C) 2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # From 25613a6b710d529e48379899655cd84cb5ebc216 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 12 Aug 2024 22:15:29 -0300 Subject: [PATCH 200/248] docs: 'Tools' rewritten --- docs/tools.rst | 236 +++++++++++++++++++++++++++++-------------------- 1 file changed, 141 insertions(+), 95 deletions(-) diff --git a/docs/tools.rst b/docs/tools.rst index ffd3d97b..8839ac2d 100644 --- a/docs/tools.rst +++ b/docs/tools.rst @@ -1,98 +1,144 @@ Tools ===== -+---------------+-----------+---------+-----+-----------------------------------------------+ -| Tools | Vendor | Version | Tcl | Comment | -+===============+===========+=========+=====+===============================================+ -| ISE | Xilinx | 14.7 | 8.4 | Discontinued in 2013 | -+---------------+-----------+---------+-----+-----------------------------------------------+ -| Libero-SoC | Microsemi | 2024.1 | 8.5 | Important changes in version 12.0 (2019) | -+---------------+-----------+---------+-----+-----------------------------------------------+ -| Openflow | | | | | -+---------------+-----------+---------+-----+-----------------------------------------------+ -| Quartus Prime | Intel | 23.1 | 8.6 | Known as Quartus II until version 15.0 (2015) | -+---------------+-----------+---------+-----+-----------------------------------------------+ -| Vivado | Xilinx | 2022.1 | 8.5 | Introduced in 2012, it superseded ISE | -+---------------+-----------+---------+-----+-----------------------------------------------+ - -* 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). - -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`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|**add_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_vlog_include** | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|**add_vlog_define** | ``TBI`` | ``TBI`` | ``TBI`` | ``TBI`` | ``TBI`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|**set_vhdl_arch** | ``TBI`` | ``TBI`` | ``TBI`` | ``TBI`` | ``TBI`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|**generate** | | | | | | -+------------------------------+---------+----------+------------+-----------+----------+ -|``prj`` | ``Yes`` | ``Yes`` | ``No`` | ``Yes`` | ``Yes`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|``syn`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | ``Yes`` | -+------------------------------+---------+----------+------------+-----------+----------+ -|``par`` | ``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 +.. 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 <https://www.latticesemi.com/latticediamond>`_ + +Diamond is the previous generation EDA tool from Lattice. + +Example: + +.. code:: + + from pyfpga.diamond import Diamond + + prj = Diamond() + +ISE +--- + +`ISE downloads <https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/vivado-design-tools/archive-ise.html>`_ + +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() + +Libero +------ + +`Libero downloads <https://www.microchip.com/en-us/products/fpgas-and-plds/fpga-and-soc-design-tools/fpga/libero-software-later-versions>`_ + +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() + +Openflow +-------- + +`Docker downloads <https://docs.docker.com/engine/install/>`_ + +Openflow is the combination of different Free/Libre and Open Source (FLOSS) tools: + +* 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() + +Quartus +------- + +`Quartus downloads <https://www.intel.com/content/www/us/en/products/details/fpga/development-tools/quartus-prime/resource.html>`_ + +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 <https://www.xilinx.com/support/download.html>`_ + +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() From 29b3d35593715d854cfd9984a429cbde91de423d Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 12 Aug 2024 22:37:05 -0300 Subject: [PATCH 201/248] docs: 'Internals' updated --- docs/internals.rst | 124 ++++++++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 59 deletions(-) diff --git a/docs/internals.rst b/docs/internals.rst index 4da687d2..504b69ec 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -1,72 +1,78 @@ Internals ========= -Underlying tool steps ---------------------- +Underlying steps +---------------- .. code-block:: - create project - config project - part - precfg (hook) - params - defines - includes - files - top - postcfg (hook) - close project + project creation [options] + project configuration + part + precfg hook + params + defines + includes + files [options] + top + postcfg hook + project close - open project - presyn (hook) - synthesis - postsyn (hook) - prepar (hook) - place_and_route - postpar (hook) - prebit (hook) - bitstream - postbit (hook) - close project + 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'] - } - } + 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' + } + } From 9fb634e7cc92420fb32b1da893c035a4bfade2c7 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 12 Aug 2024 22:46:42 -0300 Subject: [PATCH 202/248] docs: 'advanced' was updated/simplified --- docs/advanced.rst | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/docs/advanced.rst b/docs/advanced.rst index 44762476..84046c48 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -1,14 +1,7 @@ Advanced usage ============== -PyFPGA offers advanced features for more customized and flexible control over FPGA project management. -This section covers two key advanced features: - -1. **Hooks**: These are points in the code where you can insert custom code to extend or modify the behavior of the tool. -Hooks provide a way to integrate additional functionality or perform specific actions at predefined stages of the project lifecycle. - -2. **Options**: This feature allows you to specify additional options to fine-tune the tool's behavior. -Options provide greater control over the tool's operation and enable you to customize the processing according to your specific requirements. +The flow implemented by PyFPGA should be sufficient for most cases, but further customizations are possible and discussed in this section. Hooks ----- @@ -56,6 +49,8 @@ Or in a multi-line format: Options ------- +Options allow you to specify additional settings to fine-tune certain commands. The available options are: + .. ATTENTION:: - WIP feature. + This feature is WIP. From cdfec22663b543c0476dec96f6514fdb1c1d0f28 Mon Sep 17 00:00:00 2001 From: Markus Koch <markus@notsyncing.net> Date: Thu, 22 Aug 2024 11:04:32 +0200 Subject: [PATCH 203/248] diamond: Use the project name as prefix for the combined constraints Diamond defaults to <project_name>.lpf. This fixes #49. --- pyfpga/templates/diamond.jinja | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpga/templates/diamond.jinja b/pyfpga/templates/diamond.jinja index 3a191524..175be04d 100644 --- a/pyfpga/templates/diamond.jinja +++ b/pyfpga/templates/diamond.jinja @@ -25,7 +25,7 @@ prj_src add {% if 'lib' in attr %}-work {{ attr.lib }}{% else %}{% endif %} {{ n # 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 <constraints-file>` multiple times. -set fileId [open diamond.lpf "w"] +set fileId [open {{ project }}.lpf "w"] {% for name, attr in constraints.items() %} set fp [open "{{ name }}" r] set file_data [read $fp] From b2cd5cb82b26dee68fd03d1fe0b5210617a04cc2 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 22 Aug 2024 22:44:00 -0300 Subject: [PATCH 204/248] openflow: use the project name as the name for the combined constraints --- pyfpga/templates/openflow.jinja | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja index 2cafa31d..861a0917 100644 --- a/pyfpga/templates/openflow.jinja +++ b/pyfpga/templates/openflow.jinja @@ -37,7 +37,7 @@ read_verilog -defer -sv {{ name }} chparam{% for key, value in params.items() %} -set {{ key }} {{ value }}{% endfor %} {% endif %} -{% if top%} +{% if top %} synth -top {{ top }} {% endif %} @@ -71,10 +71,10 @@ synth_{{ family }} -top {{ top }} -json {{ project }}.json CONSTRAINTS="{{ constraints | join(' ') }}" {% if family == 'ice40' %} -if [ -n "$CONSTRAINTS" ]; then - cat $CONSTRAINTS > constraints.pcf - CONSTRAINT="--pcf constraints.pcf" -fi +{% 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 @@ -86,10 +86,10 @@ icetime -d {{ device }} -mtr {{ project }}.rpt {{ project }}.asc {% endif %} {% if family == 'ecp5' %} -if [ -n "$CONSTRAINTS" ]; then - cat $CONSTRAINTS > constraints.lpf - CONSTRAINT="--lpf constraints.lpf" -fi +{% 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 From e87cde86f3481268fed37bd5dc546c41b4c0f78f Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Thu, 22 Aug 2024 22:49:39 -0300 Subject: [PATCH 205/248] openflow: add missing comments --- pyfpga/templates/openflow.jinja | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja index 861a0917..088a77dc 100644 --- a/pyfpga/templates/openflow.jinja +++ b/pyfpga/templates/openflow.jinja @@ -15,15 +15,15 @@ $DOCKER hdlc/ghdl:yosys /bin/bash -c " {% if hooks %}{{ hooks.presyn | join('\n') }}{% endif %} yosys -Q -m ghdl -p ' -{% if includes %} +{% if includes %}# Verilog Includes verilog_defaults -add{% for path in includes %} -I{{ path }}{% endfor %} {% endif %} -{% if defines %} +{% if defines %}# Verilog Defines verilog_defines{% for key, value in defines.items() %} -D{{ key }}={{ value }}{% endfor %} {% endif %} -{% if files %} +{% if files %}# Files inclusion {% for name, attr in files.items() %} {% if attr.hdl == "vlog" %} read_verilog -defer {{ name }} @@ -33,11 +33,11 @@ read_verilog -defer -sv {{ name }} {% endfor %} {% endif %} -{% if params %} +{% if params %}# Verilog Parameters / VHDL Generics chparam{% for key, value in params.items() %} -set {{ key }} {{ value }}{% endfor %} {% endif %} -{% if top %} +{% if top %}# Top-level specification synth -top {{ top }} {% endif %} From 93d1bb08ad455f09f9982341fa5d9e90d8457aa1 Mon Sep 17 00:00:00 2001 From: Benjamin ZIEGLER <Benjamin.Ziegler@urbanandmainlines.com> Date: Fri, 16 Aug 2024 14:13:11 +0200 Subject: [PATCH 206/248] Print actual data when adding configurations to a project --- pyfpga/project.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index 86d9f267..0b7298b8 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -60,7 +60,7 @@ def set_part(self, name): :param name: FPGA part name :type name: str """ - self.logger.debug('Executing set_part') + self.logger.debug(f'Executing set_part: {name}') self.data['part'] = name def add_include(self, path): @@ -72,7 +72,7 @@ def add_include(self, path): :type name: str :raises NotADirectoryError: if path is not a directory """ - self.logger.debug('Executing add_include') + self.logger.debug(f'Executing add_include: {path}') path = Path(path).resolve() if not path.is_dir(): raise NotADirectoryError(path) @@ -98,7 +98,7 @@ def add_slog(self, pathname): :type pathname: str :raises FileNotFoundError: when pathname is not found """ - self.logger.debug('Executing add_slog') + self.logger.debug(f'Executing add_slog: {pathname}') self._add_file(pathname, 'slog') def add_vhdl(self, pathname, lib=None): @@ -110,7 +110,8 @@ def add_vhdl(self, pathname, lib=None): :type lib: str, optional :raises FileNotFoundError: when pathname is not found """ - self.logger.debug('Executing add_vhdl') + lib_str = 'default library' if lib is None else lib + self.logger.debug(f'Executing add_vhdl: {lib_str} : {pathname}') self._add_file(pathname, 'vhdl', lib) def add_vlog(self, pathname): @@ -120,7 +121,7 @@ def add_vlog(self, pathname): :type pathname: str :raises FileNotFoundError: when pathname is not found """ - self.logger.debug('Executing add_vlog') + self.logger.debug(f'Executing add_vlog: {pathname}') self._add_file(pathname, 'vlog') def add_cons(self, path): @@ -130,7 +131,7 @@ def add_cons(self, path): :type pathname: str :raises FileNotFoundError: if path is not found """ - self.logger.debug('Executing add_cons') + self.logger.debug(f'Executing add_cons: {path}') path = Path(path).resolve() if not path.is_file(): raise FileNotFoundError(path) @@ -145,7 +146,7 @@ def add_param(self, name, value): :param value: parameter/generic value :type name: str """ - self.logger.debug('Executing add_param') + self.logger.debug(f'Executing add_param: {name} : {value}') self.data.setdefault('params', {})[name] = value def add_define(self, name, value): @@ -156,7 +157,7 @@ def add_define(self, name, value): :param value: define value :type name: str """ - self.logger.debug('Executing add_define') + self.logger.debug(f'Executing add_define: {name} : {value}') self.data.setdefault('defines', {})[name] = value def add_fileset(self, pathname): @@ -166,7 +167,7 @@ def add_fileset(self, pathname): :type pathname: str :raises FileNotFoundError: when pathname is not found """ - self.logger.debug('Executing add_fileset') + self.logger.debug(f'Executing add_fileset: {pathname}') if not os.path.exists(pathname): raise FileNotFoundError(pathname) raise NotImplementedError() @@ -177,7 +178,7 @@ def set_top(self, name): :param name: top-level name :type name: str """ - self.logger.debug('Executing set_top') + self.logger.debug(f'Executing set_top: {name}') self.data['top'] = name def add_hook(self, stage, hook): @@ -191,7 +192,7 @@ def add_hook(self, stage, hook): :type hook: str :raises ValueError: when stage is invalid """ - self.logger.debug('Executing add_hook') + self.logger.debug(f'Executing add_hook: {stage} : {hook}') stages = [ 'precfg', 'postcfg', 'presyn', 'postsyn', 'prepar', 'postpar', 'prebit', 'postbit' From 1bdbb64fefbe2ad186039aea279dffc58d79b695 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 28 Aug 2024 22:13:28 -0300 Subject: [PATCH 207/248] Fix linting issue --- pyfpga/project.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pyfpga/project.py b/pyfpga/project.py index 0b7298b8..b6061b0f 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -60,7 +60,7 @@ def set_part(self, name): :param name: FPGA part name :type name: str """ - self.logger.debug(f'Executing set_part: {name}') + self.logger.debug('Executing set_part: %s', name) self.data['part'] = name def add_include(self, path): @@ -72,7 +72,7 @@ def add_include(self, path): :type name: str :raises NotADirectoryError: if path is not a directory """ - self.logger.debug(f'Executing add_include: {path}') + self.logger.debug('Executing add_include: %s', path) path = Path(path).resolve() if not path.is_dir(): raise NotADirectoryError(path) @@ -98,7 +98,7 @@ def add_slog(self, pathname): :type pathname: str :raises FileNotFoundError: when pathname is not found """ - self.logger.debug(f'Executing add_slog: {pathname}') + self.logger.debug('Executing add_slog: %s', pathname) self._add_file(pathname, 'slog') def add_vhdl(self, pathname, lib=None): @@ -111,7 +111,7 @@ def add_vhdl(self, pathname, lib=None): :raises FileNotFoundError: when pathname is not found """ lib_str = 'default library' if lib is None else lib - self.logger.debug(f'Executing add_vhdl: {lib_str} : {pathname}') + self.logger.debug('Executing add_vhdl: %s : %s', lib_str, pathname) self._add_file(pathname, 'vhdl', lib) def add_vlog(self, pathname): @@ -121,7 +121,7 @@ def add_vlog(self, pathname): :type pathname: str :raises FileNotFoundError: when pathname is not found """ - self.logger.debug(f'Executing add_vlog: {pathname}') + self.logger.debug('Executing add_vlog: %s', pathname) self._add_file(pathname, 'vlog') def add_cons(self, path): @@ -131,7 +131,7 @@ def add_cons(self, path): :type pathname: str :raises FileNotFoundError: if path is not found """ - self.logger.debug(f'Executing add_cons: {path}') + self.logger.debug('Executing add_cons: %s', path) path = Path(path).resolve() if not path.is_file(): raise FileNotFoundError(path) @@ -146,7 +146,7 @@ def add_param(self, name, value): :param value: parameter/generic value :type name: str """ - self.logger.debug(f'Executing add_param: {name} : {value}') + self.logger.debug('Executing add_param: %s : %s', name, value) self.data.setdefault('params', {})[name] = value def add_define(self, name, value): @@ -157,7 +157,7 @@ def add_define(self, name, value): :param value: define value :type name: str """ - self.logger.debug(f'Executing add_define: {name} : {value}') + self.logger.debug('Executing add_define: %s : %s', name, value) self.data.setdefault('defines', {})[name] = value def add_fileset(self, pathname): @@ -167,7 +167,7 @@ def add_fileset(self, pathname): :type pathname: str :raises FileNotFoundError: when pathname is not found """ - self.logger.debug(f'Executing add_fileset: {pathname}') + self.logger.debug('Executing add_fileset: %s', pathname) if not os.path.exists(pathname): raise FileNotFoundError(pathname) raise NotImplementedError() @@ -178,7 +178,7 @@ def set_top(self, name): :param name: top-level name :type name: str """ - self.logger.debug(f'Executing set_top: {name}') + self.logger.debug('Executing set_top: %s', name) self.data['top'] = name def add_hook(self, stage, hook): @@ -192,7 +192,7 @@ def add_hook(self, stage, hook): :type hook: str :raises ValueError: when stage is invalid """ - self.logger.debug(f'Executing add_hook: {stage} : {hook}') + self.logger.debug('Executing add_hook: %s : %s', stage, hook) stages = [ 'precfg', 'postcfg', 'presyn', 'postsyn', 'prepar', 'postpar', 'prebit', 'postbit' From d584bce2894632bf6c6739be408b4467eccd1cf3 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 30 Aug 2024 19:04:20 -0300 Subject: [PATCH 208/248] docs: add generation timestamp --- docs/index.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/index.rst b/docs/index.rst index 3e8dfcb0..685e7ac0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -16,3 +16,9 @@ PyFPGA's documentation tools internals extending + +.. |timestamp| date:: %Y-%m-%d %H:%M (%Z) + +.. note:: + + Documentation generated on |timestamp|. From d866f8bcf0a9be8e254abc2fa2f097fecb43d805 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 30 Aug 2024 20:11:04 -0300 Subject: [PATCH 209/248] openflow: re-added Synthesis for Xilinx devices --- pyfpga/templates/openflow.jinja | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja index 088a77dc..a77a25fd 100644 --- a/pyfpga/templates/openflow.jinja +++ b/pyfpga/templates/openflow.jinja @@ -37,35 +37,24 @@ read_verilog -defer -sv {{ name }} chparam{% for key, value in params.items() %} -set {{ key }} {{ value }}{% endfor %} {% endif %} -{% if top %}# Top-level specification +# 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 %} - -synth_{{ family }} -top {{ top }} -json {{ project }}.json ' {% if hooks %}{{ hooks.postsyn | join('\n') }}{% endif %} " {% endif %} -{# -#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 -#} - {% if 'par' in steps %}# Place and Route CONSTRAINTS="{{ constraints | join(' ') }}" From 5ae1a819a4e66d5f1948f196ee40cc7e44d4bc4e Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 30 Aug 2024 20:21:33 -0300 Subject: [PATCH 210/248] Added Diamond into regress.sh --- examples/projects/regress.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/projects/regress.sh b/examples/projects/regress.sh index 6af2f12b..22c8f9e8 100644 --- a/examples/projects/regress.sh +++ b/examples/projects/regress.sh @@ -4,6 +4,7 @@ set -e declare -A TOOLS +TOOLS["diamond"]="brevia2" TOOLS["ise"]="s6micro nexys3" TOOLS["libero"]="maker" TOOLS["openflow"]="icestick edu-ciaa orangecrab ecp5evn" @@ -19,7 +20,7 @@ for TOOL in "${!TOOLS[@]}"; do if [[ "$TOOL" == "ise" && "$SOURCE" == "slog" ]]; then continue fi - if [[ "$TOOL" == "openflow" && "$SOURCE" != "vlog" ]]; then + if [[ "$TOOL" == "openflow" && "$SOURCE" == "vhdl" ]]; then continue fi echo "> $TOOL - $BOARD - $SOURCE" From 024a89a29a01ebc8a7d44553e7bce68e99ad0efe Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 30 Aug 2024 20:23:05 -0300 Subject: [PATCH 211/248] examples: modify output directory of projects --- examples/projects/diamond.py | 2 +- examples/projects/ise.py | 3 ++- examples/projects/libero.py | 2 +- examples/projects/openflow.py | 3 ++- examples/projects/quartus.py | 3 ++- examples/projects/vivado.py | 3 ++- 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/examples/projects/diamond.py b/examples/projects/diamond.py index 4f5a85de..0128d7c3 100644 --- a/examples/projects/diamond.py +++ b/examples/projects/diamond.py @@ -17,7 +17,7 @@ ) args = parser.parse_args() -prj = Diamond(odir='../build/diamond') +prj = Diamond(odir=f'results/diamond/{args.source}/{args.board}') if args.board == 'brevia2': prj.set_part('LFXP2-5E-5TN144C') diff --git a/examples/projects/ise.py b/examples/projects/ise.py index 38d886c1..279a5db0 100644 --- a/examples/projects/ise.py +++ b/examples/projects/ise.py @@ -17,7 +17,7 @@ ) args = parser.parse_args() -prj = Ise(odir='../build/ise') +prj = Ise(odir=f'results/ise/{args.source}/{args.board}') if args.board == 's6micro': prj.set_part('xc6slx9-2-csg324') @@ -35,6 +35,7 @@ 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') diff --git a/examples/projects/libero.py b/examples/projects/libero.py index 4edd978e..dd2c5861 100644 --- a/examples/projects/libero.py +++ b/examples/projects/libero.py @@ -16,7 +16,7 @@ ) args = parser.parse_args() -prj = Libero(odir='../build/libero') +prj = Libero(odir=f'results/libero/{args.source}/{args.board}') if args.board == 'maker': prj.set_part('m2s010-1-tq144') diff --git a/examples/projects/openflow.py b/examples/projects/openflow.py index 0b1242b3..56a6b2ab 100644 --- a/examples/projects/openflow.py +++ b/examples/projects/openflow.py @@ -18,7 +18,7 @@ ) args = parser.parse_args() -prj = Openflow(odir='../build/openflow') +prj = Openflow(odir=f'results/openflow/{args.source}/{args.board}') if args.board == 'icestick': prj.set_part('hx1k-tq144') @@ -44,6 +44,7 @@ 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') diff --git a/examples/projects/quartus.py b/examples/projects/quartus.py index 02ed76d3..eb7e889e 100644 --- a/examples/projects/quartus.py +++ b/examples/projects/quartus.py @@ -17,7 +17,7 @@ ) args = parser.parse_args() -prj = Quartus(odir='../build/quartus') +prj = Quartus(odir=f'results/quartus/{args.source}/{args.board}') if args.board == 'de10nano': prj.set_part('5CSEBA6U23I7') @@ -29,6 +29,7 @@ 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') diff --git a/examples/projects/vivado.py b/examples/projects/vivado.py index 35ccecc7..af497b91 100644 --- a/examples/projects/vivado.py +++ b/examples/projects/vivado.py @@ -17,7 +17,7 @@ ) args = parser.parse_args() -prj = Vivado(odir='../build/vivado') +prj = Vivado(odir=f'results/vivado/{args.source}/{args.board}') if args.board == 'zybo': prj.set_part('xc7z010-1-clg400') @@ -35,6 +35,7 @@ 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') From e63851de95b1dc13e4a7c5a7786e0506f979e25d Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 30 Aug 2024 20:27:22 -0300 Subject: [PATCH 212/248] examples: remove odir specification in diamond (hooks) --- examples/hooks/diamond.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/hooks/diamond.py b/examples/hooks/diamond.py index 389b9a60..ce4f3558 100644 --- a/examples/hooks/diamond.py +++ b/examples/hooks/diamond.py @@ -1,8 +1,8 @@ -"""Diamond example hooks.""" +"""Diamond hooks examples.""" from pyfpga.diamond import Diamond -prj = Diamond(odir='../build/diamond') +prj = Diamond() hooks = { "reports": """ From 0d95e0178f22262f5cfb3a818060a83711a70085 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 30 Aug 2024 21:16:56 -0300 Subject: [PATCH 213/248] Add to remove the results directories --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index fb528da7..88a8c3da 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,9 @@ test: clean: py3clean . cd docs; make clean - rm -fr build .pytest_cache + rm -fr .pytest_cache + rm -fr `find . -name results` + rm -fr `find . -name __pycache__` submodule-init: git submodule update --init --recursive From 99f235e3711c8b1b96defb2f6b3cb89978b563ab Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 30 Aug 2024 21:24:59 -0300 Subject: [PATCH 214/248] docs: minor changes in the generation --- .github/workflows/docs.yml | 2 +- docs/Makefile | 2 +- docs/conf.py | 8 ++++---- docs/helpers.rst | 6 +++--- docs/{_static => images}/logo.png | Bin docs/{_static => images}/schema.png | Bin docs/index.rst | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) rename docs/{_static => images}/logo.png (100%) rename docs/{_static => images}/schema.png (100%) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 0f17d958..85963a48 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -19,4 +19,4 @@ jobs: uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: docs/_build/html + publish_dir: docs/build/html diff --git a/docs/Makefile b/docs/Makefile index a9325d75..13352008 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,7 +1,7 @@ SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build SOURCEDIR = . -BUILDDIR = _build +BUILDDIR = build HELPERS = $(BUILDDIR)/hdl2bit $(BUILDDIR)/prj2bit $(BUILDDIR)/bitprog help: diff --git a/docs/conf.py b/docs/conf.py index 3e57d268..530ce742 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -8,8 +8,8 @@ # -- Project information ----------------------------------------------------- project = 'PyFPGA' -copyright = '2024, Rodrigo Alejandro Melo' -author = 'Rodrigo Alejandro Melo' +copyright = '2016-2024, PyFPGA Project' +author = 'PyFPGA contributors' # -- General configuration --------------------------------------------------- @@ -31,9 +31,9 @@ 'repositoy': ('https://github.com/PyFPGA/pyfpga/tree/main/%s', None) } -exclude_patterns = ['_build', 'wip'] +exclude_patterns = ['build'] # -- Options for HTML output ------------------------------------------------- html_theme = 'sphinx_rtd_theme' -html_static_path = ['_static'] +html_static_path = ['images'] diff --git a/docs/helpers.rst b/docs/helpers.rst index 542deb27..619f2c35 100644 --- a/docs/helpers.rst +++ b/docs/helpers.rst @@ -4,14 +4,14 @@ Helpers hdl2bit ------- -.. literalinclude:: _build/hdl2bit +.. literalinclude:: build/hdl2bit prj2bit ------- -.. literalinclude:: _build/prj2bit +.. literalinclude:: build/prj2bit bitprog ------- -.. literalinclude:: _build/bitprog +.. literalinclude:: build/bitprog diff --git a/docs/_static/logo.png b/docs/images/logo.png similarity index 100% rename from docs/_static/logo.png rename to docs/images/logo.png diff --git a/docs/_static/schema.png b/docs/images/schema.png similarity index 100% rename from docs/_static/schema.png rename to docs/images/schema.png diff --git a/docs/index.rst b/docs/index.rst index 685e7ac0..3bfdf197 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,7 +1,7 @@ PyFPGA's documentation ====================== -.. image:: _static/logo.png +.. image:: images/logo.png :width: 200 px :align: center :target: https://github.com/PyFPGA/pyfpga From 7fc1ab9017328b405841ccc704273f47484a5461 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Fri, 30 Aug 2024 21:57:42 -0300 Subject: [PATCH 215/248] Removed an unneeded comment --- pyfpga/templates/libero.jinja | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index aaddbf9b..c64d8a5d 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -14,7 +14,7 @@ set_device -family {{ family }} -die {{ device }} -package {{ package }} -speed {% if hooks %}{{ hooks.precfg | join('\n') }}{% endif %} -{% if includes %}# Verilog Includes (Libero) +{% if includes %}# Verilog Includes set_global_include_path_order -paths "{{ includes | join(' ') }}" {% endif %} From ed36eaddd0daad22cbb401b64b689089604932b9 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 31 Aug 2024 08:41:17 -0300 Subject: [PATCH 216/248] tests: moved from projects/support.py to simply support.py --- tests/{projects => }/support.py | 40 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) rename tests/{projects => }/support.py (70%) diff --git a/tests/projects/support.py b/tests/support.py similarity index 70% rename from tests/projects/support.py rename to tests/support.py index 68bc88b1..128a17c5 100644 --- a/tests/projects/support.py +++ b/tests/support.py @@ -17,16 +17,16 @@ print('INFO: checking basic Verilog Support') prj = Factory(args.tool) -prj.add_vlog('../../examples/sources/vlog/blink.v') +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.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_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') @@ -36,7 +36,7 @@ try: print('INFO: checking Verilog Includes Support') prj = Factory(args.tool) - prj.add_vlog('../../examples/sources/vlog/*.v') + prj.add_vlog('../examples/sources/vlog/*.v') prj.set_top('Top') prj.add_define('DEFINE1', '1') prj.add_define('DEFINE2', '1') @@ -52,10 +52,10 @@ try: print('INFO: checking Verilog Defines Support') prj = Factory(args.tool) - prj.add_vlog('../../examples/sources/vlog/*.v') + 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_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') @@ -68,10 +68,10 @@ try: print('INFO: checking Verilog Parameters Support') prj = Factory(args.tool) - prj.add_vlog('../../examples/sources/vlog/*.v') + 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_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') @@ -84,16 +84,16 @@ 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.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.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_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') @@ -103,14 +103,14 @@ 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.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.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') @@ -119,8 +119,8 @@ 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.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') From 9188bd8f795af65458f6a380f0d5f012d5212ee9 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 31 Aug 2024 09:04:30 -0300 Subject: [PATCH 217/248] Add to specify the tool to run in regress.sh --- examples/projects/regress.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/projects/regress.sh b/examples/projects/regress.sh index 22c8f9e8..be6219a0 100644 --- a/examples/projects/regress.sh +++ b/examples/projects/regress.sh @@ -13,7 +13,12 @@ TOOLS["vivado"]="zybo arty" SOURCES=("vlog" "vhdl" "slog") +SPECIFIED_TOOL=$1 + 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 From f203de16cc71fae6a6307f3eb159517589607d81 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 31 Aug 2024 09:05:23 -0300 Subject: [PATCH 218/248] Removed unused targets and added all --- Makefile | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 88a8c3da..892fdcb4 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,8 @@ .PHONY: docs +all: docs lint test + docs: cd docs; make html @@ -19,9 +21,3 @@ clean: rm -fr .pytest_cache rm -fr `find . -name results` rm -fr `find . -name __pycache__` - -submodule-init: - git submodule update --init --recursive - -submodule-update: - cd examples/resources; git checkout main; git pull From a4ffd5df78f53fdb96276e695c194e8f25ceda1d Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 31 Aug 2024 09:11:07 -0300 Subject: [PATCH 219/248] docs: improve the extending section --- docs/extending.rst | 76 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 20 deletions(-) diff --git a/docs/extending.rst b/docs/extending.rst index 03c995cf..18956259 100644 --- a/docs/extending.rst +++ b/docs/extending.rst @@ -1,40 +1,76 @@ Extending ========= -1. Add support for the new tool: +.. note:: -.. code-block:: python + All <TOOL> classes inherit from 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/<NEWTOOL>.jinja pyfpga/templates/<NEWTOOL>-prog.jinja pyfpga/<NEWTOOL>.py + pyfpga/factory.py # UPDATE + pyfpga/helpers/prj2bit.py # UPDATE -2. Include the new tool on Factory: +Add tests and a tool mock-up +---------------------------- -.. code-block:: python +.. code-block:: bash - pyfpga/factory.py + tests/test_tools.py # UPDATE + tests/support.py # UPDATE if exceptions are needed + tests/mocks/<NEWCOMMAND> -3. Add tests and a tool mock-up: +Add examples +------------ -.. code-block:: python +.. code-block:: bash - tests/test_tools.py - tests/mocks/<NEWTOOL_EXECUTABLE> + examples/sources/cons/<NEWBOARD>/timing.<EXT> + examples/sources/cons/<NEWBOARD>/clk.<EXT> + examples/sources/cons/<NEWBOARD>/led.<EXT> + examples/projects/<NEWTOOL>.py + examples/projects/regress.sh # UPDATE + examples/helpers/<NEWTOOL>.sh + examples/hooks/<NEWTOOL>.py # OPTIONAL -4. Updated the project's documentation: +Verify the code +--------------- -.. code-block:: python +Run it at the root of the repo. - README.md - docs +.. code-block:: bash -5. [OPTIONAL] Add examples: + make docs + make lint + make test -.. code-block:: python +.. tip:: - examples/sources/cons/<NEWBOARD>/timing.<EXT> - examples/sources/cons/<NEWBOARD>/clk.<EXT> - examples/sources/cons/<NEWBOARD>/led.<EXT> - examples/projects/<NEWTOOL>.py - examples/hooks/<NEWTOOL>.py + 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 <NEWTOOL> + cd ../../tests/ + python3 support.py --tool <NEWTOOL> + +Updated the documentation +------------------------- + +.. code-block:: bash + + README.md + docs/intro.rst + docs/tools.rst From 8f7e89a4a554714ce1089765c94bb9469f3f1f12 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 31 Aug 2024 09:29:59 -0300 Subject: [PATCH 220/248] examples: renamed yosys as misc --- examples/{yosys/ise.py => misc/yosys-ise.py} | 0 examples/{yosys/vivado.py => misc/yosys-vivado.py} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename examples/{yosys/ise.py => misc/yosys-ise.py} (100%) rename examples/{yosys/vivado.py => misc/yosys-vivado.py} (100%) diff --git a/examples/yosys/ise.py b/examples/misc/yosys-ise.py similarity index 100% rename from examples/yosys/ise.py rename to examples/misc/yosys-ise.py diff --git a/examples/yosys/vivado.py b/examples/misc/yosys-vivado.py similarity index 100% rename from examples/yosys/vivado.py rename to examples/misc/yosys-vivado.py From 578ee491c9cb75949ec563b449521bdcb7804fa8 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 31 Aug 2024 09:31:10 -0300 Subject: [PATCH 221/248] examples: update/simplify README.md --- examples/README.md | 60 +++++++--------------------------------------- 1 file changed, 8 insertions(+), 52 deletions(-) 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). From fbf80e0b1834f861fc836eb4e132184071de1c2f Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 31 Aug 2024 10:08:55 -0300 Subject: [PATCH 222/248] diamondc: removed unused import --- tests/mocks/diamondc | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/mocks/diamondc b/tests/mocks/diamondc index 1beac9c1..50303cb3 100755 --- a/tests/mocks/diamondc +++ b/tests/mocks/diamondc @@ -8,7 +8,6 @@ import argparse import subprocess -import sys parser = argparse.ArgumentParser() From 884fda8490a2b9e2d9e69b5ab7e983868f2688d4 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 31 Aug 2024 10:09:33 -0300 Subject: [PATCH 223/248] Simplified how to clean docs --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 892fdcb4..2f2402c4 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ test: clean: py3clean . - cd docs; make clean + rm -fr docs/build rm -fr .pytest_cache rm -fr `find . -name results` rm -fr `find . -name __pycache__` From 7851b97872258d7728f6be271b6f83f21ca420a5 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 31 Aug 2024 10:25:45 -0300 Subject: [PATCH 224/248] ci: modified when to trigger the docs actions --- .github/workflows/docs.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 85963a48..1db219c9 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -2,6 +2,8 @@ name: 'docs' on: push: + paths: + - 'docs/**' branches: # - main From c7e5369def5d4fb26dfcee20ca5f7b33dea40177 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 31 Aug 2024 13:21:24 -0300 Subject: [PATCH 225/248] openflow: re-added VHDL support (WIP) --- pyfpga/templates/openflow.jinja | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja index a77a25fd..b5b6d96c 100644 --- a/pyfpga/templates/openflow.jinja +++ b/pyfpga/templates/openflow.jinja @@ -13,6 +13,17 @@ 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 %}# 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 %} +ghdl -a --std=08 -fsynopsys -fexplicit -frelaxed /home/rodrigo/repos-ram/PyFPGA/pyfpga/examples/sources/vhdl/top.vhdl + yosys -Q -m ghdl -p ' {% if includes %}# Verilog Includes @@ -29,13 +40,21 @@ verilog_defines{% for key, value in defines.items() %} -D{{ key }}={{ value }}{% read_verilog -defer {{ name }} {% elif attr.hdl == "slog" %} read_verilog -defer -sv {{ name }} +{% elif attr.hdl == "vhdl" %} +{% if loop.first %} +# VHDL Generics +{% set generics = "-gFREQ=125000000 -gSECS=1" %} +ghdl {{ gflags }} {{ generics }} {{ top }} +{% endif %} {% endif %} {% endfor %} {% endif %} -{% if params %}# Verilog Parameters / VHDL Generics +{# +{% if params %}# Verilog Parameters chparam{% for key, value in params.items() %} -set {{ key }} {{ value }}{% endfor %} {% endif %} +#} # Top-level specification and Syntesis {% if family in ['ice40', 'ecp5'] %} From 01991d7a0fe600b778b20545015dad3e99bee740 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 31 Aug 2024 13:36:01 -0300 Subject: [PATCH 226/248] openflow: add generics support --- pyfpga/templates/openflow.jinja | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja index b5b6d96c..a0c18dd6 100644 --- a/pyfpga/templates/openflow.jinja +++ b/pyfpga/templates/openflow.jinja @@ -42,19 +42,18 @@ read_verilog -defer {{ name }} read_verilog -defer -sv {{ name }} {% elif attr.hdl == "vhdl" %} {% if loop.first %} -# VHDL Generics -{% set generics = "-gFREQ=125000000 -gSECS=1" %} -ghdl {{ gflags }} {{ generics }} {{ top }} +{% if params %}# VHDL Generics +ghdl {{ gflags }}{% for key, value in params.items() %} -g{{ key }}={{ value }}{% endfor %} {{ top }} +{% else %} +ghdl {{ gflags }} {{ top }} {% endif %} {% endif %} -{% endfor %} {% endif %} - -{# -{% if params %}# Verilog Parameters +{% 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'] %} From 2864ced35c7468daa0c803185db6b2d9e4770f0c Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 2 Sep 2024 19:38:14 -0300 Subject: [PATCH 227/248] openflow: properly re-added VHDL support Closes #51 --- examples/projects/regress.sh | 3 --- pyfpga/project.py | 6 ++++-- pyfpga/templates/openflow.jinja | 5 ++--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/examples/projects/regress.sh b/examples/projects/regress.sh index be6219a0..45b7d848 100644 --- a/examples/projects/regress.sh +++ b/examples/projects/regress.sh @@ -25,9 +25,6 @@ for TOOL in "${!TOOLS[@]}"; do if [[ "$TOOL" == "ise" && "$SOURCE" == "slog" ]]; then continue fi - if [[ "$TOOL" == "openflow" && "$SOURCE" == "vhdl" ]]; then - continue - fi echo "> $TOOL - $BOARD - $SOURCE" python3 $TOOL.py --board $BOARD --source $SOURCE done diff --git a/pyfpga/project.py b/pyfpga/project.py index b6061b0f..10bb57ea 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -83,13 +83,15 @@ def _add_file(self, pathname, hdl=None, lib=None): if len(files) == 0: raise FileNotFoundError(pathname) for file in files: - path = Path(file).resolve() + path = Path(file).resolve().as_posix() attr = {} if hdl: attr['hdl'] = hdl if lib: attr['lib'] = lib - self.data.setdefault('files', {})[path.as_posix()] = attr + 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. diff --git a/pyfpga/templates/openflow.jinja b/pyfpga/templates/openflow.jinja index a0c18dd6..79977719 100644 --- a/pyfpga/templates/openflow.jinja +++ b/pyfpga/templates/openflow.jinja @@ -15,14 +15,13 @@ $DOCKER hdlc/ghdl:yosys /bin/bash -c " {% if hooks %}{{ hooks.presyn | join('\n') }}{% endif %} {% set gflags = '--std=08 -fsynopsys -fexplicit -frelaxed' %} -{% if files %}# Files inclusion +{% 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 %} -ghdl -a --std=08 -fsynopsys -fexplicit -frelaxed /home/rodrigo/repos-ram/PyFPGA/pyfpga/examples/sources/vhdl/top.vhdl yosys -Q -m ghdl -p ' @@ -34,7 +33,7 @@ verilog_defaults -add{% for path in includes %} -I{{ path }}{% endfor %} verilog_defines{% for key, value in defines.items() %} -D{{ key }}={{ value }}{% endfor %} {% endif %} -{% if files %}# Files inclusion +{% if files %}# VLOG Files inclusion {% for name, attr in files.items() %} {% if attr.hdl == "vlog" %} read_verilog -defer {{ name }} From aa29487869b751bed48cf167cdd2658e279a4da9 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 2 Sep 2024 20:34:54 -0300 Subject: [PATCH 228/248] docs: add a hint about file processing order --- docs/basic.rst | 35 ++++++++++++++++++++++++----------- docs/extending.rst | 2 +- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/docs/basic.rst b/docs/basic.rst index 5efcfccd..0b40c35f 100644 --- a/docs/basic.rst +++ b/docs/basic.rst @@ -4,7 +4,7 @@ Basic usage Project Configuration --------------------- -The first steps involve importing the necessary module to support the desired tool and instantiating the corresponding *class*: +The first steps involve importing the necessary module to support the desired tool and instantiating the corresponding ``class``: .. code-block:: python @@ -12,7 +12,9 @@ The first steps involve importing the necessary module to support the desired to prj = Vivado('PRJNAME', odir='OUTDIR') -In the example, we are using Vivado, specifying the optional parameter *project name* (*tool name* if omitted) and *output directory* (*results* by default). +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: @@ -32,9 +34,8 @@ HDL source files are added using one of the following methods: 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. - -For `add_vhdl`, you can also optionally specify a library name where the files will be included. +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:: @@ -43,6 +44,15 @@ For `add_vhdl`, you can also optionally specify a library name where the files w .. _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 @@ -82,7 +92,8 @@ After configuring the project, you can run the following to generate a bitstream prj.make() -By default, this method performs *project creation*, *synthesis*, *place and route*, and *bitstream generation*. +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 @@ -100,7 +111,8 @@ However, you can optionally specify both the initial and final stages, as follow .. note:: - After executing this method, you will find the file `<TOOL>.tcl` (or `sh` in some cases) in the output directory. + After executing this method, you will find the file ``<TOOL>.tcl`` + (``<TOOL>.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 @@ -112,13 +124,14 @@ The final step is programming the FPGA: 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). +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 `<TOOL>prog.tcl` (or `sh` in some cases) in the output directory. + After executing this method, you will find the file ``<TOOL>prog.tcl`` + (``<TOOL>-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 diff --git a/docs/extending.rst b/docs/extending.rst index 18956259..746ffc5d 100644 --- a/docs/extending.rst +++ b/docs/extending.rst @@ -3,7 +3,7 @@ Extending .. note:: - All <TOOL> classes inherit from project.py. + All <TOOL> classes inherit from ``Project`` (``project.py``). This is a guide on how to add support for a new TOOL. From 9a2df317a720e6899aad003a5b6bb38e16c73ed3 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 2 Sep 2024 21:29:43 -0300 Subject: [PATCH 229/248] docs: added a diagram about Openflow --- docs/images/Makefile | 15 +++ docs/images/openflow.dot | 28 +++++ docs/images/openflow.svg | 224 +++++++++++++++++++++++++++++++++++++++ docs/tools.rst | 4 + 4 files changed, 271 insertions(+) create mode 100644 docs/images/Makefile create mode 100644 docs/images/openflow.dot create mode 100644 docs/images/openflow.svg 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/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 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 2.43.0 (0) + --> +<!-- Title: openflow Pages: 1 --> +<svg width="396pt" height="476pt" + viewBox="0.00 0.00 395.50 476.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 472)"> +<title>openflow</title> +<polygon fill="white" stroke="transparent" points="-4,4 -4,-472 391.5,-472 391.5,4 -4,4"/> +<!-- GHDL --> +<g id="node1" class="node"> +<title>GHDL</title> +<polygon fill="none" stroke="black" points="60,-414 0,-414 0,-378 60,-378 60,-414"/> +<text text-anchor="middle" x="30" y="-392.3" font-family="Times,serif" font-size="14.00">GHDL</text> +</g> +<!-- ghdl-yosys-plugin --> +<g id="node2" class="node"> +<title>ghdl-yosys-plugin</title> +<polygon fill="none" stroke="black" points="220,-414 78,-414 78,-378 220,-378 220,-414"/> +<text text-anchor="middle" x="149" y="-392.3" font-family="Times,serif" font-size="14.00">ghdl-yosys-plugin</text> +</g> +<!-- GHDL->ghdl-yosys-plugin --> +<g id="edge3" class="edge"> +<title>GHDL->ghdl-yosys-plugin</title> +<path fill="none" stroke="black" d="M60.21,-396C62.7,-396 65.19,-396 67.68,-396"/> +<polygon fill="black" stroke="black" points="67.9,-399.5 77.9,-396 67.9,-392.5 67.9,-399.5"/> +</g> +<!-- Yosys --> +<g id="node3" class="node"> +<title>Yosys</title> +<polygon fill="none" stroke="black" points="294,-414 238,-414 238,-378 294,-378 294,-414"/> +<text text-anchor="middle" x="266" y="-392.3" font-family="Times,serif" font-size="14.00">Yosys</text> +</g> +<!-- ghdl-yosys-plugin->Yosys --> +<g id="edge4" class="edge"> +<title>ghdl-yosys-plugin->Yosys</title> +<path fill="none" stroke="black" d="M220.3,-396C222.76,-396 225.22,-396 227.68,-396"/> +<polygon fill="black" stroke="black" points="227.79,-399.5 237.79,-396 227.79,-392.5 227.79,-399.5"/> +</g> +<!-- .json --> +<g id="node15" class="node"> +<title>.json</title> +<ellipse fill="none" stroke="black" cx="266" cy="-342" rx="32.49" ry="18"/> +<text text-anchor="middle" x="266" y="-338.3" font-family="Times,serif" font-size="14.00">.json</text> +</g> +<!-- Yosys->.json --> +<g id="edge6" class="edge"> +<title>Yosys->.json</title> +<path fill="none" stroke="black" d="M266,-377.97C266,-375.57 266,-373.06 266,-370.52"/> +<polygon fill="black" stroke="black" points="269.5,-370.24 266,-360.24 262.5,-370.24 269.5,-370.24"/> +</g> +<!-- nextpnr-ice40 --> +<g id="node4" class="node"> +<title>nextpnr-ice40</title> +<polygon fill="none" stroke="black" points="258,-306 142,-306 142,-270 258,-270 258,-306"/> +<text text-anchor="middle" x="200" y="-284.3" font-family="Times,serif" font-size="14.00">nextpnr-ice40</text> +</g> +<!-- .asc --> +<g id="node16" class="node"> +<title>.asc</title> +<ellipse fill="none" stroke="black" cx="200" cy="-234" rx="28.7" ry="18"/> +<text text-anchor="middle" x="200" y="-230.3" font-family="Times,serif" font-size="14.00">.asc</text> +</g> +<!-- nextpnr-ice40->.asc --> +<g id="edge9" class="edge"> +<title>nextpnr-ice40->.asc</title> +<path fill="none" stroke="black" d="M200,-269.97C200,-267.57 200,-265.06 200,-262.52"/> +<polygon fill="black" stroke="black" points="203.5,-262.24 200,-252.24 196.5,-262.24 203.5,-262.24"/> +</g> +<!-- nextpnr-ecp5 --> +<g id="node5" class="node"> +<title>nextpnr-ecp5</title> +<polygon fill="none" stroke="black" points="387.5,-306 276.5,-306 276.5,-270 387.5,-270 387.5,-306"/> +<text text-anchor="middle" x="332" y="-284.3" font-family="Times,serif" font-size="14.00">nextpnr-ecp5</text> +</g> +<!-- .config --> +<g id="node17" class="node"> +<title>.config</title> +<ellipse fill="none" stroke="black" cx="332" cy="-234" rx="41.69" ry="18"/> +<text text-anchor="middle" x="332" y="-230.3" font-family="Times,serif" font-size="14.00">.config</text> +</g> +<!-- nextpnr-ecp5->.config --> +<g id="edge10" class="edge"> +<title>nextpnr-ecp5->.config</title> +<path fill="none" stroke="black" d="M332,-269.97C332,-267.57 332,-265.06 332,-262.52"/> +<polygon fill="black" stroke="black" points="335.5,-262.24 332,-252.24 328.5,-262.24 335.5,-262.24"/> +</g> +<!-- icetime --> +<g id="node6" class="node"> +<title>icetime</title> +<polygon fill="none" stroke="black" points="167.5,-198 98.5,-198 98.5,-162 167.5,-162 167.5,-198"/> +<text text-anchor="middle" x="133" y="-176.3" font-family="Times,serif" font-size="14.00">icetime</text> +</g> +<!-- icepack --> +<g id="node7" class="node"> +<title>icepack</title> +<polygon fill="none" stroke="black" points="256,-198 186,-198 186,-162 256,-162 256,-198"/> +<text text-anchor="middle" x="221" y="-176.3" font-family="Times,serif" font-size="14.00">icepack</text> +</g> +<!-- bit-ice40 --> +<g id="node13" class="node"> +<title>bit-ice40</title> +<ellipse fill="none" stroke="black" cx="221" cy="-126" rx="27" ry="18"/> +<text text-anchor="middle" x="221" y="-122.3" font-family="Times,serif" font-size="14.00">.bit</text> +</g> +<!-- icepack->bit-ice40 --> +<g id="edge13" class="edge"> +<title>icepack->bit-ice40</title> +<path fill="none" stroke="black" d="M221,-161.97C221,-159.57 221,-157.06 221,-154.52"/> +<polygon fill="black" stroke="black" points="224.5,-154.24 221,-144.24 217.5,-154.24 224.5,-154.24"/> +</g> +<!-- iceprog --> +<g id="node8" class="node"> +<title>iceprog</title> +<polygon fill="none" stroke="black" points="256,-90 186,-90 186,-54 256,-54 256,-90"/> +<text text-anchor="middle" x="221" y="-68.3" font-family="Times,serif" font-size="14.00">iceprog</text> +</g> +<!-- ice40 --> +<g id="node12" class="node"> +<title>ice40</title> +<polygon fill="none" stroke="black" points="248.5,-36 197.5,-36 193.5,-32 193.5,0 244.5,0 248.5,-4 248.5,-36"/> +<polyline fill="none" stroke="black" points="244.5,-32 193.5,-32 "/> +<polyline fill="none" stroke="black" points="244.5,-32 244.5,0 "/> +<polyline fill="none" stroke="black" points="244.5,-32 248.5,-36 "/> +<text text-anchor="middle" x="221" y="-14.3" font-family="Times,serif" font-size="14.00">ice40</text> +</g> +<!-- iceprog->ice40 --> +<g id="edge15" class="edge"> +<title>iceprog->ice40</title> +<path fill="none" stroke="black" d="M221,-53.97C221,-51.57 221,-49.06 221,-46.52"/> +<polygon fill="black" stroke="black" points="224.5,-46.24 221,-36.24 217.5,-46.24 224.5,-46.24"/> +</g> +<!-- eccpack --> +<g id="node9" class="node"> +<title>eccpack</title> +<polygon fill="none" stroke="black" points="368.5,-198 295.5,-198 295.5,-162 368.5,-162 368.5,-198"/> +<text text-anchor="middle" x="332" y="-176.3" font-family="Times,serif" font-size="14.00">eccpack</text> +</g> +<!-- bit-ecp5 --> +<g id="node14" class="node"> +<title>bit-ecp5</title> +<ellipse fill="none" stroke="black" cx="332" cy="-126" rx="27" ry="18"/> +<text text-anchor="middle" x="332" y="-122.3" font-family="Times,serif" font-size="14.00">.bit</text> +</g> +<!-- eccpack->bit-ecp5 --> +<g id="edge17" class="edge"> +<title>eccpack->bit-ecp5</title> +<path fill="none" stroke="black" d="M332,-161.97C332,-159.57 332,-157.06 332,-154.52"/> +<polygon fill="black" stroke="black" points="335.5,-154.24 332,-144.24 328.5,-154.24 335.5,-154.24"/> +</g> +<!-- VHDL --> +<g id="node10" class="node"> +<title>VHDL</title> +<polygon fill="none" stroke="black" points="113.5,-468 60.5,-468 60.5,-432 119.5,-432 119.5,-462 113.5,-468"/> +<polyline fill="none" stroke="black" points="113.5,-468 113.5,-462 "/> +<polyline fill="none" stroke="black" points="119.5,-462 113.5,-462 "/> +<text text-anchor="middle" x="90" y="-446.3" font-family="Times,serif" font-size="14.00">VHDL</text> +</g> +<!-- VHDL->GHDL --> +<g id="edge1" class="edge"> +<title>VHDL->GHDL</title> +<path fill="none" stroke="black" d="M70.35,-431.97C66.25,-428.42 61.86,-424.61 57.53,-420.86"/> +<polygon fill="black" stroke="black" points="59.74,-418.15 49.9,-414.24 55.16,-423.44 59.74,-418.15"/> +</g> +<!-- VHDL->ghdl-yosys-plugin --> +<g id="edge2" class="edge"> +<title>VHDL->ghdl-yosys-plugin</title> +<path fill="none" stroke="black" d="M109.32,-431.97C113.35,-428.42 117.67,-424.61 121.93,-420.86"/> +<polygon fill="black" stroke="black" points="124.25,-423.48 129.44,-414.24 119.62,-418.23 124.25,-423.48"/> +</g> +<!-- Verilog --> +<g id="node11" class="node"> +<title>Verilog</title> +<polygon fill="none" stroke="black" points="293.5,-468 232.5,-468 232.5,-432 299.5,-432 299.5,-462 293.5,-468"/> +<polyline fill="none" stroke="black" points="293.5,-468 293.5,-462 "/> +<polyline fill="none" stroke="black" points="299.5,-462 293.5,-462 "/> +<text text-anchor="middle" x="266" y="-446.3" font-family="Times,serif" font-size="14.00">Verilog</text> +</g> +<!-- Verilog->Yosys --> +<g id="edge5" class="edge"> +<title>Verilog->Yosys</title> +<path fill="none" stroke="black" d="M266,-431.97C266,-429.57 266,-427.06 266,-424.52"/> +<polygon fill="black" stroke="black" points="269.5,-424.24 266,-414.24 262.5,-424.24 269.5,-424.24"/> +</g> +<!-- bit-ice40->iceprog --> +<g id="edge14" class="edge"> +<title>bit-ice40->iceprog</title> +<path fill="none" stroke="black" d="M221,-107.97C221,-105.57 221,-103.06 221,-100.52"/> +<polygon fill="black" stroke="black" points="224.5,-100.24 221,-90.24 217.5,-100.24 224.5,-100.24"/> +</g> +<!-- .json->nextpnr-ice40 --> +<g id="edge7" class="edge"> +<title>.json->nextpnr-ice40</title> +<path fill="none" stroke="black" d="M247.97,-326.79C242.4,-322.41 236.12,-317.46 229.98,-312.62"/> +<polygon fill="black" stroke="black" points="231.79,-309.59 221.77,-306.15 227.46,-315.09 231.79,-309.59"/> +</g> +<!-- .json->nextpnr-ecp5 --> +<g id="edge8" class="edge"> +<title>.json->nextpnr-ecp5</title> +<path fill="none" stroke="black" d="M284.03,-326.79C289.6,-322.41 295.88,-317.46 302.02,-312.62"/> +<polygon fill="black" stroke="black" points="304.54,-315.09 310.23,-306.15 300.21,-309.59 304.54,-315.09"/> +</g> +<!-- .asc->icetime --> +<g id="edge11" class="edge"> +<title>.asc->icetime</title> +<path fill="none" stroke="black" d="M182.75,-219.61C176.86,-215.04 170.08,-209.78 163.47,-204.65"/> +<polygon fill="black" stroke="black" points="165.3,-201.64 155.26,-198.27 161.01,-207.17 165.3,-201.64"/> +</g> +<!-- .asc->icepack --> +<g id="edge12" class="edge"> +<title>.asc->icepack</title> +<path fill="none" stroke="black" d="M206.76,-216.26C207.9,-213.44 209.1,-210.46 210.31,-207.46"/> +<polygon fill="black" stroke="black" points="213.61,-208.65 214.11,-198.06 207.12,-206.03 213.61,-208.65"/> +</g> +<!-- .config->eccpack --> +<g id="edge16" class="edge"> +<title>.config->eccpack</title> +<path fill="none" stroke="black" d="M332,-215.97C332,-213.57 332,-211.06 332,-208.52"/> +<polygon fill="black" stroke="black" points="335.5,-208.24 332,-198.24 328.5,-208.24 335.5,-208.24"/> +</g> +</g> +</svg> diff --git a/docs/tools.rst b/docs/tools.rst index 8839ac2d..1f2ee57b 100644 --- a/docs/tools.rst +++ b/docs/tools.rst @@ -94,6 +94,10 @@ Openflow 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. From 29ed35a312a7f3586b01c70ac8855d0e5a78f2f7 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 2 Sep 2024 21:52:16 -0300 Subject: [PATCH 230/248] ci: modified to trigger docs when the branch is main or dev --- .github/workflows/docs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 1db219c9..8d26b1af 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -5,7 +5,8 @@ on: paths: - 'docs/**' branches: -# - main + - main + - dev jobs: docs: From 9aa363f3b3b23d89d7947f5c228bed9b287e8173 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 2 Sep 2024 23:51:08 -0300 Subject: [PATCH 231/248] ci: add windows tests --- .github/workflows/test.yml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 40699986..0b33ba57 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,7 +4,7 @@ on: push: jobs: - test: + test-lin: strategy: matrix: os: ['ubuntu'] @@ -25,3 +25,20 @@ jobs: run: pip install . && pip install pytest - name: Run tests run: source tests/mocks/source-me.sh && make test + test-win: + runs-on: windows-latest + name: windows | 3.12 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: true + fetch-depth: 0 + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: 3.12 + - name: Install dependencies + run: pip install . && pip install pytest + - name: Run tests + run: make test From 6dc528a4e724b398fd734cabf7c420fd66b3280e Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 2 Sep 2024 23:59:09 -0300 Subject: [PATCH 232/248] ci: add to run regress.sh --- .github/workflows/test.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0b33ba57..d45d0543 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,7 +24,9 @@ jobs: - name: Install dependencies run: pip install . && pip install pytest - name: Run tests - run: source tests/mocks/source-me.sh && make test + run: | + source tests/mocks/source-me.sh && make test + cd examples/projects && bash regress.sh test-win: runs-on: windows-latest name: windows | 3.12 From 48af8b24979e93d26381a4db9c3c26791b4f6831 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 4 Sep 2024 20:09:23 -0300 Subject: [PATCH 233/248] ci: modified when to trigger the docs actions --- .github/workflows/docs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 8d26b1af..bf55bcfb 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -4,6 +4,7 @@ on: push: paths: - 'docs/**' + - 'pyfpga/project.py' branches: - main - dev From fcd51f6e5c1ea741d10a956292a5de3ac90f48b2 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Wed, 4 Sep 2024 20:25:17 -0300 Subject: [PATCH 234/248] ci: modified to run ubuntu and windows tests using the same job --- .github/workflows/test.yml | 39 ++++++++++++++------------------------ tests/test_tools.py | 4 ++-- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d45d0543..12e54f2c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,19 +4,25 @@ on: push: jobs: - test-lin: + test: strategy: matrix: - os: ['ubuntu'] - pyver: ['3.8', '3.9', '3.10', '3.11', '3.12'] + 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: - name: Checkout repository uses: actions/checkout@v4 - with: - submodules: true - fetch-depth: 0 - name: Set up Python ${{ matrix.pyver }} uses: actions/setup-python@v5 with: @@ -25,22 +31,5 @@ jobs: run: pip install . && pip install pytest - name: Run tests run: | - source tests/mocks/source-me.sh && make test - cd examples/projects && bash regress.sh - test-win: - runs-on: windows-latest - name: windows | 3.12 - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - submodules: true - fetch-depth: 0 - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: 3.12 - - name: Install dependencies - run: pip install . && pip install pytest - - name: Run tests - run: make test + make test + cd examples/projects && bash regress.sh --notool diff --git a/tests/test_tools.py b/tests/test_tools.py index ad28632a..98f00055 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -85,9 +85,9 @@ def generate(tool, part): prj.add_hook('postbit', 'HOOK16') try: prj.make() - except Exception: + except RuntimeError: pass try: prj.prog() - except Exception: + except RuntimeError: pass From 1a0ff918b0f63b58c52193aeed798f78aa4c7c00 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 7 Sep 2024 14:22:48 -0300 Subject: [PATCH 235/248] Add the --notool option, to avoid errors when tools are not available --- examples/projects/diamond.py | 16 +++++++++++----- examples/projects/ise.py | 16 +++++++++++----- examples/projects/libero.py | 16 +++++++++++----- examples/projects/openflow.py | 16 +++++++++++----- examples/projects/quartus.py | 16 +++++++++++----- examples/projects/regress.sh | 25 +++++++++++++++++++++++-- examples/projects/vivado.py | 16 +++++++++++----- 7 files changed, 89 insertions(+), 32 deletions(-) diff --git a/examples/projects/diamond.py b/examples/projects/diamond.py index 0128d7c3..464be7ff 100644 --- a/examples/projects/diamond.py +++ b/examples/projects/diamond.py @@ -15,6 +15,9 @@ 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}') @@ -43,8 +46,11 @@ prj.set_top('Top') -if args.action in ['make', 'all']: - prj.make() - -if args.action in ['prog', 'all']: - prj.prog() +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 index 279a5db0..1955d34d 100644 --- a/examples/projects/ise.py +++ b/examples/projects/ise.py @@ -15,6 +15,9 @@ 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}') @@ -45,8 +48,11 @@ prj.set_top('Top') -if args.action in ['make', 'all']: - prj.make() - -if args.action in ['prog', 'all']: - prj.prog() +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 index dd2c5861..8bb94b7a 100644 --- a/examples/projects/libero.py +++ b/examples/projects/libero.py @@ -14,6 +14,9 @@ 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}') @@ -43,8 +46,11 @@ prj.set_top('Top') -if args.action in ['make', 'all']: - prj.make() - -if args.action in ['prog', 'all']: - prj.prog() +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 index 56a6b2ab..98e2e999 100644 --- a/examples/projects/openflow.py +++ b/examples/projects/openflow.py @@ -16,6 +16,9 @@ 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}') @@ -59,8 +62,11 @@ prj.set_top('Top') -if args.action in ['make', 'all']: - prj.make() - -if args.action in ['prog', 'all']: - prj.prog() +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 index eb7e889e..258fa8e6 100644 --- a/examples/projects/quartus.py +++ b/examples/projects/quartus.py @@ -15,6 +15,9 @@ 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}') @@ -44,8 +47,11 @@ prj.set_top('Top') -if args.action in ['make', 'all']: - prj.make() - -if args.action in ['prog', 'all']: - prj.prog() +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 index 45b7d848..a260b52e 100644 --- a/examples/projects/regress.sh +++ b/examples/projects/regress.sh @@ -13,7 +13,24 @@ TOOLS["vivado"]="zybo arty" SOURCES=("vlog" "vhdl" "slog") -SPECIFIED_TOOL=$1 +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 @@ -26,7 +43,11 @@ for TOOL in "${!TOOLS[@]}"; do continue fi echo "> $TOOL - $BOARD - $SOURCE" - python3 $TOOL.py --board $BOARD --source $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 index af497b91..33fba3f3 100644 --- a/examples/projects/vivado.py +++ b/examples/projects/vivado.py @@ -15,6 +15,9 @@ 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}') @@ -50,8 +53,11 @@ prj.set_top('Top') -if args.action in ['make', 'all']: - prj.make() - -if args.action in ['prog', 'all']: - prj.prog() +try: + if args.action in ['make', 'all']: + prj.make() + if args.action in ['prog', 'all']: + prj.prog() +except RuntimeError: + if not args.notool: + raise From 8c061c1fe94672806b2f5efabdee92573234f4c9 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 7 Sep 2024 14:27:55 -0300 Subject: [PATCH 236/248] ci: fix issue specifyng the Python version --- .github/workflows/test.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 12e54f2c..076ec499 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,17 +7,17 @@ jobs: test: strategy: matrix: - os: [ubuntu, windows] - pyver: [3.8, 3.9, 3.10, 3.11, 3.12] + 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 + - 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: From e313901c7cc51437f4f23eb2ab328a2ef0e847ef Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 7 Sep 2024 21:54:07 -0300 Subject: [PATCH 237/248] tests: checking that paths are posix-like --- tests/test_data.py | 77 ++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/tests/test_data.py b/tests/test_data.py index 49b0645b..d4d8d0f8 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -57,24 +57,24 @@ Path(tdir / 'fakedata/cons/par.xdc').resolve().as_posix(): {} }, 'params': { - 'PAR1': 'VAL1', - 'PAR2': 'VAL2', - 'PAR3': 'VAL3' + 'PAR1': 'VAL01', + 'PAR2': 'VAL02', + 'PAR3': 'VAL03' }, 'defines': { - 'DEF1': 'VAL1', - 'DEF2': 'VAL2', - 'DEF3': 'VAL3' + 'DEF1': 'VAL01', + 'DEF2': 'VAL02', + 'DEF3': 'VAL03' }, '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'] + 'precfg': ['CMD01', 'CMD02'], + 'postcfg': ['CMD03', 'CMD04'], + 'presyn': ['CMD05', 'CMD06'], + 'postsyn': ['CMD07', 'CMD08'], + 'prepar': ['CMD09', 'CMD10'], + 'postpar': ['CMD11', 'CMD12'], + 'prebit': ['CMD13', 'CMD14'], + 'postbit': ['CMD15', 'CMD16'] } } @@ -92,26 +92,31 @@ def test_data(): 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_param('PAR3', 'VAL3') - prj.add_define('DEF1', 'VAL1') - prj.add_define('DEF2', 'VAL2') - prj.add_define('DEF3', 'VAL3') - prj.add_hook('precfg', 'CMD1') - prj.add_hook('precfg', 'CMD2') - prj.add_hook('postcfg', 'CMD1') - prj.add_hook('postcfg', 'CMD2') - prj.add_hook('presyn', 'CMD1') - prj.add_hook('presyn', 'CMD2') - prj.add_hook('postsyn', 'CMD1') - prj.add_hook('postsyn', 'CMD2') - prj.add_hook('prepar', 'CMD1') - prj.add_hook('prepar', 'CMD2') - prj.add_hook('postpar', 'CMD1') - prj.add_hook('postpar', 'CMD2') - prj.add_hook('prebit', 'CMD1') - prj.add_hook('prebit', 'CMD2') - prj.add_hook('postbit', 'CMD1') - prj.add_hook('postbit', 'CMD2') + 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." + ) From d94057e431663c5280aedc38de847397d667d907 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 7 Sep 2024 22:00:35 -0300 Subject: [PATCH 238/248] ci: moved to run regress from test.yml to the Makefile --- .github/workflows/test.yml | 4 +--- Makefile | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 076ec499..b04f05be 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,6 +30,4 @@ jobs: - name: Install dependencies run: pip install . && pip install pytest - name: Run tests - run: | - make test - cd examples/projects && bash regress.sh --notool + run: make test diff --git a/Makefile b/Makefile index 2f2402c4..7893bb51 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,7 @@ lint: test: pytest + cd examples/projects && bash regress.sh --notool clean: py3clean . From d6b773b334f6b3a705528312f0e141ae77b465c0 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 9 Nov 2024 09:23:57 -0300 Subject: [PATCH 239/248] prj2bit: improve ERROR messages --- pyfpga/helpers/prj2bit.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyfpga/helpers/prj2bit.py b/pyfpga/helpers/prj2bit.py index 1124e452..ea633b6b 100644 --- a/pyfpga/helpers/prj2bit.py +++ b/pyfpga/helpers/prj2bit.py @@ -63,7 +63,7 @@ def main(): prjfile = Path(args.prjfile) if not prjfile.exists(): - sys.exit('file not found.') + sys.exit(f'ERROR: {prjfile} file not found.') directory = prjfile.parent base_name = prjfile.stem @@ -72,9 +72,9 @@ def main(): tool = '' if extension in tool_per_ext: tool = tool_per_ext[extension] - print(f'* {tool} project file found.') + print(f'INFO: {tool} project file found.') else: - sys.exit('Unknown project file extension') + sys.exit(f'ERROR: unknown project file extension ({extension})') # ------------------------------------------------------------------------- # Solving with PyFPGA From 5c5c4f99500481db7b9e2b6a1b28f84f745e96b6 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 10 Nov 2024 11:47:08 -0300 Subject: [PATCH 240/248] Add constraints for the mpfs-disco-kit board --- examples/sources/cons/mpfs-disco-kit/clk.pdc | 1 + examples/sources/cons/mpfs-disco-kit/led.pdc | 1 + examples/sources/cons/mpfs-disco-kit/timing.sdc | 1 + 3 files changed, 3 insertions(+) create mode 100644 examples/sources/cons/mpfs-disco-kit/clk.pdc create mode 100644 examples/sources/cons/mpfs-disco-kit/led.pdc create mode 100644 examples/sources/cons/mpfs-disco-kit/timing.sdc 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] From 25e683c7fa9c4c7d19c47bf99f5aed94c3efd249 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 10 Nov 2024 12:06:53 -0300 Subject: [PATCH 241/248] Fix support for PolarFireSoC devices --- examples/projects/libero.py | 9 ++++++++- pyfpga/libero.py | 29 +++++++++++++++++++++++++---- pyfpga/templates/libero.jinja | 4 ++-- tests/test_part.py | 3 ++- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/examples/projects/libero.py b/examples/projects/libero.py index 8bb94b7a..3498a6e3 100644 --- a/examples/projects/libero.py +++ b/examples/projects/libero.py @@ -6,7 +6,7 @@ parser = argparse.ArgumentParser() parser.add_argument( - '--board', choices=['maker'], default='maker' + '--board', choices=['mpfs-disco-kit', 'maker'], default='mpfs-disco-kit' ) parser.add_argument( '--source', choices=['vlog', 'vhdl', 'slog'], default='vlog' @@ -21,6 +21,13 @@ 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') diff --git a/pyfpga/libero.py b/pyfpga/libero.py index 2f1e4bc4..6f1bda0f 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -31,9 +31,10 @@ def _make_custom(self): self.data['device'] = info['device'] self.data['speed'] = info['speed'] self.data['package'] = info['package'] + self.data['prange'] = info['prange'] def _prog_custom(self): - raise NotImplementedError('Libero programming not supported') + raise NotImplementedError('Libero programming not supported yet') # pylint: disable=duplicate-code @@ -42,7 +43,7 @@ def get_info(part): """Get info about the FPGA part. :param part: the FPGA part as specified by the tool - :returns: a dictionary with the keys family, device, speed and package + :returns: a dict with the keys family, device, speed, package and prange """ part = part.lower() # Looking for the family @@ -51,6 +52,7 @@ def get_info(part): r'm2s': 'SmartFusion2', r'm2gl': 'Igloo2', r'rt4g': 'RTG4', + r'mpfs': 'PolarFireSoC', r'mpf': 'PolarFire', r'a2f': 'SmartFusion', r'afs': 'Fusion', @@ -65,10 +67,11 @@ def get_info(part): if re.match(key, part): family = value break - # Looking for the device and package + # Looking for the device, speed and package device = None speed = None package = None + prange = None aux = part.split('-') if len(aux) == 2: device = aux[0] @@ -86,7 +89,25 @@ def get_info(part): raise ValueError( 'Part must be DEVICE-SPEED-PACKAGE or DEVICE-PACKAGE' ) + # Looking for a part_range + pranges = { + 'c': 'COM', + 'e': 'EXT', + 'i': 'IND', + 'm': 'MIL', + 't1': 'TGrade1' + } + 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 + 'family': family, + 'device': device, + 'speed': speed, + 'package': package, + 'prange': prange } diff --git a/pyfpga/templates/libero.jinja b/pyfpga/templates/libero.jinja index c64d8a5d..499a9230 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -9,8 +9,8 @@ {% if 'cfg' in steps %}# Project configuration ------------------------------------------------------- if { [ file exists {{ project }} ] } { file delete -force -- {{ project }} } -new_project -name {{ project }} -location {libero} -hdl {VERILOG} -family {SmartFusion2} -set_device -family {{ family }} -die {{ device }} -package {{ package }} -speed {{ speed }} +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 %} diff --git a/tests/test_part.py b/tests/test_part.py index 86087946..5319e3dc 100644 --- a/tests/test_part.py +++ b/tests/test_part.py @@ -19,7 +19,8 @@ def test_libero(): 'family': 'SmartFusion2', 'device': 'm2s010', 'speed': '-1', - 'package': 'tq144' + 'package': 'tq144', + 'prange': 'COM' } assert get_info_libero('m2s010-1-tq144') == info assert get_info_libero('m2s010-tq144-1') == info From a90c1f4e11f084791a21835a366587e0f9a65b95 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 10 Nov 2024 22:09:48 -0300 Subject: [PATCH 242/248] Improve PART format parsing (ise, libero and openflow) --- docs/tools.rst | 26 +++++++++++ pyfpga/ise.py | 20 ++++---- pyfpga/libero.py | 20 ++++---- pyfpga/openflow.py | 15 ++++-- pyfpga/templates/ise.jinja | 2 +- tests/test_part.py | 96 ++++++++++++++++++++++++++++++++++---- 6 files changed, 147 insertions(+), 32 deletions(-) diff --git a/docs/tools.rst b/docs/tools.rst index 1f2ee57b..38da3ef3 100644 --- a/docs/tools.rst +++ b/docs/tools.rst @@ -70,6 +70,13 @@ Example: prj = Ise() +Valid PART formats: + +.. code:: + + <DEVICE>-<SPEED>-<PACKAGE> + <DEVICE>-<PACKAGE>-<SPEED> + Libero ------ @@ -87,6 +94,19 @@ Example: prj = Libero() +Valid PART formats: + +.. code:: + + <DEVICE>-<PACKAGE> + <DEVICE>-<SPEED><PACKAGE> + <DEVICE>-<SPEED>-<PACKAGE> + <DEVICE>-<PACKAGE>-<SPEED> + <DEVICE>-<PACKAGE><RANGE> + <DEVICE>-<SPEED><PACKAGE><RANGE> + <DEVICE>-<SPEED>-<PACKAGE><RANGE> + <DEVICE>-<PACKAGE><RANGE>-<SPEED> + Openflow -------- @@ -116,6 +136,12 @@ Example: prj = Openflow() +Valid PART formats: + +.. code:: + + <DEVICE>-<PACKAGE> + Quartus ------- diff --git a/pyfpga/ise.py b/pyfpga/ise.py index 03eb8707..5ca52f8a 100644 --- a/pyfpga/ise.py +++ b/pyfpga/ise.py @@ -43,9 +43,9 @@ def get_info(part): """Get info about the FPGA part. :param part: the FPGA part as specified by the tool - :returns: a dictionary with the keys family, device, speed and package + :returns: a dict with the keys family, device, speed and package """ - part = part.lower() + part = part.lower().replace(' ', '') # Looking for the family family = None families = { @@ -71,7 +71,7 @@ def get_info(part): if re.match(key, part): family = value break - # Looking for the device, package and speed + # Looking for the other values device = None speed = None package = None @@ -79,16 +79,18 @@ def get_info(part): if len(aux) == 3: device = aux[0] if len(aux[1]) < len(aux[2]): - speed = aux[1] + speed = f'-{aux[1]}' package = aux[2] else: - speed = aux[2] + speed = f'-{aux[2]}' package = aux[1] else: - raise ValueError( - 'Part must be DEVICE-SPEED-PACKAGE or DEVICE-PACKAGE-SPEED' - ) + 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 + 'family': family, + 'device': device, + 'speed': speed, + 'package': package } diff --git a/pyfpga/libero.py b/pyfpga/libero.py index 6f1bda0f..e0ab20df 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -14,7 +14,7 @@ class Libero(Project): - """Class to support Libero.""" + """Class to support Libero projects.""" def _configure(self): tool = 'libero' @@ -45,12 +45,12 @@ def get_info(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() + part = part.lower().replace(' ', '') # Looking for the family family = None families = { r'm2s': 'SmartFusion2', - r'm2gl': 'Igloo2', + r'm2gl': 'IGLOO2', r'rt4g': 'RTG4', r'mpfs': 'PolarFireSoC', r'mpf': 'PolarFire', @@ -67,7 +67,7 @@ def get_info(part): if re.match(key, part): family = value break - # Looking for the device, speed and package + # Looking for the other values device = None speed = None package = None @@ -75,8 +75,12 @@ def get_info(part): aux = part.split('-') if len(aux) == 2: device = aux[0] - speed = 'STD' 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]): @@ -86,10 +90,8 @@ def get_info(part): speed = f'-{aux[2]}' package = aux[1] else: - raise ValueError( - 'Part must be DEVICE-SPEED-PACKAGE or DEVICE-PACKAGE' - ) - # Looking for a part_range + valid = 'DEVICE-[SPEED][-]PACKAGE[PRANGE][-SPEED]' + raise ValueError(f'Invalid PART format ({valid})') pranges = { 'c': 'COM', 'e': 'EXT', diff --git a/pyfpga/openflow.py b/pyfpga/openflow.py index 07cefac4..25ab9fc4 100644 --- a/pyfpga/openflow.py +++ b/pyfpga/openflow.py @@ -34,9 +34,9 @@ def get_info(part): """Get info about the FPGA part. :param part: the FPGA part as specified by the tool - :returns: a dictionary with the keys family, device and package + :returns: a dict with the keys family, device and package """ - part = part.lower() + part = part.lower().replace(' ', '') # Looking for the family family = None families = [ @@ -62,7 +62,7 @@ def get_info(part): ] if part.startswith(tuple(families)): family = 'ecp5' - # Looking for the device and package + # Looking for the other values device = None package = None aux = part.split('-') @@ -73,11 +73,16 @@ def get_info(part): device = f'{aux[0]}-{aux[1]}' package = aux[2] else: - raise ValueError('Part must be DEVICE-PACKAGE') + 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} + return { + 'family': family, + 'device': device, + 'package': package + } diff --git a/pyfpga/templates/ise.jinja b/pyfpga/templates/ise.jinja index c90f2d5e..54803e9b 100644 --- a/pyfpga/templates/ise.jinja +++ b/pyfpga/templates/ise.jinja @@ -13,7 +13,7 @@ project new {{ project }}.xise project set family {{ family }} project set device {{ device }} project set package {{ package }} -project set speed -{{ speed }} +project set speed {{ speed }} {% if hooks %}{{ hooks.precfg | join('\n') }}{% endif %} diff --git a/tests/test_part.py b/tests/test_part.py index 5319e3dc..1d155aa8 100644 --- a/tests/test_part.py +++ b/tests/test_part.py @@ -7,25 +7,105 @@ def test_ise(): info = { 'family': 'kintex7', 'device': 'xc7k160t', - 'speed': '3', + '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': 'm2s010', - 'speed': '-1', - 'package': 'tq144', + 'device': 'm2s025t', + 'speed': 'STD', + 'package': 'fg484', 'prange': 'COM' } - assert get_info_libero('m2s010-1-tq144') == info - assert get_info_libero('m2s010-tq144-1') == info - info['speed'] = 'STD' - assert get_info_libero('m2s010-tq144') == info + 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(): From 167fbeb84b0c05a1b2b7b83c62766387dcadb2a4 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Mon, 11 Nov 2024 23:46:59 -0300 Subject: [PATCH 243/248] Initial support for Libero programming --- pyfpga/libero.py | 12 +++++------- pyfpga/templates/libero-prog.jinja | 14 ++++++-------- pyfpga/templates/openflow-prog.jinja | 1 + 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/pyfpga/libero.py b/pyfpga/libero.py index e0ab20df..01800102 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -21,9 +21,9 @@ def _configure(self): self.conf['tool'] = tool self.conf['make_cmd'] = f'{tool} SCRIPT:{tool}.tcl' self.conf['make_ext'] = 'tcl' - self.conf['prog_bit'] = None - self.conf['prog_cmd'] = None - self.conf['prog_ext'] = None + self.conf['prog_bit'] = 'pdd' + self.conf['prog_cmd'] = f'{tool} SCRIPT:{tool}-prog.tcl' + self.conf['prog_ext'] = 'tcl' def _make_custom(self): info = get_info(self.data.get('part', 'mpf100t-1-fcg484')) @@ -33,9 +33,6 @@ def _make_custom(self): self.data['package'] = info['package'] self.data['prange'] = info['prange'] - def _prog_custom(self): - raise NotImplementedError('Libero programming not supported yet') - # pylint: disable=duplicate-code @@ -97,7 +94,8 @@ def get_info(part): 'e': 'EXT', 'i': 'IND', 'm': 'MIL', - 't1': 'TGrade1' + 't1': 'TGrade1', + 't2': 'TGrade2' } prange = 'COM' for suffix, name in pranges.items(): diff --git a/pyfpga/templates/libero-prog.jinja b/pyfpga/templates/libero-prog.jinja index f2369645..df37dc0b 100644 --- a/pyfpga/templates/libero-prog.jinja +++ b/pyfpga/templates/libero-prog.jinja @@ -1,14 +1,12 @@ +{# # -# Copyright (C) 2015-2024 PyFPGA Project +# Copyright (C) 2024 PyFPGA Project # # SPDX-License-Identifier: GPL-3.0-or-later # #} -# 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}" +if { [catch {open_project {{ project }}/{{ project }}.prjx} ] } { + open_project {{ project }}.prjx +} +run_tool -name {PROGRAMDEVICE} diff --git a/pyfpga/templates/openflow-prog.jinja b/pyfpga/templates/openflow-prog.jinja index 7956b2b3..5055b505 100644 --- a/pyfpga/templates/openflow-prog.jinja +++ b/pyfpga/templates/openflow-prog.jinja @@ -1,3 +1,4 @@ +{# # # Copyright (C) 2020-2024 PyFPGA Project # From 113f78dcee91516a97c51bdc33219b1471df99ee Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 23 Nov 2024 23:44:02 -0300 Subject: [PATCH 244/248] Modified to employ absolute paths when a bitstream is specified --- pyfpga/project.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyfpga/project.py b/pyfpga/project.py index 10bb57ea..b49b2174 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -253,6 +253,9 @@ def prog(self, bitstream=None, position=1): self.logger.info('Programming') if not bitstream: bitstream = f'{self.data["project"]}.{self.conf["prog_bit"]}' + else: + bitstream = Path(bitstream).resolve().as_posix() + 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') From c4340abeefe16b8710bb949459daebb41229b690 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sat, 23 Nov 2024 23:44:46 -0300 Subject: [PATCH 245/248] Improved Libero programming support --- pyfpga/libero.py | 4 ++-- pyfpga/templates/libero-prog.jinja | 11 +++++++---- pyfpga/templates/libero.jinja | 1 + 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/pyfpga/libero.py b/pyfpga/libero.py index 01800102..b0661d14 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -21,8 +21,8 @@ def _configure(self): self.conf['tool'] = tool self.conf['make_cmd'] = f'{tool} SCRIPT:{tool}.tcl' self.conf['make_ext'] = 'tcl' - self.conf['prog_bit'] = 'pdd' - self.conf['prog_cmd'] = f'{tool} SCRIPT:{tool}-prog.tcl' + self.conf['prog_bit'] = 'ppd' + self.conf['prog_cmd'] = f'FPExpress SCRIPT:{tool}-prog.tcl' self.conf['prog_ext'] = 'tcl' def _make_custom(self): diff --git a/pyfpga/templates/libero-prog.jinja b/pyfpga/templates/libero-prog.jinja index df37dc0b..3dba4fd8 100644 --- a/pyfpga/templates/libero-prog.jinja +++ b/pyfpga/templates/libero-prog.jinja @@ -6,7 +6,10 @@ # #} -if { [catch {open_project {{ project }}/{{ project }}.prjx} ] } { - open_project {{ project }}.prjx -} -run_tool -name {PROGRAMDEVICE} +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 index 499a9230..71b9afc1 100644 --- a/pyfpga/templates/libero.jinja +++ b/pyfpga/templates/libero.jinja @@ -113,6 +113,7 @@ run_tool -name {VERIFYTIMING} {% 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 %} From aa24928256aafe93b0b5a0a5b17436cb961bd840 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 24 Nov 2024 13:15:04 -0300 Subject: [PATCH 246/248] Add to check if the bitstream exists --- pyfpga/project.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyfpga/project.py b/pyfpga/project.py index b49b2174..b5d947ac 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -245,6 +245,7 @@ def prog(self, bitstream=None, position=1): :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') @@ -255,6 +256,8 @@ def prog(self, bitstream=None, position=1): bitstream = f'{self.data["project"]}.{self.conf["prog_bit"]}' else: bitstream = Path(bitstream).resolve().as_posix() + if not os.path.exists(bitstream): + raise FileNotFoundError(bitstream) self.data['bitstream'] = bitstream self._prog_custom() self._create_file(f'{self.conf["tool"]}-prog', self.conf['prog_ext']) From 3666ac73500dc8b742659b2954e1c4215a271e5e Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 24 Nov 2024 13:52:51 -0300 Subject: [PATCH 247/248] ise: replace file rename by copy --- pyfpga/templates/ise.jinja | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpga/templates/ise.jinja b/pyfpga/templates/ise.jinja index 54803e9b..34577302 100644 --- a/pyfpga/templates/ise.jinja +++ b/pyfpga/templates/ise.jinja @@ -94,7 +94,7 @@ if { [process get "Place & Route" status] == "errors" } { exit 1 } process run "Generate Programming File" if { [process get "Generate Programming File" status] == "errors" } { exit 1 } -catch { file rename -force {{ top }}.bit {{ project }}.bit } +catch { file copy -force {{ top }}.bit {{ project }}.bit } {% if hooks %}{{ hooks.postbit | join('\n') }}{% endif %} From ef4ae95cd67c132253200d4209f2bda4092467d9 Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" <rodrigomelo9@gmail.com> Date: Sun, 24 Nov 2024 22:59:15 -0300 Subject: [PATCH 248/248] Improve bitstream handling --- pyfpga/diamond.py | 2 +- pyfpga/ise.py | 2 +- pyfpga/libero.py | 2 +- pyfpga/openflow.py | 6 +++++- pyfpga/project.py | 10 +++++++--- pyfpga/quartus.py | 2 +- pyfpga/templates/openflow-prog.jinja | 4 ++-- pyfpga/vivado.py | 2 +- tests/mocks/FPExpress | 25 +++++++++++++++++++++++++ tests/mocks/libero | 13 ++++++++++++- tests/mocks/quartus_sh | 9 +++++++++ tests/mocks/vivado | 9 +++++++++ tests/mocks/xtclsh | 9 +++++++++ tests/test_tools.py | 8 ++++++++ 14 files changed, 91 insertions(+), 12 deletions(-) create mode 100755 tests/mocks/FPExpress diff --git a/pyfpga/diamond.py b/pyfpga/diamond.py index a5b3e622..01226a6d 100644 --- a/pyfpga/diamond.py +++ b/pyfpga/diamond.py @@ -21,7 +21,7 @@ def _configure(self): 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_bit'] = ['bit'] self.conf['prog_cmd'] = f'sh {tool}-prog.sh' self.conf['prog_ext'] = 'sh' diff --git a/pyfpga/ise.py b/pyfpga/ise.py index 5ca52f8a..048b140b 100644 --- a/pyfpga/ise.py +++ b/pyfpga/ise.py @@ -21,7 +21,7 @@ def _configure(self): 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_bit'] = ['bit'] self.conf['prog_cmd'] = f'impact -batch {tool}-prog.tcl' self.conf['prog_ext'] = 'tcl' diff --git a/pyfpga/libero.py b/pyfpga/libero.py index b0661d14..37c56650 100644 --- a/pyfpga/libero.py +++ b/pyfpga/libero.py @@ -21,7 +21,7 @@ def _configure(self): self.conf['tool'] = tool self.conf['make_cmd'] = f'{tool} SCRIPT:{tool}.tcl' self.conf['make_ext'] = 'tcl' - self.conf['prog_bit'] = 'ppd' + self.conf['prog_bit'] = ['ppd', 'stp', 'bit', 'jed'] self.conf['prog_cmd'] = f'FPExpress SCRIPT:{tool}-prog.tcl' self.conf['prog_ext'] = 'tcl' diff --git a/pyfpga/openflow.py b/pyfpga/openflow.py index 25ab9fc4..299abc7c 100644 --- a/pyfpga/openflow.py +++ b/pyfpga/openflow.py @@ -19,7 +19,7 @@ def _configure(self): self.conf['tool'] = tool self.conf['make_cmd'] = f'bash {tool}.sh' self.conf['make_ext'] = 'sh' - self.conf['prog_bit'] = 'bit' + self.conf['prog_bit'] = ['svf', 'bit'] self.conf['prog_cmd'] = f'bash {tool}-prog.sh' self.conf['prog_ext'] = 'sh' @@ -29,6 +29,10 @@ def _make_custom(self): 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. diff --git a/pyfpga/project.py b/pyfpga/project.py index b5d947ac..75eaec47 100644 --- a/pyfpga/project.py +++ b/pyfpga/project.py @@ -253,10 +253,14 @@ def prog(self, bitstream=None, position=1): raise ValueError('Invalid position.') self.logger.info('Programming') if not bitstream: - bitstream = f'{self.data["project"]}.{self.conf["prog_bit"]}' + 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().as_posix() - if not os.path.exists(bitstream): + bitstream = Path(bitstream).resolve() + if not bitstream or not bitstream.exists(): raise FileNotFoundError(bitstream) self.data['bitstream'] = bitstream self._prog_custom() diff --git a/pyfpga/quartus.py b/pyfpga/quartus.py index 8cd8f66a..747c6628 100644 --- a/pyfpga/quartus.py +++ b/pyfpga/quartus.py @@ -19,7 +19,7 @@ def _configure(self): self.conf['tool'] = tool self.conf['make_cmd'] = f'quartus_sh --script {tool}.tcl' self.conf['make_ext'] = 'tcl' - self.conf['prog_bit'] = 'sof' + self.conf['prog_bit'] = ['sof', 'pof'] self.conf['prog_cmd'] = f'bash {tool}-prog.tcl' self.conf['prog_ext'] = 'tcl' diff --git a/pyfpga/templates/openflow-prog.jinja b/pyfpga/templates/openflow-prog.jinja index 5055b505..b26de159 100644 --- a/pyfpga/templates/openflow-prog.jinja +++ b/pyfpga/templates/openflow-prog.jinja @@ -11,7 +11,7 @@ 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 {{ project }}.svf; exit" +$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 {{ project }}.bit +$DOCKER --device /dev/bus/usb hdlc/prog iceprog {{ bitstream }} {% endif %} diff --git a/pyfpga/vivado.py b/pyfpga/vivado.py index 0391a17f..2b04819d 100644 --- a/pyfpga/vivado.py +++ b/pyfpga/vivado.py @@ -20,7 +20,7 @@ def _configure(self): 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_bit'] = ['bit'] self.conf['prog_cmd'] = f'{command} {tool}-prog.tcl' self.conf['prog_ext'] = 'tcl' 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/libero b/tests/mocks/libero index 510b9926..94c8dd05 100755 --- a/tests/mocks/libero +++ b/tests/mocks/libero @@ -7,6 +7,7 @@ # import argparse +import re import subprocess import sys @@ -23,10 +24,12 @@ 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.replace('SCRIPT:', '')} +source {args.source} ''' with open(f'{tool}-mock.tcl', 'w', encoding='utf-8') as file: @@ -39,4 +42,12 @@ subprocess.run( 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_sh b/tests/mocks/quartus_sh index 7f437145..38f6507d 100755 --- a/tests/mocks/quartus_sh +++ b/tests/mocks/quartus_sh @@ -8,6 +8,7 @@ import argparse import os +import re import subprocess @@ -41,4 +42,12 @@ subprocess.run( 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/vivado b/tests/mocks/vivado index 3bc95381..2f5cbe69 100755 --- a/tests/mocks/vivado +++ b/tests/mocks/vivado @@ -7,6 +7,7 @@ # import argparse +import re import subprocess @@ -70,4 +71,12 @@ subprocess.run( 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 index 22f66a77..4064f790 100755 --- a/tests/mocks/xtclsh +++ b/tests/mocks/xtclsh @@ -7,6 +7,7 @@ # import argparse +import re import subprocess @@ -34,4 +35,12 @@ subprocess.run( 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/test_tools.py b/tests/test_tools.py index 98f00055..a2b3e489 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -9,6 +9,7 @@ def test_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(): @@ -24,6 +25,7 @@ def test_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(): @@ -87,6 +89,12 @@ def generate(tool, part): 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: