From 3d9b185a98fca180c97e3dc53c3f3c4ae322e60a Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" Date: Sun, 15 May 2022 20:38:50 -0300 Subject: [PATCH 01/69] 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" Date: Sun, 15 May 2022 20:40:25 -0300 Subject: [PATCH 02/69] 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" Date: Sun, 22 May 2022 21:28:21 -0300 Subject: [PATCH 03/69] 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 . +# + +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 . +# + +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 . +# + +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 . +# + +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 . +# + +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 . +# + +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 . +# + +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" Date: Tue, 24 May 2022 21:53:10 -0300 Subject: [PATCH 04/69] 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" Date: Tue, 24 May 2022 22:20:55 -0300 Subject: [PATCH 05/69] 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" Date: Tue, 24 May 2022 22:35:30 -0300 Subject: [PATCH 06/69] 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" Date: Wed, 25 May 2022 23:01:04 -0300 Subject: [PATCH 07/69] 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" Date: Wed, 25 May 2022 23:06:45 -0300 Subject: [PATCH 08/69] 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" Date: Thu, 26 May 2022 22:14:25 -0300 Subject: [PATCH 09/69] 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" Date: Thu, 26 May 2022 22:52:45 -0300 Subject: [PATCH 10/69] 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 . # # 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" Date: Thu, 26 May 2022 23:32:58 -0300 Subject: [PATCH 11/69] 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" Date: Fri, 27 May 2022 00:02:29 -0300 Subject: [PATCH 12/69] 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 Date: Tue, 31 Jan 2023 18:46:32 -0500 Subject: [PATCH 13/69] 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" Date: Sun, 19 May 2024 21:27:38 -0300 Subject: [PATCH 14/69] 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 . -# - -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" Date: Sat, 25 May 2024 23:51:41 -0300 Subject: [PATCH 15/69] 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" Date: Sat, 25 May 2024 23:52:35 -0300 Subject: [PATCH 16/69] 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" Date: Sat, 25 May 2024 23:57:13 -0300 Subject: [PATCH 17/69] 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" Date: Sat, 25 May 2024 23:59:25 -0300 Subject: [PATCH 18/69] 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" Date: Sun, 26 May 2024 09:01:21 -0300 Subject: [PATCH 19/69] 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" Date: Sun, 26 May 2024 13:42:54 -0300 Subject: [PATCH 20/69] 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" Date: Sun, 26 May 2024 13:43:32 -0300 Subject: [PATCH 21/69] 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" Date: Sun, 26 May 2024 13:48:13 -0300 Subject: [PATCH 22/69] 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" Date: Sun, 26 May 2024 15:40:46 -0300 Subject: [PATCH 23/69] 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" Date: Sun, 26 May 2024 15:41:16 -0300 Subject: [PATCH 24/69] 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 `. +You can read the :ref:`basic` and :ref:`advanced` sections, check the detailed :ref:`api` or start with the available :repositoy:`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" Date: Sun, 26 May 2024 15:43:08 -0300 Subject: [PATCH 25/69] 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" Date: Sun, 26 May 2024 15:43:34 -0300 Subject: [PATCH 26/69] 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" Date: Sun, 26 May 2024 15:43:54 -0300 Subject: [PATCH 27/69] 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" Date: Sun, 26 May 2024 15:47:57 -0300 Subject: [PATCH 28/69] 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" Date: Sun, 26 May 2024 15:55:30 -0300 Subject: [PATCH 29/69] 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" Date: Sun, 26 May 2024 19:25:14 -0300 Subject: [PATCH 30/69] 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" Date: Sun, 26 May 2024 19:25:33 -0300 Subject: [PATCH 31/69] 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" Date: Mon, 27 May 2024 22:33:55 -0300 Subject: [PATCH 32/69] 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" Date: Mon, 27 May 2024 22:37:50 -0300 Subject: [PATCH 33/69] 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" Date: Mon, 27 May 2024 23:03:58 -0300 Subject: [PATCH 34/69] 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" Date: Mon, 27 May 2024 23:12:19 -0300 Subject: [PATCH 35/69] 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" Date: Mon, 27 May 2024 23:38:30 -0300 Subject: [PATCH 36/69] 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" Date: Tue, 28 May 2024 21:40:51 -0300 Subject: [PATCH 37/69] 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" Date: Tue, 28 May 2024 21:48:41 -0300 Subject: [PATCH 38/69] 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" Date: Tue, 28 May 2024 23:30:36 -0300 Subject: [PATCH 39/69] 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" Date: Wed, 29 May 2024 23:11:27 -0300 Subject: [PATCH 40/69] 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" Date: Wed, 29 May 2024 23:50:13 -0300 Subject: [PATCH 41/69] 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" Date: Wed, 29 May 2024 23:50:40 -0300 Subject: [PATCH 42/69] 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" Date: Thu, 30 May 2024 00:06:18 -0300 Subject: [PATCH 43/69] 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 - -

- -

- -
- -.. 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" Date: Thu, 30 May 2024 18:08:33 -0300 Subject: [PATCH 44/69] 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" Date: Thu, 30 May 2024 18:51:53 -0300 Subject: [PATCH 45/69] 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 /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 . -# -# 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 . -# -# 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 /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" Date: Thu, 30 May 2024 19:28:49 -0300 Subject: [PATCH 46/69] 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 /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 /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" Date: Fri, 31 May 2024 19:34:40 -0300 Subject: [PATCH 47/69] 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" Date: Fri, 31 May 2024 21:09:02 -0300 Subject: [PATCH 48/69] 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 /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 /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" Date: Fri, 31 May 2024 22:02:01 -0300 Subject: [PATCH 49/69] 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" Date: Fri, 31 May 2024 23:41:42 -0300 Subject: [PATCH 50/69] 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" Date: Sat, 1 Jun 2024 00:04:19 -0300 Subject: [PATCH 51/69] 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 `_ 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 `. 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/.py -=================== - -A base class (``__init__.py``) was developed to provides a uniform API to be -implemented for each supported Tool. -Also, validation of values is performed here. - -Classes to supports each Tool (``.py``) implements the base class, ideally -setting a few variables. - -.. NOTE:: - Transfer of the bitstream to a device is not always performed by a Tcl - script, so special methods must be developed, following the proposed API. - -fpga/project.py -=============== - -This class implements the Application Programming Interface (API) which is -employed to manage an FPGA project. It solves high-level things such as -collect several files using glob, setting and use of a working/output -directory and time measurement. - -FAQ -==== - -How to deal with deprecated Tcl commands? ------------------------------------------ - -From Vivado 2019.1 to 2019.2, ``open_hw`` changed to ``open_hw_manager``: -``if { [ catch { open_hw_manager } ] } { open_hw }`` diff --git a/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" Date: Sat, 1 Jun 2024 01:09:36 -0300 Subject: [PATCH 52/69] 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" Date: Sat, 1 Jun 2024 18:56:30 -0300 Subject: [PATCH 53/69] 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" Date: Sat, 1 Jun 2024 18:57:38 -0300 Subject: [PATCH 54/69] 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" Date: Sat, 1 Jun 2024 19:02:34 -0300 Subject: [PATCH 55/69] 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" Date: Sat, 1 Jun 2024 19:31:11 -0300 Subject: [PATCH 56/69] 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" Date: Sat, 1 Jun 2024 20:17:52 -0300 Subject: [PATCH 57/69] 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 . +# 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" Date: Sat, 1 Jun 2024 20:23:04 -0300 Subject: [PATCH 58/69] 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" Date: Sun, 2 Jun 2024 09:28:00 -0300 Subject: [PATCH 59/69] 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" Date: Sun, 2 Jun 2024 09:28:52 -0300 Subject: [PATCH 60/69] 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" Date: Sun, 2 Jun 2024 09:32:05 -0300 Subject: [PATCH 61/69] 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" Date: Sun, 2 Jun 2024 13:34:45 -0300 Subject: [PATCH 62/69] 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" Date: Sun, 2 Jun 2024 13:36:59 -0300 Subject: [PATCH 63/69] 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" Date: Sun, 2 Jun 2024 16:55:51 -0300 Subject: [PATCH 64/69] 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" Date: Sun, 2 Jun 2024 16:56:45 -0300 Subject: [PATCH 65/69] 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" Date: Sun, 2 Jun 2024 16:57:03 -0300 Subject: [PATCH 66/69] 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" Date: Tue, 4 Jun 2024 21:25:39 -0300 Subject: [PATCH 67/69] 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 . +# 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 . +# 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 . +# 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 . +# 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 . +# 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 . +# 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 . +# SPDX-License-Identifier: GPL-3.0-or-later # """fpga.tool.vivado From 29aae524114ed2b7f835cbf74220fda888b534de Mon Sep 17 00:00:00 2001 From: "Rodrigo A. Melo" Date: Tue, 4 Jun 2024 22:09:40 -0300 Subject: [PATCH 68/69] 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 /techlibs/xilinx/synth_xilinx.cc - 'xcup', 'xcu', 'xc7', 'xc6s', 'xc6v', 'xc5v', 'xc4v', 'xc3sda', - 'xc3sa', 'xc3se', 'xc3s', 'xc2vp', 'xc2v', 'xcve', 'xcv' - ] - for family in families: - if part.startswith(family): - return family - families = [ - # From /ice40/main.cc - 'lp384', 'lp1k', 'lp4k', 'lp8k', 'hx1k', 'hx4k', 'hx8k', - 'up3k', 'up5k', 'u1k', 'u2k', 'u4k' - ] - if part.startswith(tuple(families)): - return 'ice40' - families = [ - # From /ecp5/main.cc - '12k', '25k', '45k', '85k', 'um-25k', 'um-45k', 'um-85k', - 'um5g-25k', 'um5g-45k', 'um5g-85k' - ] - if part.startswith(tuple(families)): - return 'ecp5' - return 'UNKNOWN' +# def get_family(part): +# """Get the Family name from the specified part name.""" +# part = part.lower() +# families = [ +# # From /techlibs/xilinx/synth_xilinx.cc +# 'xcup', 'xcu', 'xc7', 'xc6s', 'xc6v', 'xc5v', 'xc4v', 'xc3sda', +# 'xc3sa', 'xc3se', 'xc3s', 'xc2vp', 'xc2v', 'xcve', 'xcv' +# ] +# for family in families: +# if part.startswith(family): +# return family +# families = [ +# # From /ice40/main.cc +# 'lp384', 'lp1k', 'lp4k', 'lp8k', 'hx1k', 'hx4k', 'hx8k', +# 'up3k', 'up5k', 'u1k', 'u2k', 'u4k' +# ] +# if part.startswith(tuple(families)): +# return 'ice40' +# families = [ +# # From /ecp5/main.cc +# '12k', '25k', '45k', '85k', 'um-25k', 'um-45k', 'um-85k', +# 'um5g-25k', 'um5g-45k', 'um5g-85k' +# ] +# if part.startswith(tuple(families)): +# return 'ecp5' +# return 'UNKNOWN' diff --git a/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" Date: Tue, 4 Jun 2024 22:14:41 -0300 Subject: [PATCH 69/69] 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())