16 |
17 | This is the top-level repository for the [F4PGA](https://f4pga.org/) project, which is a Workgroup under the [CHIPS Alliance](https://chipsalliance.org); consisting of members from different backgrounds, including FPGA vendors, industrial users and academia (see [Documentation > Community](https://f4pga.readthedocs.io/en/latest/community.html));
18 | who collaborate to build a more open source and software-driven FPGA ecosystem (IP, tools and workflows) to drive the
19 | adoption of FPGAs in existing and new use cases, and eliminate barriers of entry.
20 |
21 | * [Documentation > Getting started](https://f4pga.readthedocs.io) (for everyone)
22 | * [F4PGA Examples](https://f4pga-examples.readthedocs.io) (for users)
23 | * [F4PGA Architecture Definitions](https://f4pga.readthedocs.io/projects/arch-defs) (for developers)
24 |
--------------------------------------------------------------------------------
/action/action.yml:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2020-2022 F4PGA Authors.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 | # SPDX-License-Identifier: Apache-2.0
16 |
17 | name: F4PGA Action
18 | description: Use FOSS Flows For FPGA (F4PGA)
19 |
20 | inputs:
21 | cmd:
22 | description: 'Script or commands to execute'
23 | default: './run.py'
24 | image:
25 | description: 'Container image to run the script/command on'
26 | default: xc7/a100t
27 |
28 | runs:
29 | using: "composite"
30 | steps:
31 | - shell: bash
32 | run: |
33 | docker run --rm -i \
34 | -v $(pwd):/wrk -w /wrk \
35 | gcr.io/hdl-containers/conda/f4pga/${{ inputs.image }} \
36 | bash -le <<'EOF'
37 | ${{ inputs.cmd }}
38 | EOF
39 |
40 | branding:
41 | icon: cpu
42 | color: blue
43 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | /_build/
2 | /env/
3 | /status.inc
4 | /development/changes.inc
5 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2020-2022 F4PGA Authors.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 | # SPDX-License-Identifier: Apache-2.0
16 |
17 | TOP_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST))))
18 | REQUIREMENTS_FILE := requirements.txt
19 | ENVIRONMENT_FILE := environment.yml
20 |
21 | include ../third_party/make-env/conda.mk
22 |
23 | # NOTE: make env to create a conda environment with the needed packages
24 |
25 | SPHINXOPTS =
26 | SPHINXBUILD = sphinx-build
27 | PAPER =
28 | BUILDDIR = _build
29 |
30 | PAPEROPT_a4 = -D latex_paper_size=a4
31 | PAPEROPT_letter = -D latex_paper_size=letter
32 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
33 |
34 | clean::
35 | -rm -rf $(BUILDDIR)/
36 |
37 | latex:
38 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
39 | sed -i 's/PDFLATEX = pdflatex/PDFLATEX = texfot pdflatex/' $(BUILDDIR)/latex/Makefile
40 | @echo
41 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
42 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
43 | "(use \`make latexpdf' here to do that automatically)."
44 |
45 | latexpdf:
46 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
47 | sed -i 's/PDFLATEX = pdflatex/PDFLATEX = texfot pdflatex/' $(BUILDDIR)/latex/Makefile
48 | @echo "Running LaTeX files through pdflatex..."
49 | make -C $(BUILDDIR)/latex all-pdf
50 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
51 |
52 | %:
53 | $(SPHINXBUILD) -b $@ $(ALLSPHINXOPTS) $(BUILDDIR)/$@
54 |
--------------------------------------------------------------------------------
/docs/_static/favicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/_static/images/flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chipsalliance/f4pga/0723f9ca4ea4b686ff9c2e32c9cec56d40de1a62/docs/_static/images/flow.png
--------------------------------------------------------------------------------
/docs/_static/images/step.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chipsalliance/f4pga/0723f9ca4ea4b686ff9c2e32c9cec56d40de1a62/docs/_static/images/step.png
--------------------------------------------------------------------------------
/docs/_static/images/tool.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chipsalliance/f4pga/0723f9ca4ea4b686ff9c2e32c9cec56d40de1a62/docs/_static/images/tool.png
--------------------------------------------------------------------------------
/docs/_static/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
67 |
--------------------------------------------------------------------------------
/docs/changes.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2020-2022 F4PGA Authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | from pathlib import Path
21 | from textwrap import indent
22 |
23 | from yaml import load as yaml_load, Loader as yaml_loader
24 |
25 |
26 | ROOT = Path(__file__).resolve().parent
27 |
28 |
29 | def repo_url(repo, value):
30 | ref = value
31 | url = 'commit'
32 | parts = None
33 | if '@' in value:
34 | parts = value.split('@')
35 | if '#' in value:
36 | parts = value.split('#')
37 | if parts is not None:
38 | url = 'pull'
39 | repo = parts[0]
40 | ref = parts[1]
41 | return f'https://github.com/{repo}/{url}/{ref}'
42 |
43 |
44 | def generate_changes_inc():
45 |
46 | with open(ROOT / "changes.yml") as data_file:
47 | changes_yml = yaml_load(data_file, yaml_loader)
48 |
49 | with (ROOT / "development/changes.inc").open("w", encoding="utf-8") as wfptr:
50 | revs = sorted(changes_yml.keys())
51 |
52 | wfptr.write('''
53 | Tested environments
54 | ===================\n
55 | ''')
56 |
57 | for item in reversed(revs[1:]):
58 |
59 | f4pga = changes_yml[revs[item]]['f4pga']
60 | examples = changes_yml[revs[item]]['examples']
61 |
62 | arch_defs = changes_yml[revs[item]]['arch-defs'].split('@')
63 | arch_defs_timestamp = arch_defs[0]
64 | arch_defs_hash = arch_defs[1]
65 |
66 | tarballs = changes_yml[revs[item]]['tarballs']
67 | eos_s3 = ' '.join([f"* ``{item}``\n" for item in tarballs['eos-s3']])
68 | xc7 = ' '.join([f"* ``{item}``\n" for item in tarballs['xc7']])
69 |
70 | wfptr.write(f'''
71 | {revs[item]}
72 | ---
73 |
74 | .. NOTE::
75 | {indent(changes_yml[revs[item]]['description'], ' ')}
76 |
77 | * Examples: `{examples} <{repo_url("chipsalliance/f4pga-examples", examples)}>`__
78 | * CLI: `{f4pga} <{repo_url("chipsalliance/f4pga",f4pga)}>`__
79 | * Architecture Definitions: {arch_defs[0]} @ `{arch_defs[1]} `__
80 |
81 | * xc7
82 |
83 | {xc7}
84 | * eos-s3
85 |
86 | {eos_s3}
87 | ''')
88 |
89 | wfptr.write('''
90 | Future work
91 | ===========\n
92 | ''')
93 | wfptr.write(changes_yml[revs[0]]['future-work'])
94 |
--------------------------------------------------------------------------------
/docs/changes.yml:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2020-2022 F4PGA Authors.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 | # SPDX-License-Identifier: Apache-2.0
16 |
17 | 0:
18 | future-work: |
19 | - `f4pga build` now supports Lattice's ICE40 devices.
20 | - `f4pga build` now supports Lattice's ECP5 devices.
21 | - `f4pga build` now supports Lattice's NX devices.
22 |
23 | 8:
24 | examples: e3a23897c2692d54a3abb57f5199cded6852da60
25 | arch-defs: 20220907-210059@66a976d
26 | f4pga: 6b4976a028e8a8a3b78711b6471655d3bfe58ed7
27 | <<: &tarballs-consistent
28 | tarballs:
29 | xc7:
30 | - symbiflow-install-xc7-*-tar-xz
31 | - symbiflow-arch-defs-xc7a50t_test-*.tar.xz
32 | eos-s3:
33 | - symbiflow-install-ql-*-tar-xz
34 | - symbiflow-ql-eos-s3_wlcsp-*-tar-xz
35 | description: |
36 | * Python scripts from f4pga-arch-defs tarballs were moved to f4pga.
37 |
38 | * Yosys 'conv' and 'synth' TCL scripts combined in a single pipeline.
39 |
40 |
41 | 7:
42 | examples: 954a21090b14808b50d259b552e353b69f0ddae0
43 | arch-defs: 20220818-143856@24e8f73
44 | f4pga: 2d8d85706ecc7a72e17078ea903e2751022825da
45 | <<: *tarballs-consistent
46 | description: |
47 | * Yosys TCL scripts from f4pga-arch-defs tarballs were moved to f4pga as ``f4pga.wrappers.tcl``.
48 |
49 | * Use ``--`` (instead of ``-a|--additional_vpr_options``) to provide additional VPR arguments to the (deprecated)
50 | ``symbiflow_*`` entrypoints.
51 |
52 | * Setting ``FPGA_FAM`` before installing ``f4pga`` is not required anymore.
53 |
54 | * Sources related to ``f4pga build`` were moved to submodule ``f4pga.flows``.
55 |
56 |
57 | 6:
58 | examples: 78b5e8f2845985be0c63631324adc33756de642d
59 | arch-defs: 20220803-160711@df6d9e5
60 | f4pga: e9a520a17a00cfd268f8ee549340aaa297b63da5
61 | <<: *tarballs-consistent
62 | description: |
63 | * Tarballs from f4pga-arch-defs now include usable environment and requirements files which allow bootstraping
64 | minimal Conda environments.
65 |
66 | * The default F4PGA_SHARE_DIR path does not include subdir 'install' by default.
67 |
68 | * F4PGA_BIN_DIR and VPRPATH are not used anymore.
69 |
70 |
71 | 5:
72 | examples: 524dfb6e746b632fdeddc9d06d8a002a3c7a3118
73 | arch-defs: 20220729-181657@7833050
74 | f4pga: c342fc6ff1684f3dc6072713730ac9fc574ab2f3
75 | <<: *tarballs-consistent
76 | description: |
77 | ``f4pga build`` now supports QuickLogic's EOS-S3 devices.
78 |
79 |
80 | 4:
81 | examples: 52bc71a0506cad647951a16fb214ea82934bed0a
82 | arch-defs: 20220729-181657@7833050
83 | f4pga: df55ee75e4a1da978ffa5f7385bed47faf255661
84 | <<: *tarballs-consistent
85 | description: |
86 | The default F4PGA_SHARE_DIR is now consistent for AMD/Xilinx or QuickLogic:
87 | ``F4PGA_INSTALL_DIR / FPGA_FAM / 'install/share/f4pga'``.
88 |
89 |
90 | 3:
91 | examples: d8134c926167a8092d5b392110ce2932bdae8f54
92 | arch-defs: 20220729-181657@7833050
93 | f4pga: 8c411eb74e4bb23d1ec243a1515b9bfb48e2cd83
94 | <<: *tarballs-consistent
95 | description: |
96 | Architecture Definitions:
97 |
98 | * AMD/Xilinx base package is now ``*-install-xc7-*``.
99 |
100 | * QuickLogic now has a base package named ``*-install-ql-*``.
101 |
102 | * Python scripts in QuickLogic packages moved from ``bin/python`` to ``share/f4pga/scripts``.
103 |
104 | * Share paths modified:
105 |
106 | * AND/Xilinx and QuickLogic arch-defs packages homogenized.
107 |
108 | * ``s/symbiflow/f4pga/``
109 |
110 |
111 | 2:
112 | examples: 3e8a003cabec84f7841571a6929fe2623e702e96
113 | arch-defs: 20220721-204939@38358c4
114 | f4pga: 27eca4f35db3c4e04ce255ab319611774de9b9a1
115 | <<: &tarballs-split
116 | tarballs:
117 | xc7:
118 | - symbiflow-arch-defs-install-*.tar.xz
119 | - symbiflow-arch-defs-xc7a50t_test-*.tar.xz
120 | eos-s3:
121 | - quicklogic-arch-defs-qlf-fc5d8da.tar.gz
122 | description: |
123 | * Environment variables were renamed and are now supported by ``f4pga build``:
124 |
125 | * ``s/F4PGA_ENV_BIN/F4PGA_BIN_DIR/``
126 |
127 | * ``s/F4PGA_ENV_SHARE/F4PGA_SHARE_DIR/``
128 |
129 | * f4pga is now pinned in the requirements files of f4pga-examples.
130 |
131 |
132 | 1:
133 | examples: 6b0e7b8a75fb8715bb081cb7f24948c3aec0df31
134 | arch-defs: 20220714-173445@f7afc12
135 | f4pga: 39da9c9f17513ea0c3dd12be655a3684e5b77519
136 | <<: *tarballs-split
137 | description: |
138 | First release being tracked.
139 |
--------------------------------------------------------------------------------
/docs/community.rst:
--------------------------------------------------------------------------------
1 | .. _Community:
2 |
3 | Community
4 | #########
5 |
6 | `FOSS Flows For FPGA (F4PGA) `__ project is a `Workgroup `__
7 | under the `CHIPS Alliance `__.
8 | The F4PGA Workgroup consists of members from different backgrounds, including FPGA vendors
9 | (`Xilinx `__
10 | and `QuickLogic `__),
11 | industrial users
12 | (`Google `__
13 | and `Antmicro `__)
14 | and academia
15 | (`University of Toronto `__),
16 | who collaborate to build a more open source and software-driven FPGA ecosystem (IP, tools and workflows) to drive the
17 | adoption of FPGAs in existing and new use cases, and eliminate barriers of entry.
18 |
19 | Communication
20 | =============
21 |
22 | * `Twitter [@f4pga] `__
23 |
24 | * `Slack [chipsalliance.slack.com] `__
25 |
26 | .. TIP::
27 | To register to CHIPS Alliance Slack workspace, use the following `Slack Invite `__.
28 |
29 | * `IRC [irc.libera.chat/#F4PGA] `__
30 |
31 | * `Mailing list [lists.chipsalliance.org/g/f4pga-wg] `__
32 |
33 | Sources
34 | =======
35 |
36 | * :gh:`github.com/chipsalliance `
37 |
38 | * :gh:`github.com/F4PGA `
39 |
40 | .. _Contributing:
41 |
42 | Contributing
43 | ============
44 |
45 | Are you interested in helping this project move forward?
46 | F4PGA is a collaborative project and we welcome your contributions.
47 | The code is available on GitHub, while the HTML documentation is available on Read The Docs.
48 | There are multiple areas and technologies we need help with - reach out to us, we're sure we will find something for you.
49 |
50 | * Do you know **Python**?
51 | Almost all scripts are written in Python!
52 |
53 | * Do you know **C++**?
54 | VPR & nextpnr & libraries written in C++!
55 |
56 | * Do you know **TCL**?
57 | All the EDA tools use TCL!
58 |
59 | * Do you know **(System) Verilog**, **VHDL**, **Chisel**, **Migen** and/or **Amaranth**?
60 | Simulation and models are written in Hardware Description Languages (HDLs)!
61 |
62 | * Do you know **XML**?
63 | Most file formats are XML!
64 |
65 | * Do you know English?
66 | Documentation is written in English!
67 |
68 | * Do you know **Docker** and/or **Podman**?
69 | Help make it easier to set up F4PGA!
70 |
71 | * Do you have time?
72 | We will find you a task!
73 |
--------------------------------------------------------------------------------
/docs/development/building-docs.rst:
--------------------------------------------------------------------------------
1 | Building the documentation
2 | ##########################
3 |
4 | Activate the virtual environment and install dependencies::
5 |
6 | make env
7 | make enter
8 |
9 | Build the whole documentation::
10 |
11 | make html
12 |
13 | For more options see::
14 |
15 | make help
16 |
--------------------------------------------------------------------------------
/docs/development/changes.rst:
--------------------------------------------------------------------------------
1 | Changes
2 | #######
3 |
4 | .. include:: changes.inc
5 |
--------------------------------------------------------------------------------
/docs/development/venv.rst:
--------------------------------------------------------------------------------
1 | Packages in virtual environment
2 | ###############################
3 |
4 | To install packages in conda environment you can use both
5 | ``conda`` and ``pip``.
6 |
7 | Note that ``pip`` is installed in the conda environment which uses ``python3``,
8 | and is related to ``pip3`` **inside** the virtual environment,
9 | whereas invocation of ``pip3`` directly uses your **system** ``pip3`` instance,
10 | typically located in ``/usr/bin/pip3``
11 |
--------------------------------------------------------------------------------
/docs/environment.yml:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2020-2022 F4PGA Authors.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 | # SPDX-License-Identifier: Apache-2.0
16 |
17 | name: f4pga-docs
18 |
19 | channels:
20 | - conda-forge
21 |
22 | dependencies:
23 | - pygobject
24 | - pip
25 | - pip:
26 | - -r requirements.txt
27 |
--------------------------------------------------------------------------------
/docs/f4pga/DevNotes.md:
--------------------------------------------------------------------------------
1 | # Developer's notes
2 | ##### Last update: 2022-05-06
3 |
4 | :::{warning}
5 | These notes are provided as-is and they shouldn't be treated as a full-blown accurate
6 | documentation, but rather as a helpful resource for those who want to get involved with
7 | development of _f4pga_. These are not updated regularly.
8 |
9 | For more detailed, up-to-date information about the code, refer to the pydoc documentation.
10 | :::
11 |
12 | ## Project's structure
13 |
14 | * `__init__.py` contains the logic and entry point of the build system
15 | * `argparser.py` contains boring code for CLI interface
16 | * `cache.py` contains code needed for tracking modifications in the project.
17 | * `common.py` contains code shared by the main utility and the modules
18 | * `flow_config.py` contains code for reading and accessing flow definitions and configurations
19 | * `module_inspector.py` contains utilities for inspecting I/O of modules
20 | * `module_runner.py` contains code required to load modules at run-time
21 | * `module.py` contains definitions required for writing and using f4pga modules
22 | * `part_db.json` contains mappings from part names to platform names
23 | * `setup.py` contains a package installation script
24 | * `stage.py` contains classes relevant to stage representation
25 | * `modules` contains loadable modules
26 | * `platforms` contains platform flow definitions
27 |
28 | :::{important}
29 | Through the codebase _f4pga_ (tool) might be often referenced as _sfbuild_.
30 | Similarly, _F4PGA_ (toolchain) might get called _Symbiflow_.
31 | This is due to the project being written back when _F4PGA_ was called _Symbiflow_.
32 | :::
33 |
34 | ## Different subsystems and where to find them?
35 |
36 | ### Building and dependency resolution
37 |
38 | All the code regarding dependency resolution is located in `__init__.py` file.
39 | Take a look at the `Flow` class.
40 |
41 | Most of the work is done in `Flow._resolve_dependencies` method. Basically it
42 | performs a _DFS_ with _stages_ (instances of _f4pga modules_) as its nodes
43 | which are linked using symbolic names of dependencies on inputs and outputs.
44 | It queries the modules for information regarding i/o (most importantly the paths
45 | on which they are going to produce outputs), checks whether
46 | their inputs are going to be satisfied, checks if dependencies were modified, etc.
47 |
48 | The actual building is done using `Flow._build_dep` procedure. It uses a similar
49 | _DFS_ approach to invoke modules and check their inputs and outputs.
50 |
51 | ### Modification tracking
52 |
53 | Modification tracking is done by taking, comparing and keeping track of `adler32`
54 | hashes of all dependencies. Each dependency has a set of hashes associated with it.
55 | The reason for having multiple hashes is that a dependency may have multiple
56 | "_consumers_", ie. _stages_ which take it as input. Each hash is associated with
57 | particular consumer. This is necessary, because the system tries to avoid rebuilds
58 | when possible and status of each file (modified/unmodified) may differ in regards
59 | to individual stages.
60 |
61 | Keeping track of status of each file is done using `F4Cache` class, which is
62 | defined in `cache.py` file. `F4Cache` is used mostly inside `Flow`'s methods.
63 |
64 | ### Internal environmental variable system
65 |
66 | _f4pga_ exposes some data to the user as well as reads some using internal
67 | environmental variables. These can be referenced by users in
68 | _platform flow definitions_ and _project flow configurations_ using the
69 | `${variable_name}` syntax when defining values. They can also be read inside
70 | _f4pga modules_ by accessing the `ctx.values` namespace.
71 |
72 | The core of its system is the `ResolutionEnvironemt` class which can be found
73 | inside the `common` module.
74 |
75 | ### Installation
76 |
77 | Check `CMakeLists.txt`.
78 |
79 | ## TODO:
80 |
81 | * Define a clear specification for entries in _platform flow definitions_ and
82 | _platform flow configurations_. Which environmental variables can be accessed
83 | where, and when?
84 |
85 | * Force "_on-demand_" outputs if they are required by another stage.
86 | This may require redesigning the "on-demand" feature, which currently works
87 | by producing a dependency if and only if the user explicitly provides the
88 | path. Otherwise the path is unknown.
89 |
90 | * Make commenting style consistent
91 |
92 | * Document writing flow definitions
93 |
94 | * Extend the metadata system for modules, perhaps make it easier to use.
95 |
96 | * Add missing metadata for module targets.
97 |
98 | * (_suggestion_) Generate platform definitions using CMake.
99 |
100 | ### Out of the current scope
101 |
102 | * Change interfaces of some internal python scripts. This could lead to possibly
103 | merging some modules for XC7 and Quicklogic into one common module.
104 |
--------------------------------------------------------------------------------
/docs/f4pga/browse_pydoc.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | #
3 | # Copyright (C) 2020-2022 F4PGA Authors.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | #
17 | # SPDX-License-Identifier: Apache-2.0
18 |
19 | MY_DIR=`dirname $0`
20 | SFBUILD_DIR=${MY_DIR}/../../f4pga
21 | SFBUILD_PY=${SFBUILD_DIR}/__init__.py
22 |
23 | PYTHONPATH=${SFBUILD_DIR} pydoc -b
24 |
--------------------------------------------------------------------------------
/docs/f4pga/index.rst:
--------------------------------------------------------------------------------
1 | .. _pyF4PGA:
2 |
3 | Overview
4 | ########
5 |
6 | Python F4PGA is a package containing multiple modules to facilitate the usage of all the tools integrated in the F4PGA
7 | ecosystem, and beyond.
8 | The scope of Python F4PGA is threefold:
9 |
10 | * Provide a fine-grained *pythonic* interface to the tools and utilities available as either command-line interfaces
11 | (CLIs) or application proggraming interfaces (APIs) (either web or through shared libraries).
12 | * Provide a CLI entrypoint covering the whole flows for end-users to produce bitstreams from HDL and/or software sources.
13 | * Provide a CLI entrypoint for developers contributing to bitstream documentation and testing (continuous integration).
14 |
15 | .. ATTENTION::
16 | This is work-in-progress to adapt and organize the existing shell/bash based plumbing from multiple F4PGA repositories.
17 | Therefore, it's still a *pre-alpha* and the codebase, commands and flows are subject to change.
18 | It is strongly suggested not to rely on Python F4PGA until this note is updated/removed.
19 |
20 | References
21 | ==========
22 |
23 | * :gh:`chipsalliance/fpga-tool-perf#390@issuecomment-1023487178 `
24 | * :ghsharp:`2225`
25 | * :ghsharp:`2371`
26 | * :ghsharp:`2455`
27 | * `F4PGA GSoC 2022 project ideas: Generalization of wrapper scripts for installed F4PGA toolchain and making them OS agnostic `__
28 | * :gh:`FuseSoc ` | :gh:`Edalize `
29 | * `Electronic Design Automation Abstraction (EDA²) `__
30 |
--------------------------------------------------------------------------------
/docs/f4pga/modules/fasm.md:
--------------------------------------------------------------------------------
1 | # fasm
2 |
3 | The _fasm_ module generates FPGA assembly using `genfasm` (VPR-only).
4 |
5 | The module should guarantee the following outputs:
6 | * `fasm`
7 |
8 | For detailed information about these targets, please refer to
9 | `docs/common targets and variables.md`
10 |
11 | The setup of the synth module follows the following specifications:
12 |
13 | ## Values
14 |
15 | The `fasm` module accepts the following values:
16 |
17 | * `pnr_corner` (string, optional): PnR corner to use. Relevant only for Quicklogic's
18 | eFPGAs.
--------------------------------------------------------------------------------
/docs/f4pga/modules/generic_script_wrapper.md:
--------------------------------------------------------------------------------
1 | # generic_script_wrapper
2 |
3 | This module provides a way to integrate an external command into an f4pga flow.
4 | Its inputs and outputs are fully defined by the author of flow definition.
5 |
6 | ## Parameters
7 |
8 | Parameters are everything when it comes to this module:
9 |
10 | * `stage_name` (string, optional): Name describing the stage
11 | * `script` (string, mandatory): Path to the script to be executed
12 | * `interpreter` (string, optional): Interpreter for the script
13 | * `cwd` (string, optional): Current Working Directory for the script
14 | * `outputs` (dict[string -> dict[string -> string]],
15 | mandatory):
16 | A dict with output descriptions (dicts). Keys name output dependencies.
17 | * `mode` (string, mandatory): "file" or "stdout". Describes how the output is
18 | grabbed from the script.
19 | * `file` (string, required if `mode` is "file"): Name of the file generated by the
20 | script.
21 | * `target` (string, required): Default name of the file of the generated
22 | dependency. You can use all values available during map_io stage. Each input
23 | dependency also gets two extra values associated with it:
24 | `:dependency_name[noext]`, which contains the path to the dependency the
25 | extension with anything after last "." removed and `:dependency_name[dir]` which
26 | contains directory paths of the dependency. This is useful for deriving an output
27 | name from the input.
28 | * `meta` (string, optional): Description of the output dependency.
29 | * `inputs` (dict[string -> string | bool], mandatory):
30 | A dict with input descriptions. Key is can be a name of a named argument, a
31 | position of unnamed argument, when prefaced with "#" (eg. "#1"), or a name of an
32 | environmental variable, when prefaced with "$". Positions are indexed
33 | from 1, as it's a convention that 0th argument is the path of the executed program.
34 | Values are strings that can contain references to variables to be resolved
35 | after the project flow configuration is loaded (that means they can reference
36 | values and dependencies which are to be set by the user). All of modules inputs
37 | will be determined by the references used. Thus dependency and value definitions
38 | are implicit. If the value of the resolved string is empty and is associated with a
39 | named argument, the argument in question will be skipped entirely. This allows
40 | using optional dependencies. To use a named argument as a flag instead, set it to
41 | `true`.
42 |
--------------------------------------------------------------------------------
/docs/f4pga/modules/io_rename.md:
--------------------------------------------------------------------------------
1 | # io_rename
2 |
3 | This module provides a way to rename (ie. change) dependencies and values of an
4 | instance of a different module. It wraps another, module whose name is specified in `params.module` and changes the names of the dependencies and values it relies on.
5 |
6 | ## Parameters
7 |
8 | * `module` (string, required) - name of the wrapped module
9 | * `params` (dict[string -> any], optional): parameters passed to the wrapped
10 | module instance.
11 | * `rename_takes` (dict[string -> string]) - mapping for inputs ("takes")
12 | * `rename_produces` (dict[string -> string]) - mapping for outputs ("products")
13 | * `rename_values` (dict[string -> string]) - mapping for values
14 |
15 | In the three mapping dicts, keys represent the names visible to the wrapped module
16 | and values represent the names visible to the modules outside.
17 | Not specifying a mapping for a given entry will leave it with its original name.
18 |
19 | ## Values
20 |
21 | All values specified for this modules will be accessible by the wrapped module.
22 |
23 | ## Extra notes
24 |
25 | This module might be removed in the future in favor of a native renaming support.
26 |
--------------------------------------------------------------------------------
/docs/f4pga/modules/mkdirs.md:
--------------------------------------------------------------------------------
1 | # mkdirs
2 |
3 | This modules creates directories specified by the author of flow definition
4 | as its targets..
5 |
6 | ## Parameters
7 |
8 | Each key serves as a name of a directory to be created, while the value is the
9 | path for that directory.
--------------------------------------------------------------------------------
/docs/f4pga/modules/pack.md:
--------------------------------------------------------------------------------
1 | # pack
2 |
3 | :::{warning}
4 | this page is under construction
5 | :::
6 |
7 | Pack circuit with VPR.
8 |
--------------------------------------------------------------------------------
/docs/f4pga/modules/place.md:
--------------------------------------------------------------------------------
1 | # place
2 |
3 | :::{warning}
4 | this page is under construction
5 | :::
6 |
7 | Place cells with VPR.
8 |
--------------------------------------------------------------------------------
/docs/f4pga/modules/place_constraints.md:
--------------------------------------------------------------------------------
1 | # place_constraints
2 |
3 | :::{warning}
4 | this page is under construction
5 | :::
6 |
7 | Move cell placement to satisfy constraints imposed by an architecture. (VPR-only)
8 |
9 | :::{note}
10 | This will be deprecated once VPR constraint system supports this functionality natively.
11 | :::
12 |
--------------------------------------------------------------------------------
/docs/f4pga/modules/route.md:
--------------------------------------------------------------------------------
1 | # route
2 |
3 | :::{warning}
4 | this page is under construction
5 | :::
6 |
7 | Route a design with VPR.
8 |
--------------------------------------------------------------------------------
/docs/f4pga/modules/synth.md:
--------------------------------------------------------------------------------
1 | # synth
2 |
3 | The _synth_ module is meant to be used to execute YOSYS synthesis.
4 |
5 | The module should guarantee the following outputs:
6 | * `eblif`
7 | * `fasm_extra` (can be empty)
8 | * `json`
9 | * `synth_json`
10 | * `synth_log` (on demand)
11 |
12 | For detailed information about these targets, please refer to
13 | `docs/common targets and variables.md`
14 |
15 | What files and how are they generated is dependent on TCL scripts executed
16 | withing YOSYS and the script vary depending on the target platform. Due to this
17 | design choice it is required for the author of the flow definition to parameterize
18 | the `synth` module in a way that will **GUARANTEE** the targets mentioned above
19 | will be generated upon a successful YOSYS run.
20 |
21 | The setup of the synth module follows the following specifications:
22 |
23 | ## Parameters
24 |
25 | The `params` section of a stage configuration may contain a `produces` list.
26 | The list should specify additional targets that will be generated
27 | (`?` qualifier is allowed).
28 |
29 | ## Values
30 |
31 | The `synth` module requires the following values:
32 |
33 | * `tcl_scripts` (string, required): A path to a directory containing `synth.tcl`
34 | and `conv.tcl` scripts that will be used by YOSYS.
35 | * `read_verilog_args` (list[string | number], optional) - If specified, the Verilog
36 | sources will be read using the `read_verilog` procedure with options contained in
37 | this value.
38 | * `yosys_tcl_env` (dict[string -> string | list[string], required) - A mapping that
39 | defines environmental variables that will be used within the TCL scripts. This
40 | should contain the references to module's inputs and outputs in order to guarantee
41 | the generation of the desired targets.
42 |
--------------------------------------------------------------------------------
/docs/flows/bitstream.rst:
--------------------------------------------------------------------------------
1 | Bitstream translation
2 | #####################
3 |
4 | The routing process results in an output file specifying the used blocks
5 | and routing paths. It contains the resources that needs to be instantiated
6 | on the FPGA chip, however, the output format is not understood
7 | by the FPGA chip itself.
8 |
9 | In the last step, the description of the chip is translated into
10 | the appropriate format, suitable for the chosen FPGA.
11 | That final file contains instructions readable by the configuration block of
12 | the desired chip.
13 |
14 | Documenting the bitstream format for different FPGA chips is one of the
15 | most important tasks in the F4PGA Project!
16 |
--------------------------------------------------------------------------------
/docs/flows/index.rst:
--------------------------------------------------------------------------------
1 | .. _Flows:
2 |
3 | Introduction
4 | ============
5 |
6 | This section provides a description of the F4PGA toolchain as well as the basic concepts of the FPGA design flow.
7 |
8 | F4PGA is an end-to-end FPGA synthesis toolchain, because of that it provides all the necessary tools to convert input
9 | Hardware Description Language (HDL) sources into a final bitstream.
10 | It is simple to use however, the whole synthesis and implementation process is not trivial.
11 |
12 | The final bitstream format depends on the used platform.
13 | What's more, every platform has different resources and even if some of them provide similar functionality, they can be
14 | implemented in a different way.
15 | In order to be able to match all that variety of possible situations, the creation of the final bitstream is divided
16 | into few steps.
17 | F4PGA uses different programs to create the bitstream and is responsible for their proper integration.
18 | The procedure of converting HDL files into the bitstream is described in the next sections.
19 |
20 | .. figure:: ../_static/images/toolchain-flow.svg
21 | :align: center
22 |
23 | F4PGA Toolchain design flow
24 |
--------------------------------------------------------------------------------
/docs/flows/pnr.rst:
--------------------------------------------------------------------------------
1 | Place & Route
2 | #############
3 |
4 | The Synthesis process results in an output containing logical elements
5 | available on the desired FPGA chip with the specified connections between them.
6 | However, it does not specify the physical layout of those elements in the
7 | final design. The goal of the Place and Route (PnR) process is to take the
8 | synthesized design and implement it into the target FPGA device. The PnR tool
9 | needs to have information about the physical composition of the device, routing
10 | paths between the different logical blocks and signal propagation timings.
11 | The working flow of different PnR tools may vary, however, the process presented
12 | below represents the typical one, adopted by most of these tools. Usually, it
13 | consists of four steps - packing, placing, routing and analysis.
14 |
15 | Packing
16 | =======
17 |
18 | In the first step, the tool collects and analyzes the primitives present
19 | in the synthesized design (e.g. Flip-Flops, Muxes, Carry-chains, etc), and
20 | organizes them in clusters, each one belonging to a physical tile of the device.
21 | The PnR tool makes the best possible decision, based on the FPGA routing
22 | resources and timings between different points in the chip.
23 |
24 | Placing
25 | =======
26 |
27 | After having clustered all the various primitives into the physical tiles of the
28 | device, the tool begins the placement process. This step consists in assigning a
29 | physical location to every cluster generated in the packing stage. The choice of
30 | the locations is based on the chosen algorithm and on the user's parameters, but
31 | generally, the final goal is to find the best placement that allows the routing
32 | step to find more optimal solutions.
33 |
34 | Routing
35 | =======
36 |
37 | Routing is one of the most demanding tasks of the whole process.
38 | All possible connections between the placed blocks and the information on
39 | the signals propagation timings, form a complex graph.
40 | The tool tries to find the optimal path connecting all the placed
41 | clusters using the information provided in the routing graph. Once all the nets
42 | have been routed, an output file containing the implemented design is produced.
43 |
44 | Analysis
45 | ========
46 |
47 | This last step usually checks the whole design in terms of timings and power
48 | consumption.
49 |
--------------------------------------------------------------------------------
/docs/flows/synthesis.rst:
--------------------------------------------------------------------------------
1 | Synthesis
2 | #########
3 |
4 | Synthesis is the process of converting input Verilog file into a netlist,
5 | which describes the connections between different block available on the
6 | desired FPGA chip. However, it is worth to notice that these are only
7 | logical connections. So the synthesized model is only a draft of the final
8 | design, made with the use of available resources.
9 |
10 | RTL Generation
11 | ==============
12 |
13 | the input Verilog file is often really complicated. Usually it is written in
14 | a way that it is hard to distinguish the digital circuit standing behind
15 | the implemented functionality. Designers often use a so-called
16 | *Behavioral Level* of abstraction, in their designs, which means that the whole
17 | description is mostly event-driven. In Verilog, support for behavioral models
18 | is made with use of ``always`` statements.
19 |
20 | However, FPGA mostly consist of Look Up Tables (LUT) and flip-flops.
21 | Look Up Tables implement only the functionality of logic gates.
22 | Due to that, the synthesis process has to convert the complicated
23 | Behavioral model to a simpler description.
24 |
25 | Firstly, the design is described in terms of registers and logical operations.
26 | This is the so-called *Register-Transfer Level* (*RTL*).
27 | Secondly, in order to simplify the design even more, some complex logic is
28 | rewritten in the way that the final result contain only logic gates
29 | and registers. This model is on *Logical Gate level* of abstraction.
30 |
31 | The process of simplification is quite complicated, because of that it often
32 | demands additional simulations between mentioned steps to prove that the input
33 | design is equivalent to its simplified form.
34 |
35 | Technology mapping
36 | ==================
37 |
38 | FPGAs from different architectures may have different architecture. For example,
39 | they may contain some complicated functional blocks (i.e. RAM, DSP blocks)
40 | and even some of the basic blocks like LUT tables and flip-flops may vary
41 | between chips. Because of that, there is a need to describe the final design
42 | in terms of platform-specific resources. This is the next step in the process
43 | of synthesis. The simplified description containing i.e. logic gates, flip-flops
44 | and a few more complicated blocks like RAM is taken and used "general" blocks
45 | are substituted with that physically located in the chosen FPGA.
46 | The vendor-specific definitions of these blocks are often located
47 | in a separate library.
48 |
49 | Optimization
50 | ============
51 |
52 | Optimization is the key factor that allows to better utilize resources
53 | of an FPGA. There are some universal situations in which the design
54 | can be optimized, for example by substituting a bunch of logic gates
55 | in terms of fewer, different gates. However, some operations can be performed
56 | only after certain steps i.e. after technology mapping.
57 | As a result, optimization is an integral part of most of the synthesis steps.
58 |
--------------------------------------------------------------------------------
/docs/how.rst:
--------------------------------------------------------------------------------
1 | How it works
2 | ############
3 |
4 | To understand how F4PGA works, it is best to start with an overview of the general EDA tooling ecosystem and then
5 | proceed to see what the F4PGA project consists of.
6 | For both ASIC- and FPGA-oriented EDA tooling, there are three major areas that the workflows need to cover: description,
7 | frontend and backend.
8 |
9 | .. image:: _static/images/EDA.svg
10 | :align: center
11 |
12 | Hardware description languages are either established (such as Verilog and `VHDL ➚ `__) or
13 | emerging software-inspired paradigms like
14 | `Chisel ➚ `_,
15 | `SpinalHDL ➚ `_,
16 | `Migen ➚ `_, or
17 | :gh:`Amaranth ➚ `.
18 | Since early 2000s, free and open source tools allow simulating HDLs.
19 | However, for several decades the major problem lay in the frontend and backend, where there was no established
20 | standard vendor-neutral tooling that would cover all the necessary components for an end-to-end flow.
21 | This pertains both to ASIC and FPGA workflows.
22 | Although F4PGA focuses on the latter, some parts of F4PGA will also be useful in the former.
23 |
24 | To achieve F4PGA's goal of a complete FOSS FPGA toolchain, a number of tools and projects are necessary to provide all
25 | the needed components of an end-to-end flow.
26 | The F4PGA toolchains consist of logic synthesis and implementation tools, as well as chip documentation projects for
27 | chips of various vendors.
28 | Thus, F4PGA serves as an umbrella project for several activities.
29 |
30 | .. image:: _static/images/parts.svg
31 | :align: center
32 |
33 | The central resources are the so-called FPGA "architecture definitions" (i.e. documentation of how specific FPGAs work
34 | internally) and the "interchange schema" (for logical and physical netlists).
35 | Those definitions serve as input to frontend and backend tools, such as
36 | `Yosys ➚ `__,
37 | :gh:`nextpnr ➚ ` and `Verilog to Routing ➚ `_.
38 | They are created within separate collaborating projects targeting different FPGAs:
39 |
40 | * :doc:`Project X-Ray ➚ ` for Xilinx 7-Series
41 | * `Project IceStorm ➚ `__ for Lattice iCE40
42 | * :doc:`Project Trellis ➚ ` for Lattice ECP5 FPGAs
43 |
44 | More information can be found at :doc:`F4PGA Architecture Definitions ➚ ` and :doc:`FPGA Interchange ➚ `.
45 |
46 | To prepare a working bitstream for a particular FPGA chip, the toolchain goes through the following stages:
47 |
48 | * A description of the FPGA chip is created with the information from the relevant bitstream documentation
49 | project.
50 | This part is done within the :gh:`F4PGA Architecture Definitions ➚ `.
51 | The project prepares information about the timings and resources available in the chip needed at the implementation
52 | stage, as well as techmaps for the synthesis tools.
53 |
54 | .. NOTE::
55 | This stage is typically pre-built and installed as assets.
56 | However, developers contributing to the bitstream documentation might build it.
57 |
58 | * Then, logic synthesis is carried out in the `Yosys ➚ `__ framework, which expresses the
59 | user-provided hardware description by means of the block and connection types available in the chosen chip.
60 |
61 | * The next step is implementation.
62 | Placement and routing tools put individual blocks from the synthesis description in specific chip locations and create
63 | paths between them.
64 | To do that, F4PGA uses either :gh:`nextpnr ➚ ` or :gh:`Verilog to Routing ➚ `.
65 |
66 | * Finally, the design properties are translated into a set of features available in the given FPGA chip.
67 | These features are saved in the :gh:`FASM format ➚ `, which is developed as part of F4PGA.
68 | The FASM file is then translated to a bitstream, using the information from the bitstream documentation projects.
69 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | FOSS Flows For FPGA
2 | ###################
3 |
4 | `F4PGA ➚ `__, which is a Workgroup under the `CHIPS Alliance ➚ `__, is an
5 | Open Source solution for Hardware Description Language (HDL) to Bitstream FPGA synthesis, currently targeting
6 | Xilinx's 7-Series, QuickLogic's EOS-S3, and Lattice' iCE40 and ECP5 devices.
7 | Think of it as the GCC of FPGAs.
8 | The project aims to design tools that are highly extendable and multiplatform.
9 |
10 | .. image:: _static/images/hero.svg
11 | :align: center
12 |
13 | The elements of the project include (but are not limited to):
14 |
15 | * The F4PGA open source FPGA toolchains for programming FPGAs (formerly known as :gh:`SymbiFlow ➚ `):
16 |
17 | * :gh:`F4PGA Python CLI ➚ `
18 | * :gh:`F4PGA Architecture Definitions ➚ `
19 | * :gh:`F4PGA Examples ➚ `
20 | * :gh:`F4PGA Yosys plugins ➚ `
21 |
22 | * The FPGA interchange format (an interchange format defined by CHIPS Alliance to enable interoperability between
23 | different FPGA tools) adopted by the F4PGA toolchain:
24 |
25 | * :gh:`FPGA Interchange schema ➚ `
26 | * :gh:`FPGA Interchange Python utilities ➚ `
27 | * :gh:`FPGA Interchange Test suite ➚ `
28 |
29 | * The :gh:`FPGA tool performance framework ➚ ` framework for benchmarking
30 | designs against various FPGA tools, and vice versa, over time.
31 |
32 | * FPGA visualisation tools for visual exploration of FPGA bitstream and databases:
33 |
34 | * :gh:`F4PGA bitstream viewer ➚ `
35 | * :gh:`F4PGA database visualizer ➚ `
36 |
37 | * Other utilities (FPGA assembly format, documentation and other):
38 |
39 | * :gh:`F4PGA Assembly (FASM) ➚ `
40 | * :gh:`Xilinx bitstream generation library ➚ `
41 | * :gh:`Verilog-to-routing XML utilities ➚ `
42 | * :gh:`SDF format utilities ➚ `
43 | * :gh:`F4PGA tools data manager ➚ `
44 | * :gh:`F4PGA Sphinx Theme ➚ `
45 | * :gh:`F4PGA Sphinx HDL diagrams ➚ `
46 | * :gh:`F4PGA Sphinx Verilog domain ➚ `
47 |
48 |
49 | Table of Contents
50 | =================
51 |
52 | .. toctree::
53 | :caption: About F4PGA
54 |
55 | getting-started
56 | how
57 | status
58 | community
59 |
60 |
61 | .. toctree::
62 | :caption: Python utils
63 | :maxdepth: 2
64 |
65 | f4pga/index
66 | f4pga/Usage
67 | f4pga/modules/index
68 | f4pga/DevNotes
69 | f4pga/Deprecated
70 |
71 |
72 | .. toctree::
73 | :caption: Development
74 |
75 | development/changes
76 | development/building-docs
77 | development/venv
78 |
79 |
80 | .. toctree::
81 | :caption: Design Flows
82 |
83 | flows/index
84 | flows/synthesis
85 | flows/pnr
86 | flows/bitstream
87 | flows/f4pga
88 |
89 |
90 | .. toctree::
91 | :caption: Specifications
92 |
93 | FPGA Assembly (FASM) ➚
94 | FPGA Interchange schema ➚
95 |
96 |
97 | .. toctree::
98 | :caption: Appendix
99 |
100 | glossary
101 | references
102 |
--------------------------------------------------------------------------------
/docs/references.rst:
--------------------------------------------------------------------------------
1 | .. _References:
2 |
3 | References
4 | ##########
5 |
6 | .. bibliography::
7 | :notcited:
8 | :labelprefix: R
9 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | myst-parser
2 | pyyaml
3 | sphinx>=4.5.0
4 | sphinxcontrib-bibtex
5 | https://github.com/f4pga/sphinx_f4pga_theme/archive/f4pga.zip#sphinx-f4pga-theme
6 | https://github.com/SymbiFlow/sphinx-verilog-domain/archive/master.zip#sphinx-verilog-domain
7 | tabulate
8 |
--------------------------------------------------------------------------------
/docs/status.rst:
--------------------------------------------------------------------------------
1 | Supported Architectures
2 | #######################
3 |
4 |
5 | * `Xilinx 7-Series `__:
6 | the most popular Xilinx FPGA family.
7 |
8 | * `Lattice ice40 `__:
9 | world's smallest FPGAs for mobile devices.
10 |
11 | * `Lattice ecp5 `__:
12 | low cost FPGAs with high performance features.
13 |
14 | * `QuickLogic EOS S3 `__:
15 | FPGA + CPU sensor processing platform.
16 |
17 | * `QuickLogic QLF K4N8 `__:
18 | a 24x24 eFPGA with 6144 flip-flops, 4608 LUT4s, adder and shift-register support.
19 |
20 | * Do you want to add more? :ref:`Help us! `
21 |
22 |
23 | Bitstream documentation
24 | =======================
25 |
26 | .. include:: status.inc
27 |
28 | Boards
29 | ======
30 |
31 | See `f4pga.org: Supported boards `__.
32 |
--------------------------------------------------------------------------------
/f4pga/__main__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | from f4pga.flows import main
21 |
22 |
23 | if __name__ == "__main__":
24 | main()
25 |
--------------------------------------------------------------------------------
/f4pga/context.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | from pathlib import Path
21 | from os import environ
22 |
23 |
24 | FPGA_FAM = environ.get("FPGA_FAM", "xc7")
25 | if FPGA_FAM not in ["xc7", "eos-s3", "qlf_k4n8", "ice40"]:
26 | raise (Exception(f"Unsupported FPGA_FAM <{FPGA_FAM}>!"))
27 |
28 | F4PGA_DEBUG = environ.get("F4PGA_DEBUG")
29 |
30 | install_dir = environ.get("F4PGA_INSTALL_DIR")
31 | if install_dir is None:
32 | default_install_dir = Path("/usr/local")
33 | if F4PGA_DEBUG is not None:
34 | print("Environment variable F4PGA_INSTALL_DIR is undefined!")
35 | print(f"Using default {default_install_dir}")
36 | F4PGA_INSTALL_DIR = default_install_dir
37 | else:
38 | F4PGA_INSTALL_DIR = Path(install_dir)
39 |
40 | F4PGA_SHARE_DIR = Path(environ.get("F4PGA_SHARE_DIR", F4PGA_INSTALL_DIR / FPGA_FAM / "share/f4pga"))
41 |
--------------------------------------------------------------------------------
/f4pga/flows/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | """
21 | F4PGA Build System
22 |
23 | This tool allows for building FPGA targets (such as bitstreams) for any supported platform with just one simple command
24 | and a project file.
25 |
26 | The idea is that F4PGA wraps all the tools needed by different platforms in "modules", which define inputs/outputs and
27 | various parameters.
28 | This allows F4PGA to resolve dependencies for any target provided that a "flow definition" file exists for such target.
29 | The flow defeinition file list modules available for that platform and may tweak some settings of those modules.
30 |
31 | A basic example of using F4PGA:
32 |
33 | $ f4pga build --flow flow.json --part XC7A35TCSG324-1 -t bitstream
34 |
35 | This will make F4PGA attempt to create a bitstream for arty_35 platform.
36 | ``flow.json`` is a flow configuration file, which should be created for a project that uses F4PGA.
37 | Contains project-specific definitions needed within the flow, such as list of source code files.
38 | """
39 |
40 | from typing import Iterable
41 |
42 | from f4pga.flows.stage import Stage
43 | from f4pga.flows.common import set_verbosity_level, sfprint
44 | from f4pga.flows.argparser import setup_argparser
45 | from f4pga.flows.commands import cmd_build, cmd_show_dependencies, f4pga_done
46 |
47 |
48 | def platform_stages(platform_flow, r_env):
49 | """Iterates over all stages available in a given flow."""
50 |
51 | stage_options = platform_flow.get("stage_options")
52 | for stage_name, modulestr in platform_flow["stages"].items():
53 | mod_opts = stage_options.get(stage_name) if stage_options else None
54 | yield Stage(stage_name, modulestr, mod_opts, r_env)
55 |
56 |
57 | def get_stage_values_override(og_values: dict, stage: Stage):
58 | values = og_values.copy()
59 | values.update(stage.value_ovds)
60 | return values
61 |
62 |
63 | def prepare_stage_io_input(stage: Stage):
64 | return {"params": stage.params} if stage.params is not None else {}
65 |
66 |
67 | def main():
68 | parser = setup_argparser()
69 | args = parser.parse_args()
70 |
71 | set_verbosity_level(args.verbose - (1 if args.silent else 0))
72 |
73 | if args.command == "build":
74 | cmd_build(args)
75 | f4pga_done()
76 |
77 | if args.command == "showd":
78 | cmd_show_dependencies(args)
79 | f4pga_done()
80 |
81 | sfprint(0, "Please use a command.\nUse `--help` flag to learn more.")
82 | f4pga_done()
83 |
84 |
85 | if __name__ == "__main__":
86 | main()
87 |
--------------------------------------------------------------------------------
/f4pga/flows/cache.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | from pathlib import Path
21 | from zlib import adler32 as zlib_adler32
22 | from json import dump as json_dump, load as json_load, JSONDecodeError
23 |
24 | from f4pga.flows.common import sfprint
25 |
26 |
27 | class F4Cache:
28 | """
29 | `F4Cache` is used to track changes among dependencies and keep the status of the files on a persistent storage.
30 | Files which are tracked get their checksums calculated and stored in a file.
31 | If file's checksum differs from the one saved in a file, that means, the file has changed.
32 | """
33 |
34 | hashes: "dict[str, dict[str, str]]"
35 | current_hashes: "dict[str, str]"
36 | status: "dict[str, str]"
37 | cachefile_path: str
38 |
39 | def __init__(self, cachefile_path):
40 | """
41 | `chachefile_path` - path to a file used for persistent storage of checksums.
42 | """
43 |
44 | self.status = {}
45 | self.current_hashes = {}
46 | self.cachefile_path = cachefile_path
47 | self.load()
48 |
49 | def _try_pop_consumer(self, path: str, consumer: str):
50 | if self.status.get(path) and self.status[path].get(consumer):
51 | self.status[path].pop(consumer)
52 | if len(self.status[path]) == 0:
53 | self.status.pop(path)
54 | if self.hashes.get(path) and self.hashes[path].get(consumer):
55 | self.hashes[path].pop(consumer)
56 | if len(self.hashes[path]) == 0:
57 | self.hashes.pop(path)
58 |
59 | def _try_push_consumer_hash(self, path: str, consumer: str, hash):
60 | if not self.hashes.get(path):
61 | self.hashes[path] = {}
62 | self.hashes[path][consumer] = hash
63 |
64 | def _try_push_consumer_status(self, path: str, consumer: str, status):
65 | if not self.status.get(path):
66 | self.status[path] = {}
67 | self.status[path][consumer] = status
68 |
69 | def process_file(self, path: Path):
70 | """Process file for tracking with f4cache."""
71 |
72 | if path.is_dir():
73 | # Directories always get '0' hash.
74 | hash = 0
75 | else:
76 | with path.open("rb") as rfptr:
77 | hash = zlib_adler32(rfptr.read())
78 |
79 | self.current_hashes[path.as_posix()] = hash
80 |
81 | def update(self, path: Path, consumer: str):
82 | """Add/remove a file to.from the tracked files, update checksum if necessary and calculate status.
83 |
84 | Multiple hashes are stored per file, one for each consumer module.
85 | "__target" is used as a convention for a "fake" consumer in case the file is requested as a target and not used
86 | by a module within the active flow.
87 | """
88 |
89 | posix_path = path.as_posix()
90 |
91 | assert self.current_hashes.get(posix_path) is not None
92 |
93 | if not path.exists():
94 | self._try_pop_consumer(posix_path, consumer)
95 | return True
96 |
97 | hash = self.current_hashes[posix_path]
98 | last_hashes = self.hashes.get(posix_path)
99 | last_hash = None if last_hashes is None else last_hashes.get(consumer)
100 |
101 | if hash != last_hash:
102 | self._try_push_consumer_status(posix_path, consumer, "changed")
103 | self._try_push_consumer_hash(posix_path, consumer, hash)
104 | return True
105 | self._try_push_consumer_status(posix_path, consumer, "same")
106 | return False
107 |
108 | def get_status(self, path: str, consumer: str):
109 | """Get status for a file with a given path.
110 | returns 'untracked' if the file is not tracked.
111 | """
112 |
113 | assert self.current_hashes.get(path) is not None
114 |
115 | statuses = self.status.get(path)
116 | if not statuses:
117 | hashes = self.hashes.get(path)
118 | if hashes is not None:
119 | last_hash = hashes.get(consumer)
120 | if last_hash is not None:
121 | if self.current_hashes[path] != last_hash:
122 | return "changed"
123 | return "same"
124 | return "untracked"
125 | status = statuses.get(consumer)
126 | if not status:
127 | return "untracked"
128 | return status
129 |
130 | def load(self):
131 | """Loads cache's state from the persistent storage"""
132 |
133 | try:
134 | with Path(self.cachefile_path).open("r") as rfptr:
135 | self.hashes = json_load(rfptr)
136 | except JSONDecodeError:
137 | sfprint(
138 | 0,
139 | f"WARNING: `{self.cachefile_path}` f4cache is corrupted!\n"
140 | "This will cause flow to re-execute from the beginning.",
141 | )
142 | self.hashes = {}
143 | except FileNotFoundError:
144 | sfprint(
145 | 0,
146 | f"Couldn't open `{self.cachefile_path}` cache file.\n"
147 | "This will cause flow to re-execute from the beginning.",
148 | )
149 | self.hashes = {}
150 |
151 | def save(self):
152 | """Saves cache's state to the persistent storage."""
153 | with Path(self.cachefile_path).open("w") as wfptr:
154 | json_dump(self.hashes, wfptr, indent=4)
155 |
--------------------------------------------------------------------------------
/f4pga/flows/inspector.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | from colorama import Style
21 |
22 | from f4pga.flows.module import Module
23 | from f4pga.flows.common import decompose_depname
24 |
25 |
26 | def p_get_if_qualifier(deplist: "list[str]", qualifier: str):
27 | for dep_name in deplist:
28 | name, q = decompose_depname(dep_name)
29 | if q == qualifier:
30 | yield f"● {Style.BRIGHT}{name}{Style.RESET_ALL}"
31 |
32 |
33 | def p_list_if_qualifier(deplist: "list[str]", qualifier: str, indent: int = 4):
34 | indent_str = "".join([" " for _ in range(0, indent)])
35 | r = ""
36 |
37 | for line in p_get_if_qualifier(deplist, qualifier):
38 | r += indent_str + line + "\n"
39 |
40 | return r
41 |
42 |
43 | def get_module_info(module: Module) -> str:
44 | r = ""
45 | r += f"Module `{Style.BRIGHT}{module.name}{Style.RESET_ALL}`:\n"
46 | r += "Inputs:\n Required:\n Dependencies\n"
47 | r += p_list_if_qualifier(module.takes, "req", indent=6)
48 | r += " Values:\n"
49 | r += p_list_if_qualifier(module.values, "req", indent=6)
50 | r += " Optional:\n Dependencies:\n"
51 | r += p_list_if_qualifier(module.takes, "maybe", indent=6)
52 | r += " Values:\n"
53 | r += p_list_if_qualifier(module.values, "maybe", indent=6)
54 | r += "Outputs:\n Guaranteed:\n"
55 | r += p_list_if_qualifier(module.produces, "req", indent=4)
56 | r += " On-demand:\n"
57 | r += p_list_if_qualifier(module.produces, "demand", indent=4)
58 | r += " Not guaranteed:\n"
59 | r += p_list_if_qualifier(module.produces, "maybe", indent=4)
60 |
61 | return r
62 |
--------------------------------------------------------------------------------
/f4pga/flows/module.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | """
21 | Here are the things necessary to write an F4PGA Module.
22 | """
23 |
24 | from types import SimpleNamespace
25 | from abc import abstractmethod
26 |
27 | from f4pga.flows.common import decompose_depname, ResolutionEnv, fatal
28 |
29 |
30 | class Module:
31 | """
32 | A `Module` is a wrapper for whatever tool is used in a flow.
33 | Modules can request dependencies, values and are guranteed to have all the required ones present when entering
34 | `exec` mode.
35 | They also have to specify what dependencies they produce and create the files for these dependencies.
36 | """
37 |
38 | no_of_phases: int
39 | name: str
40 | takes: "list[str]"
41 | produces: "list[str]"
42 | values: "list[str]"
43 | prod_meta: "dict[str, str]"
44 |
45 | @abstractmethod
46 | def execute(self, ctx):
47 | """
48 | Executes module.
49 | Use yield to print a message informing about current execution phase.
50 | `ctx` is `ModuleContext`.
51 | """
52 | pass
53 |
54 | @abstractmethod
55 | def map_io(self, ctx) -> "dict[str, ]":
56 | """
57 | Returns paths for outputs derived from given inputs.
58 | `ctx` is `ModuleContext`.
59 | """
60 | pass
61 |
62 | def __init__(self, params: "dict[str, ]"):
63 | self.no_of_phases = 0
64 | self.current_phase = 0
65 | self.name = ""
66 | self.prod_meta = {}
67 |
68 |
69 | class ModuleContext:
70 | """
71 | A class for object holding mappings for dependencies and values as well as other information needed during modules
72 | execution.
73 | """
74 |
75 | share: str # Absolute path to F4PGA's share directory
76 | bin: str # Absolute path to F4PGA's bin directory
77 | takes: SimpleNamespace # Maps symbolic dependency names to relative paths.
78 | produces: SimpleNamespace # Contains mappings for explicitely specified dependencies.
79 | # Useful mostly for checking for on-demand optional outputs (such as logs) with
80 | # `is_output_explicit` method.
81 | outputs: SimpleNamespace # Contains mappings for all available outputs.
82 | values: SimpleNamespace # Contains all available requested values.
83 | r_env: ResolutionEnv # `ResolutionEnvironmet` object holding mappings for current scope.
84 | module_name: str # Name of the module.
85 |
86 | def is_output_explicit(self, name: str):
87 | """
88 | True if user has explicitely specified output's path.
89 | """
90 | return getattr(self.produces, name) is not None
91 |
92 | def _getreqmaybe(self, obj, deps: "list[str]", deps_cfg: "dict[str, ]"):
93 | """
94 | Add attribute for a dependency or panic if a required dependency has not been given to the module on its input.
95 | """
96 | for name in deps:
97 | name, spec = decompose_depname(name)
98 | value = deps_cfg.get(name)
99 | if value is None and spec == "req":
100 | fatal(-1, f"Dependency `{name}` is required by module `{self.module_name}` but wasn't provided")
101 | setattr(obj, name, self.r_env.resolve(value))
102 |
103 | # `config` should be a dictionary given as modules input.
104 | def __init__(self, module: Module, config: "dict[str, ]", r_env: ResolutionEnv, share: str, bin: str):
105 | self.module_name = module.name
106 | self.takes = SimpleNamespace()
107 | self.produces = SimpleNamespace()
108 | self.values = SimpleNamespace()
109 | self.outputs = SimpleNamespace()
110 | self.r_env = r_env
111 | self.share = share
112 | self.bin = bin
113 |
114 | self._getreqmaybe(self.takes, module.takes, config["takes"])
115 | self._getreqmaybe(self.values, module.values, config["values"])
116 |
117 | produces_resolved = self.r_env.resolve(config["produces"])
118 | for name, value in produces_resolved.items():
119 | setattr(self.produces, name, value)
120 |
121 | outputs = module.map_io(self)
122 | outputs.update(produces_resolved)
123 |
124 | self._getreqmaybe(self.outputs, module.produces, outputs)
125 |
126 | def shallow_copy(self):
127 | cls = type(self)
128 | mycopy = cls.__new__(cls)
129 |
130 | mycopy.module_name = self.module_name
131 | mycopy.takes = self.takes
132 | mycopy.produces = self.produces
133 | mycopy.values = self.values
134 | mycopy.outputs = self.outputs
135 | mycopy.r_env = self.r_env
136 | mycopy.share = self.share
137 | mycopy.bin = self.bin
138 |
139 | return mycopy
140 |
141 |
142 | class ModuleRuntimeException(Exception):
143 | info: str
144 |
145 | def __init__(self, info: str):
146 | self.info = info
147 |
148 | def __str___(self):
149 | return self.info
150 |
151 |
152 | def get_mod_metadata(module: Module):
153 | """
154 | Get descriptions for produced dependencies.
155 | """
156 | meta = {}
157 | has_meta = hasattr(module, "prod_meta")
158 | for prod in module.produces:
159 | prod = prod.replace("?", "").replace("!", "")
160 | if not has_meta:
161 | meta[prod] = ""
162 | continue
163 | prod_meta = module.prod_meta.get(prod)
164 | meta[prod] = prod_meta if prod_meta else ""
165 | return meta
166 |
--------------------------------------------------------------------------------
/f4pga/flows/modules/analysis.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | from pathlib import Path
21 |
22 | from f4pga.flows.tools.vpr import vpr_specific_values, vpr, VprArgs
23 | from f4pga.flows.module import Module, ModuleContext
24 |
25 |
26 | class analysisModule(Module):
27 | def map_io(self, ctx: ModuleContext):
28 | return {
29 | "merged_post_implementation_v": p_analysis_merged_post_implementation_file(ctx),
30 | "post_implementation_v": p_analysis_post_implementation_file(ctx),
31 | }
32 |
33 | def execute(self, ctx: ModuleContext):
34 | build_dir = Path(ctx.takes.eblif).parent
35 |
36 | yield "Analysis with VPR..."
37 | vpr(
38 | "analysis",
39 | VprArgs(
40 | share=ctx.share,
41 | eblif=ctx.takes.eblif,
42 | arch_def=ctx.values.arch_def,
43 | lookahead=ctx.values.rr_graph_lookahead_bin,
44 | rr_graph=ctx.values.rr_graph_real_bin,
45 | place_delay=ctx.values.vpr_place_delay,
46 | device_name=ctx.values.vpr_grid_layout_name,
47 | vpr_options=ctx.values.vpr_options if ctx.values.vpr_options else {},
48 | sdc_file=ctx.takes.sdc,
49 | ),
50 | cwd=build_dir,
51 | )
52 |
53 | if ctx.is_output_explicit("merged_post_implementation_v"):
54 | Path(p_analysis_merged_post_implementation_file(ctx)).rename(ctx.outputs.merged_post_implementation_v)
55 |
56 | if ctx.is_output_explicit("post_implementation_v"):
57 | Path(p_analysis_post_implementation_file(ctx)).rename(ctx.outputs.post_implementation_v)
58 |
59 | yield "Saving log..."
60 | save_vpr_log("analysis.log", build_dir=build_dir)
61 |
62 | def __init__(self, _):
63 | self.name = "analysis"
64 | self.no_of_phases = 2
65 | self.takes = ["eblif", "route", "sdc?"]
66 | self.produces = ["merged_post_implementation_v", "post_implementation_v", "analysis_log"]
67 | self.values = ["device", "vpr_options?"] + vpr_specific_values
68 |
69 |
70 | ModuleClass = analysisModule
71 |
72 |
73 | def p_analysis_merged_post_implementation_file(ctx: ModuleContext):
74 | return str(Path(ctx.takes.eblif).with_suffix("")) + "_merged_post_implementation.v"
75 |
76 |
77 | def p_analysis_post_implementation_file(ctx: ModuleContext):
78 | return str(Path(ctx.takes.eblif).with_suffix("")) + "_post_synthesis.v"
79 |
--------------------------------------------------------------------------------
/f4pga/flows/modules/fasm.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | from pathlib import Path
21 |
22 | from f4pga.flows.common import get_verbosity_level, sub as common_sub
23 | from f4pga.flows.tools.vpr import vpr_specific_values, VprArgs
24 | from f4pga.flows.module import Module, ModuleContext
25 |
26 |
27 | class FasmModule(Module):
28 | def map_io(self, ctx: ModuleContext):
29 | build_dir = str(Path(ctx.takes.eblif).parent)
30 | return {"fasm": f"{(Path(build_dir)/ctx.values.top)!s}.fasm"}
31 |
32 | def execute(self, ctx: ModuleContext):
33 | build_dir = str(Path(ctx.takes.eblif).parent)
34 |
35 | vprargs = VprArgs(
36 | share=ctx.share,
37 | eblif=ctx.takes.eblif,
38 | arch_def=ctx.values.arch_def,
39 | lookahead=ctx.values.rr_graph_lookahead_bin,
40 | rr_graph=ctx.values.rr_graph_real_bin,
41 | place_delay=ctx.values.vpr_place_delay,
42 | device_name=ctx.values.vpr_grid_layout_name,
43 | vpr_options=ctx.values.vpr_options if ctx.values.vpr_options else {},
44 | )
45 |
46 | optional = []
47 | if ctx.values.pnr_corner is not None:
48 | optional += ["--pnr_corner", ctx.values.pnr_corner]
49 | if ctx.takes.sdc:
50 | optional += ["--sdc", ctx.takes.sdc]
51 |
52 | s = [
53 | "genfasm",
54 | vprargs.arch_def,
55 | str(Path(ctx.takes.eblif).resolve()),
56 | "--device",
57 | vprargs.device_name,
58 | "--read_rr_graph",
59 | vprargs.rr_graph,
60 | ] + vprargs.optional
61 |
62 | if get_verbosity_level() >= 2:
63 | yield "Generating FASM...\n " + " ".join(s)
64 | else:
65 | yield "Generating FASM..."
66 |
67 | common_sub(*s, cwd=build_dir)
68 |
69 | default_fasm_output_name = Path(build_dir) / f"{ctx.values.top}.fasm"
70 | if str(default_fasm_output_name) != ctx.outputs.fasm:
71 | default_fasm_output_name.rename(ctx.outputs.fasm)
72 |
73 | if ctx.takes.fasm_extra:
74 | yield "Appending extra FASM..."
75 | with open(ctx.outputs.fasm, "a") as fasm_file, open(ctx.takes.fasm_extra, "r") as fasm_extra_file:
76 | fasm_file.write(f"\n{fasm_extra_file.read()}")
77 | else:
78 | yield "No extra FASM to append"
79 |
80 | def __init__(self, _):
81 | self.name = "fasm"
82 | self.no_of_phases = 2
83 | self.takes = ["eblif", "net", "place", "route", "fasm_extra?", "sdc?"]
84 | self.produces = ["fasm"]
85 | self.values = ["device", "top", "pnr_corner?"] + vpr_specific_values
86 | self.prod_meta = {"fasm": "FPGA assembly file"}
87 |
88 |
89 | ModuleClass = FasmModule
90 |
--------------------------------------------------------------------------------
/f4pga/flows/modules/io_rename.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | """
21 | Rename (ie. change) dependencies and values of a module. This module wraps another,
22 | module whoose name is specified in `params.module` and changes the names of the
23 | dependencies and values it relies on. The parmeters for the wrapped module can be
24 | specified through `params.params`. dict. There are three mapping for the names:
25 | * `params.rename_takes` - mapping for inputs ("takes")
26 | * `params.rename_produces` - mapping for outputs ("products")
27 | * `params.rename_values` - mapping for values
28 | Keys represent the names visible to the wrpped module and values represent the
29 | names visible to the modules outside.
30 | Not specifying a mapping for a given entry will leave it with its original name.
31 |
32 | ---------------
33 |
34 | Accepted module parameters:
35 | * `module` (string, required)
36 | * `params` (dict[string -> any], optional)
37 | * `rename_takes` (dict[string -> string], optional)
38 | * `rename_produces` (dict[string -> string], optional)
39 | * `rename_values` (dict[string -> string], optional)
40 |
41 | """
42 |
43 | from argparse import Namespace
44 | from types import SimpleNamespace
45 |
46 | from f4pga.flows.common import decompose_depname, resolve_modstr, with_qualifier
47 | from f4pga.flows.module import Module, ModuleContext
48 | from f4pga.flows.runner import get_module
49 |
50 |
51 | def p_switch_keys(d: "dict[str, ]", renames: "dict[str, str]") -> "dict[str, ]":
52 | newd = {}
53 | for k, v in d.items():
54 | r = renames.get(k)
55 | if r is not None:
56 | newd[r] = v
57 | else:
58 | newd[k] = v
59 | return newd
60 |
61 |
62 | def p_switchback_attrs(d: Namespace, renames: "dict[str, str]") -> SimpleNamespace:
63 | newn = SimpleNamespace()
64 | for k, v in vars(d).items():
65 | setattr(newn, k, v)
66 | for k, r in renames.items():
67 | if hasattr(newn, r):
68 | v = getattr(newn, r)
69 | delattr(newn, r)
70 | setattr(newn, k, v)
71 | return newn
72 |
73 |
74 | def p_switch_entries(l: "list[str]", renames: "dict[str, str]") -> "list[str]":
75 | newl = []
76 | for e in l:
77 | r = renames.get(e)
78 | if r is not None:
79 | _, q = decompose_depname(e)
80 | newl.append(with_qualifier(r, q))
81 | else:
82 | newl.append(r if r is not None else e)
83 | return newl
84 |
85 |
86 | def p_or_empty_dict(d: "dict | None"):
87 | return d if d is not None else {}
88 |
89 |
90 | class IORenameModule(Module):
91 | module: Module
92 | rename_takes: "dict[str, str]"
93 | rename_produces: "dict[str, str]"
94 | rename_values: "dict[str, str]"
95 |
96 | def map_io(self, ctx: ModuleContext):
97 | newctx = ctx.shallow_copy()
98 | newctx.takes = p_switchback_attrs(ctx.takes, self.rename_takes)
99 | newctx.values = p_switchback_attrs(ctx.values, self.rename_values)
100 | r = self.module.map_io(newctx)
101 | return p_switch_keys(r, self.rename_produces)
102 |
103 | def execute(self, ctx: ModuleContext):
104 | newctx = ctx.shallow_copy()
105 | newctx.takes = p_switchback_attrs(ctx.takes, self.rename_takes)
106 | newctx.values = p_switchback_attrs(ctx.values, self.rename_values)
107 | newctx.outputs = p_switchback_attrs(ctx.produces, self.rename_produces)
108 | print(newctx.takes)
109 | return self.module.execute(newctx)
110 |
111 | def __init__(self, params):
112 | mod_path = resolve_modstr(params["module"])
113 | module_class = get_module(mod_path)
114 | module: Module = module_class(params.get("params"))
115 |
116 | self.rename_takes = p_or_empty_dict(params.get("rename_takes"))
117 | self.rename_produces = p_or_empty_dict(params.get("rename_produces"))
118 | self.rename_values = p_or_empty_dict(params.get("rename_values"))
119 |
120 | self.module = module
121 | self.name = f"{module.name}-io_renamed"
122 | self.no_of_phases = module.no_of_phases
123 | self.takes = p_switch_entries(module.takes, self.rename_takes)
124 | self.produces = p_switch_entries(module.produces, self.rename_produces)
125 | self.values = p_switch_entries(module.values, self.rename_values)
126 | if hasattr(module, "prod_meta"):
127 | self.prod_meta = p_switch_keys(module.prod_meta, self.rename_produces)
128 |
129 |
130 | ModuleClass = IORenameModule
131 |
--------------------------------------------------------------------------------
/f4pga/flows/modules/mkdirs.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | """
21 | This module is used as a helper in a abuild chain to automate creating build directiores.
22 | It's currenty the only parametric module, meaning it can take user-provided input at an early stage in order to
23 | determine its take/produces I/O.
24 | This allows other repesenting configurable directories, such as a build directory as dependencies and by doing so, allow
25 | the dependency algorithm to lazily create the directories if they become necessary.
26 | """
27 |
28 | from pathlib import Path
29 |
30 | from f4pga.flows.module import Module, ModuleContext
31 |
32 |
33 | class MkDirsModule(Module):
34 | deps_to_produce: "dict[str, str]"
35 |
36 | def map_io(self, ctx: ModuleContext):
37 | return ctx.r_env.resolve(self.deps_to_produce)
38 |
39 | def execute(self, ctx: ModuleContext):
40 | outputs = vars(ctx.outputs)
41 | for _, path in outputs.items():
42 | yield f"Creating directory {path}..."
43 | Path(path).mkdir(parents=True, exist_ok=True)
44 |
45 | def __init__(self, params):
46 | self.name = "mkdirs"
47 | self.no_of_phases = len(params) if params else 0
48 | self.takes = []
49 | self.produces = list(params.keys()) if params else []
50 | self.values = []
51 | self.deps_to_produce = params
52 |
53 |
54 | ModuleClass = MkDirsModule
55 |
--------------------------------------------------------------------------------
/f4pga/flows/modules/nextpnr_ice40.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | import pathlib
21 | from f4pga.flows.common import ResolutionEnv
22 | from f4pga.flows.module import ModuleContext
23 | from f4pga.flows.tools.nextpnr import NextPnrBaseModule
24 |
25 | import re
26 | from pathlib import Path
27 |
28 |
29 | class Ice40ChipInfo:
30 | subfamily: str
31 | size: str
32 | package_code: str
33 |
34 | def __init__(self, part_name: str):
35 | m = re.match("ICE40([A-Z]*)([0-9]+[A-Z]?)-([A-Z0-9]*)$", part_name.upper())
36 | assert m is not None
37 |
38 | self.subfamily = m.group(1)
39 | self.size = m.group(2)
40 | self.package_code = m.group(3)
41 |
42 |
43 | class NextPnrModule(NextPnrBaseModule):
44 | def map_io(self, ctx: ModuleContext) -> "dict[str, ]":
45 | return {"ice_asm": str(Path(ctx.takes.json).with_suffix(".ice"))}
46 |
47 | def execute(self, ctx: ModuleContext):
48 | chip_info = Ice40ChipInfo(ctx.values.part_name)
49 |
50 | self.extra_nextpnr_opts = [
51 | f"--{(chip_info.subfamily + chip_info.size).lower()}",
52 | f"--package",
53 | chip_info.package_code.lower(),
54 | f"--asc",
55 | ctx.outputs.ice_asm,
56 | ]
57 |
58 | if ctx.takes.pcf is not None:
59 | self.extra_nextpnr_opts += ["--pcf", ctx.takes.pcf]
60 | else:
61 | self.extra_nextpnr_opts += ["--pcf-allow-unconstrained"]
62 |
63 | return super().execute(ctx)
64 |
65 | def __init__(self, params: "dict[str, ]"):
66 | super().__init__(params, interchange=False)
67 | self.name = "nextpnr-ice40"
68 | self.nextpnr_variant = "ice40"
69 | self.takes += ["pcf?"]
70 | self.values += ["part_name"]
71 | self.produces += ["ice_asm"]
72 |
73 |
74 | ModuleClass = NextPnrModule
75 |
--------------------------------------------------------------------------------
/f4pga/flows/modules/pack.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | from pathlib import Path
21 |
22 | from f4pga.flows.common import noisy_warnings
23 | from f4pga.flows.tools.vpr import vpr_specific_values, vpr, VprArgs
24 | from f4pga.flows.module import Module, ModuleContext
25 |
26 |
27 | DEFAULT_TIMING_RPT = "pre_pack.report_timing.setup.rpt"
28 | DEFAULT_UTIL_RPT = "packing_pin_util.rpt"
29 |
30 |
31 | class PackModule(Module):
32 | def map_io(self, ctx: ModuleContext):
33 | epath = Path(ctx.takes.eblif)
34 | build_dir = epath.parent
35 | return {
36 | "net": str(epath.with_suffix(".net")),
37 | "util_rpt": str(build_dir / DEFAULT_UTIL_RPT),
38 | "timing_rpt": str(build_dir / DEFAULT_TIMING_RPT),
39 | }
40 |
41 | def execute(self, ctx: ModuleContext):
42 | noisy_warnings(ctx.values.device)
43 | build_dir = Path(ctx.outputs.net).parent
44 |
45 | yield "Packing with VPR..."
46 | vpr(
47 | "pack",
48 | VprArgs(
49 | share=ctx.share,
50 | eblif=ctx.takes.eblif,
51 | arch_def=ctx.values.arch_def,
52 | lookahead=ctx.values.rr_graph_lookahead_bin,
53 | rr_graph=ctx.values.rr_graph_real_bin,
54 | place_delay=ctx.values.vpr_place_delay,
55 | device_name=ctx.values.vpr_grid_layout_name,
56 | vpr_options=ctx.values.vpr_options if ctx.values.vpr_options else {},
57 | sdc_file=ctx.takes.sdc,
58 | ),
59 | cwd=build_dir,
60 | )
61 |
62 | og_log = build_dir / "vpr_stdout.log"
63 |
64 | yield "Moving/deleting files..."
65 | if ctx.outputs.pack_log:
66 | og_log.rename(ctx.outputs.pack_log)
67 | else:
68 | og_log.unlink()
69 |
70 | if ctx.outputs.timing_rpt:
71 | (build_dir / DEFAULT_TIMING_RPT).rename(ctx.outputs.timing_rpt)
72 |
73 | if ctx.outputs.util_rpt:
74 | (build_dir / DEFAULT_UTIL_RPT).rename(ctx.outputs.util_rpt)
75 |
76 | def __init__(self, _):
77 | self.name = "pack"
78 | self.no_of_phases = 2
79 | self.takes = ["eblif", "sdc?"]
80 | self.produces = ["net", "util_rpt", "timing_rpt", "pack_log!"]
81 | self.values = ["device"] + vpr_specific_values
82 |
83 |
84 | ModuleClass = PackModule
85 |
--------------------------------------------------------------------------------
/f4pga/flows/modules/place.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | from pathlib import Path
21 | from re import match as re_match
22 |
23 | from f4pga.flows.tools.vpr import vpr_specific_values, vpr, VprArgs, save_vpr_log
24 | from f4pga.flows.module import Module, ModuleContext
25 |
26 |
27 | def p_default_output_name(eblif):
28 | return Path(eblif).with_suffix(".place")
29 |
30 |
31 | class PlaceModule(Module):
32 | def map_io(self, ctx: ModuleContext):
33 | return {"place": str(p_default_output_name(ctx.takes.eblif))}
34 |
35 | def execute(self, ctx: ModuleContext):
36 | build_dir = ctx.takes.build_dir
37 |
38 | vpr_options = ctx.values.vpr_options if ctx.values.vpr_options else {}
39 | if ctx.takes.place_constraints:
40 | vpr_options.update({"fix_clusters": ctx.takes.place_constraints})
41 |
42 | yield "Running VPR..."
43 | vpr(
44 | "place",
45 | VprArgs(
46 | share=ctx.share,
47 | eblif=ctx.takes.eblif,
48 | arch_def=ctx.values.arch_def,
49 | lookahead=ctx.values.rr_graph_lookahead_bin,
50 | rr_graph=ctx.values.rr_graph_real_bin,
51 | place_delay=ctx.values.vpr_place_delay,
52 | device_name=ctx.values.vpr_grid_layout_name,
53 | vpr_options=vpr_options,
54 | sdc_file=ctx.takes.sdc,
55 | ),
56 | cwd=build_dir,
57 | )
58 |
59 | # VPR names output on its own. If user requested another name, the
60 | # output file should be moved.
61 | # TODO: This extends the set of names that would cause collisions.
62 | # As for now (22-07-2021), no collision detection is being done, but
63 | # when the problem gets tackled, we should keep in mind that VPR-based
64 | # modules may produce some temporary files with names that differ from
65 | # the ones in flow configuration.
66 | if ctx.is_output_explicit("place"):
67 | p_default_output_name(ctx.takes.eblif).rename(ctx.outputs.place)
68 |
69 | yield "Saving log..."
70 | save_vpr_log("place.log", build_dir=build_dir)
71 |
72 | def __init__(self, _):
73 | self.name = "place"
74 | self.no_of_phases = 2
75 | self.takes = ["build_dir", "eblif", "sdc?", "place_constraints?", "io_place?"]
76 | self.produces = ["place"]
77 | self.values = ["device", "vpr_options?"] + vpr_specific_values
78 |
79 |
80 | ModuleClass = PlaceModule
81 |
--------------------------------------------------------------------------------
/f4pga/flows/modules/place_constraints.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | from pathlib import Path
21 |
22 | from f4pga.flows.common import sub as common_sub, options_dict_to_list
23 | from f4pga.flows.module import Module, ModuleContext
24 |
25 |
26 | class PlaceConstraintsModule(Module):
27 | def map_io(self, ctx: ModuleContext):
28 | return {"place_constraints": f"{Path(ctx.takes.net).stem!s}.preplace"}
29 |
30 | def execute(self, ctx: ModuleContext):
31 | yield "Saving place constraint data..."
32 | with Path(ctx.outputs.place_constraints).open("wb") as wfptr:
33 | wfptr.write(
34 | common_sub(
35 | *(
36 | [
37 | "python3",
38 | ctx.values.script,
39 | "--net",
40 | ctx.takes.net,
41 | "--arch",
42 | str(Path(ctx.share) / "arch" / ctx.values.device / "arch.timing.xml"),
43 | "--blif",
44 | ctx.takes.eblif,
45 | "--input",
46 | ctx.takes.io_place,
47 | "--db_root",
48 | common_sub("prjxray-config").decode().replace("\n", ""),
49 | "--part",
50 | ctx.values.part_name,
51 | ]
52 | + (options_dict_to_list(ctx.values.extra_opts) if ctx.values.extra_opts else [])
53 | )
54 | )
55 | )
56 |
57 | def __init__(self, _):
58 | self.name = "place_constraints"
59 | self.no_of_phases = 2
60 | self.takes = ["eblif", "net", "io_place"]
61 | self.produces = ["place_constraints"]
62 | self.values = ["device", "part_name", "script", "extra_opts?"]
63 |
64 |
65 | ModuleClass = PlaceConstraintsModule
66 |
--------------------------------------------------------------------------------
/f4pga/flows/modules/route.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | from pathlib import Path
21 |
22 | from f4pga.flows.tools.vpr import vpr_specific_values, vpr, VprArgs, save_vpr_log
23 | from f4pga.flows.module import Module, ModuleContext
24 |
25 |
26 | def p_route_place_file(ctx: ModuleContext):
27 | return Path(ctx.takes.eblif).with_suffix(".route")
28 |
29 |
30 | class RouteModule(Module):
31 | def map_io(self, ctx: ModuleContext):
32 | return {"route": str(p_route_place_file(ctx))}
33 |
34 | def execute(self, ctx: ModuleContext):
35 | build_dir = Path(ctx.takes.eblif).parent
36 |
37 | yield "Routing with VPR..."
38 | vpr(
39 | "route",
40 | VprArgs(
41 | share=ctx.share,
42 | eblif=ctx.takes.eblif,
43 | arch_def=ctx.values.arch_def,
44 | lookahead=ctx.values.rr_graph_lookahead_bin,
45 | rr_graph=ctx.values.rr_graph_real_bin,
46 | place_delay=ctx.values.vpr_place_delay,
47 | device_name=ctx.values.vpr_grid_layout_name,
48 | vpr_options=ctx.values.vpr_options if ctx.values.vpr_options else {},
49 | sdc_file=ctx.takes.sdc,
50 | ),
51 | cwd=build_dir,
52 | )
53 |
54 | if ctx.is_output_explicit("route"):
55 | p_route_place_file(ctx).rename(ctx.outputs.route)
56 |
57 | yield "Saving log..."
58 | save_vpr_log("route.log", build_dir=build_dir)
59 |
60 | def __init__(self, _):
61 | self.name = "route"
62 | self.no_of_phases = 2
63 | self.takes = ["eblif", "place", "sdc?"]
64 | self.produces = ["route"]
65 | self.values = ["device", "vpr_options?"] + vpr_specific_values
66 |
67 |
68 | ModuleClass = RouteModule
69 |
--------------------------------------------------------------------------------
/f4pga/flows/modules/yosys.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | from os import environ
21 | from pathlib import Path
22 |
23 | from f4pga.context import FPGA_FAM
24 | from f4pga.flows.common import decompose_depname, get_verbosity_level, sub as common_sub
25 | from f4pga.flows.module import Module, ModuleContext
26 | from f4pga.wrappers.tcl import get_script_path as get_tcl_wrapper_path
27 |
28 |
29 | class YosysModule(Module):
30 | extra_products: "list[str]"
31 |
32 | def map_io(self, ctx: ModuleContext):
33 | top = Path(ctx.takes.build_dir) / ctx.values.top if ctx.takes.build_dir else Path(ctx.values.top)
34 |
35 | mapping = {
36 | "eblif": f"{top!s}.eblif",
37 | "fasm_extra": f"{top!s}_fasm_extra.fasm",
38 | "json": f"{top!s}.json",
39 | "synth_json": f"{top!s}_io.json",
40 | }
41 |
42 | for extra in self.extra_products:
43 | name, spec = decompose_depname(extra)
44 | if spec == "maybe":
45 | raise ModuleRuntimeException(
46 | f"Yosys synth extra products can't use 'maybe\ "
47 | f"(?) specifier. Product causing this error: `{extra}`."
48 | )
49 | elif spec == "req":
50 | mapping[name] = str(top.parent / f"{ctx.values.device}_{name}.{name}")
51 |
52 | return mapping
53 |
54 | def execute(self, ctx: ModuleContext):
55 | yield f"Synthesizing sources{f': {ctx.takes.sources}...' if get_verbosity_level() >= 2 else f'...'}"
56 |
57 | # Set up environment for TCL weirdness
58 | env = environ.copy()
59 | env.update(
60 | (
61 | {
62 | key: (" ".join(val) if type(val) is list else val)
63 | for key, val in ctx.values.yosys_tcl_env.items()
64 | if val is not None
65 | }
66 | if ctx.values.yosys_tcl_env
67 | else {}
68 | )
69 | )
70 |
71 | # Execute YOSYS command
72 | args_str = "" if ctx.values.read_verilog_args is None else " ".join(ctx.values.read_verilog_args)
73 |
74 | extra_args = ["-l", ctx.outputs.synth_log] if ctx.outputs.synth_log else []
75 | if ctx.values.extra_args is not None:
76 | extra_args.extend(ctx.values.extra_args)
77 |
78 | common_sub(
79 | *(
80 | ["yosys"]
81 | + extra_args
82 | + [
83 | "-p",
84 | (
85 | " ".join([f"read_verilog {args_str} {vfile};" for vfile in ctx.takes.sources])
86 | + f" tcl {str(get_tcl_wrapper_path(pnrtool=self.pnrtool))}"
87 | ),
88 | ]
89 | ),
90 | env=env,
91 | )
92 |
93 | if self.pnrtool == "vpr":
94 | if not Path(ctx.produces.fasm_extra).is_file():
95 | with Path(ctx.produces.fasm_extra).open("w") as wfptr:
96 | wfptr.write("")
97 |
98 | def __init__(self, params):
99 | self.name = "yosys"
100 | self.no_of_phases = 3
101 |
102 | self.pnrtool = "nextpnr" if FPGA_FAM == "ice40" else "vpr"
103 |
104 | self.takes = ["sources", "build_dir?"]
105 | # Extra takes for use with TCL scripts
106 | extra_takes = params.get("takes")
107 | if extra_takes:
108 | self.takes += extra_takes
109 |
110 | self.produces = ["json", "synth_log!"]
111 | if self.pnrtool == "vpr":
112 | self.produces.extend(
113 | [
114 | "eblif",
115 | "fasm_extra",
116 | "synth_json",
117 | ]
118 | )
119 | # Extra products for use with TCL scripts
120 | extra_products = params.get("produces")
121 | if extra_products:
122 | self.produces += extra_products
123 | self.extra_products = extra_products
124 | else:
125 | self.extra_products = []
126 |
127 | self.values = [
128 | "top",
129 | "device",
130 | "tcl_scripts?",
131 | "extra_args?",
132 | "yosys_tcl_env?",
133 | "read_verilog_args?",
134 | ]
135 | self.prod_meta = {
136 | "eblif": "Extended BLIF hierarchical sequential designs file\n" "generated by YOSYS",
137 | "json": "JSON file containing a design generated by YOSYS",
138 | "synth_log": "YOSYS synthesis log",
139 | "fasm_extra": "Extra FASM generated during sythesis stage. Needed in "
140 | "some designs.\nIn case it's not necessary, the file "
141 | "will be empty.",
142 | }
143 | extra_meta = params.get("prod_meta")
144 | if extra_meta:
145 | self.prod_meta.update(extra_meta)
146 |
147 |
148 | ModuleClass = YosysModule
149 |
--------------------------------------------------------------------------------
/f4pga/flows/part_db.yml:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2022 F4PGA Authors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 | # SPDX-License-Identifier: Apache-2.0
16 |
17 | xc7a50t:
18 | - XC7A50TCSG324-1
19 | - XC7A35TCSG324-1
20 | - XC7A35TCPG236-1
21 |
22 | xc7a100t:
23 | - XC7A100TCSG324-1
24 | - XC7A100TFGG484-2
25 |
26 | xc7a200t:
27 | - XC7A200TSBG484-1
28 |
29 | xc7z010t:
30 | - XC7C010CLG400-1
31 |
32 | ql-eos-s3:
33 | - EOS3FF512-PDN64
34 | - EOS3FF512-WRN42
35 | - EOS3FLF512-PDN64
36 | - EOS3FLF512-WRN42
37 | - EOS3CF512-PDN64
38 | - EOS3CF512-WRN42
39 | - EOS3CLF512-PDN64
40 | - EOS3CLF512-WRN42
41 |
42 | ql-k4n8_slow:
43 | - K4N8
44 | - K4N8_SLOW
45 |
46 | ql-k4n8_fast:
47 | - K4N8_FAST
48 |
49 | ice40:
50 | - ICE40LP1K-CB121
51 | - ICE40LP1K-CB81
52 | - ICE40LP4K-CM225
53 | - ICE40LP8K-CM225
54 | - ICE40LP1K-CM121
55 | - ICE40LP4K-CM121
56 | - ICE40LP8K-CM121
57 | - ICE40LP384-CM36
58 | - ICE40LP1K-CM36
59 | - ICE40LP384-CM49
60 | - ICE40LP1K-CM49
61 | - ICE40LP1K-CM81
62 | - ICE40LP4K-CM81
63 | - ICE40LP8K-CM81
64 | - ICE40LP1K-QN84
65 | - ICE40LP384-SG32
66 | - ICE40LP640-SWG16
67 | - ICE40LP1K-SWG16
68 | - ICE40LP384-VQ100
69 | - ICE40LP640-VQ100
70 | - ICE40LP1K-VQ100
71 | - ICE40LP4K-VQ100
72 | - ICE40LP8K-VQ100
73 | - ICE40HX1K-CB132
74 | - ICE40HX4K-CB132
75 | - ICE40HX8K-CB132
76 | - ICE40HX1K-VQ100
77 | - ICE40HX1K-TQ144
78 | - ICE40HX4K-TQ144
79 | - ICE40HX8K-CM225
80 | - ICE40HX8K-CT256
81 | - ICE40UP3K-UWG30
82 | - ICE40UP3K-SG48
83 | - ICE40UP5K-UWG30
84 | - ICE40UP5K-SG48
85 | - ICE40UL640-SWG16
86 | - ICE40UL640-CM36
87 | - ICE40UL1K-CM36
88 | - ICE5LP1K-SWG36
89 | - ICE5LP2K-SWG36
90 | - ICE5LP4K-SWG36
91 | - ICE5LP1K-CM36
92 | - ICE5LP2K-CM36
93 | - ICE5LP4K-CM36
94 | - ICE5LP1K-SG48
95 | - ICE5LP2K-SG48
96 | - ICE5LP4K-SG4
97 |
--------------------------------------------------------------------------------
/f4pga/flows/requirements.txt:
--------------------------------------------------------------------------------
1 | colorama
2 | pyyaml
3 |
--------------------------------------------------------------------------------
/f4pga/flows/runner.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | """
21 | Dynamically import and run F4PGA modules.
22 | """
23 |
24 | from contextlib import contextmanager
25 | import importlib.util as importlib_util
26 | from pathlib import Path
27 |
28 | from colorama import Style
29 |
30 | from f4pga.flows.module import Module, ModuleContext, get_mod_metadata
31 | from f4pga.flows.common import ResolutionEnv, deep, sfprint
32 |
33 |
34 | @contextmanager
35 | def _add_to_sys_path(path: str):
36 | import sys
37 |
38 | old_syspath = sys.path
39 | sys.path = [path] + sys.path
40 | try:
41 | yield
42 | finally:
43 | sys.path = old_syspath
44 |
45 |
46 | def import_module_from_path(path: str):
47 | absolute_path = str(Path(path).resolve())
48 | with _add_to_sys_path(path):
49 | spec = importlib_util.spec_from_file_location(absolute_path, absolute_path)
50 | module = importlib_util.module_from_spec(spec)
51 | spec.loader.exec_module(module)
52 | return module
53 |
54 |
55 | # Once imported a module will be added to that dict to avaid re-importing it
56 | preloaded_modules = {}
57 |
58 |
59 | def get_module(path: str):
60 | global preloaded_modules
61 |
62 | cached = preloaded_modules.get(path)
63 | if cached:
64 | return cached.ModuleClass
65 |
66 | mod = import_module_from_path(path)
67 | preloaded_modules[path] = mod
68 |
69 | # All F4PGA modules should expose a `ModuleClass` type/alias which is a class implementing a Module interface
70 | return mod.ModuleClass
71 |
72 |
73 | class ModRunCtx:
74 | share: str
75 | bin: str
76 | config: "dict[str, ]"
77 |
78 | def __init__(self, share: str, bin: str, config: "dict[str, ]"):
79 | self.share = share
80 | self.bin = bin
81 | self.config = config
82 |
83 | def make_r_env(self):
84 | return ResolutionEnv(self.config["values"])
85 |
86 |
87 | class ModuleFailException(Exception):
88 | module: str
89 | mode: str
90 | e: Exception
91 |
92 | def __init__(self, module: str, mode: str, e: Exception):
93 | self.module = module
94 | self.mode = mode
95 | self.e = e
96 |
97 | def __str__(self) -> str:
98 | return f"""ModuleFailException:
99 | Module `{self.module}` failed MODE: \'{self.mode}\'
100 | Exception `{type(self.e)}`: {self.e}
101 | """
102 |
103 |
104 | def module_io(module: Module):
105 | return {"name": module.name, "takes": module.takes, "produces": module.produces, "meta": get_mod_metadata(module)}
106 |
107 |
108 | _deep_resolve = deep(lambda p: str(Path(p).resolve()), allow_none=True)
109 |
110 |
111 | def module_map(module: Module, ctx: ModRunCtx):
112 | try:
113 | mod_ctx = ModuleContext(module, ctx.config, ctx.make_r_env(), ctx.share, ctx.bin)
114 | except Exception as e:
115 | raise ModuleFailException(module.name, "map", e)
116 |
117 | return _deep_resolve(vars(mod_ctx.outputs))
118 |
119 |
120 | def module_exec(module: Module, ctx: ModRunCtx):
121 | try:
122 | mod_ctx = ModuleContext(module, ctx.config, ctx.make_r_env(), ctx.share, ctx.bin)
123 | except Exception as e:
124 | raise ModuleFailException(module.name, "exec", e)
125 |
126 | sfprint(1, f"Executing module `{Style.BRIGHT + module.name + Style.RESET_ALL}`:")
127 | current_phase = 1
128 | try:
129 | for phase_msg in module.execute(mod_ctx):
130 | sfprint(1, f" {Style.BRIGHT}[{current_phase}/{module.no_of_phases}] {Style.RESET_ALL}: {phase_msg}")
131 | current_phase += 1
132 | except Exception as e:
133 | raise ModuleFailException(module.name, "exec", e)
134 |
135 | sfprint(1, f"Module `{Style.BRIGHT + module.name + Style.RESET_ALL}` has finished its work!")
136 |
--------------------------------------------------------------------------------
/f4pga/flows/stage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | from f4pga.flows.common import decompose_depname, resolve_modstr
21 | from f4pga.flows.module import Module
22 | from f4pga.flows.runner import get_module, module_io
23 |
24 |
25 | class StageIO:
26 | """
27 | Stage dependency input/output.
28 | TODO: Solve the inconsistecy between usage of that and usage of `decompose_depname` with an unprocessed string.
29 | """
30 |
31 | name: str # A symbolic name given to the dependency
32 | spec: str
33 |
34 | def __init__(self, encoded_name: str):
35 | """
36 | Encoded name feauters special characters that imply certain qualifiers.
37 | Any name that ends with '?' is treated as with 'maybe' qualifier.
38 | The '?' Symbol is then dropped from the dependency name.
39 | """
40 |
41 | self.name, self.spec = decompose_depname(encoded_name)
42 |
43 | def __repr__(self) -> str:
44 | return "StageIO { name: '" + self.name + "', spec: " + self.spec + "}"
45 |
46 |
47 | class Stage:
48 | """
49 | Represents a single stage in a flow.
50 | I.e an instance of a module with a local set of values.
51 | """
52 |
53 | module: Module
54 |
55 | # Name of the stage (module's name)
56 | name: str
57 |
58 | # List of symbolic names of dependencies used by the stage
59 | takes: "list[StageIO]"
60 |
61 | # List of symbolic names of dependencies produced by the stage
62 | produces: "list[StageIO]"
63 |
64 | # Stage-specific values
65 | value_overrides: "dict[str, ]"
66 |
67 | # Stage's metadata extracted from module's output.
68 | meta: "dict[str, str]"
69 |
70 | def __init__(self, name: str, stage_def: "dict[str, ]"):
71 | self.name = name
72 |
73 | if stage_def is None:
74 | stage_def = {}
75 |
76 | self.module = get_module(resolve_modstr(stage_def["module"]))(stage_def.get("params"))
77 |
78 | values = stage_def.get("values")
79 | self.value_overrides = values if values is not None else {}
80 |
81 | mod_io = module_io(self.module)
82 | self.takes = [StageIO(input) for input in mod_io["takes"]]
83 | self.produces = [StageIO(input) for input in mod_io["produces"]]
84 | self.meta = mod_io["meta"]
85 |
86 | def __repr__(self) -> str:
87 | return (
88 | "Stage '" + self.name + "' {"
89 | f" value_overrides: {self.value_ovds},"
90 | f" args: {self.args},"
91 | f" takes: {self.takes},"
92 | f" produces: {self.produces} " + "}"
93 | )
94 |
--------------------------------------------------------------------------------
/f4pga/flows/tools/nextpnr.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | from f4pga.flows.common import ResolutionEnv, get_verbosity_level, sub as common_sub
21 | from f4pga.flows.module import Module, ModuleContext
22 |
23 |
24 | class NextPnrBaseModule(Module):
25 | nextpnr_variant: str
26 | extra_nextpnr_opts: "list[str]"
27 | nextpnr_log_name: "str | None"
28 | use_interchange: bool
29 |
30 | def map_io(self, ctx: ModuleContext) -> "dict[str, ]":
31 | return {}
32 |
33 | def execute(self, ctx: ModuleContext):
34 | nextpnr_cmd = f"nextpnr-{self.nextpnr_variant}"
35 |
36 | nextpnr_opts = [
37 | "--top",
38 | ctx.values.top,
39 | "--placer",
40 | ctx.values.placer,
41 | "--router",
42 | ctx.values.router,
43 | ]
44 |
45 | if self.use_interchange:
46 | nextpnr_opts += ["--netlist", ctx.takes.ic_logical_netlist]
47 | else:
48 | nextpnr_opts += ["--json", ctx.takes.json]
49 |
50 | if ctx.values.prepack_script is not None:
51 | nextpnr_opts += ["--pre-pack", ctx.values.prepack_script]
52 | if ctx.values.preplace_script is not None:
53 | nextpnr_opts += ["--pre-place", ctx.values.preplace_script]
54 | if ctx.values.preroute_script is not None:
55 | nextpnr_opts += ["--pre-route", ctx.values.preroute_script]
56 | if ctx.values.postroute_script is not None:
57 | nextpnr_opts += ["--post-poute", ctx.values.postroute_script]
58 | if ctx.values.fail_script is not None:
59 | nextpnr_opts += ["--on-fail", ctx.values.fail_script]
60 |
61 | if ctx.values.thread_count:
62 | nextpnr_opts += ["--threads", ctx.values.thread_count]
63 | if ctx.values.parallel:
64 | nextpnr_opts += ["--parallel-refine"]
65 |
66 | nextpnr_opts += self.extra_nextpnr_opts
67 |
68 | if get_verbosity_level() >= 2:
69 | yield "Place-and-routing with nextpnr...\n " f'{nextpnr_cmd} {" ".join(nextpnr_opts)}'
70 | else:
71 | yield "Place-and-routing with nextpnr..."
72 |
73 | res = common_sub(nextpnr_cmd, *nextpnr_opts)
74 |
75 | yield "Saving log..."
76 | log_path = getattr(ctx.outputs, self.nextpnr_log_name)
77 | if log_path is not None:
78 | with open(log_path, "w") as f:
79 | f.write(res.decode())
80 |
81 | def __init__(self, params: "dict[str, ]", interchange=False):
82 | super().__init__(params)
83 | self.name = "nextpnr"
84 | self.nextpnr_variant = "unknown"
85 | self.extra_nextpnr_opts = []
86 |
87 | self.no_of_phases = 2
88 | self.use_interchange = interchange
89 |
90 | self.takes = ["ic_logical_netlist"] if self.use_interchange else ["json"]
91 |
92 | self.values = [
93 | "top",
94 | "placer",
95 | "router",
96 | "prepack_script?",
97 | "preplace_script?",
98 | "preroute_script?",
99 | "postroute_script?",
100 | "fail_script?",
101 | "thread_count?",
102 | "parallel?",
103 | ]
104 |
105 | self.nextpnr_log_name = f"nextpnr_log"
106 |
107 | self.produces = [f"{self.nextpnr_log_name}!"]
108 |
--------------------------------------------------------------------------------
/f4pga/flows/tools/vpr.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 |
21 | from pathlib import Path
22 | from shutil import move as sh_mv
23 |
24 | from f4pga.flows.common import sub as common_sub, options_dict_to_list
25 |
26 |
27 | class VprArgs:
28 | """
29 | Represents argument list for VPR (Versatile Place and Route).
30 | """
31 |
32 | arch_dir: str
33 | arch_def: str
34 | lookahead: str
35 | rr_graph: str
36 | place_delay: str
37 | device_name: str
38 | eblif: str
39 | optional: list
40 |
41 | def __init__(
42 | self,
43 | share: str,
44 | eblif,
45 | arch_def,
46 | lookahead,
47 | rr_graph,
48 | place_delay,
49 | device_name,
50 | vpr_options={},
51 | sdc_file: "str | None" = None,
52 | ):
53 | self.arch_dir = str(Path(share) / "arch")
54 | self.arch_def = arch_def
55 | self.lookahead = lookahead
56 | self.rr_graph = rr_graph
57 | self.place_delay = place_delay
58 | self.device_name = device_name
59 | self.eblif = str(Path(eblif).resolve())
60 | self.optional = options_dict_to_list(vpr_options)
61 | if sdc_file is not None:
62 | self.optional += ["--sdc_file", sdc_file]
63 |
64 |
65 | def vpr(mode: str, vprargs: VprArgs, cwd=None):
66 | """
67 | Execute `vpr`.
68 | """
69 | return common_sub(
70 | *(
71 | [
72 | "vpr",
73 | vprargs.arch_def,
74 | vprargs.eblif,
75 | "--device",
76 | vprargs.device_name,
77 | "--read_rr_graph",
78 | vprargs.rr_graph,
79 | "--read_router_lookahead",
80 | vprargs.lookahead,
81 | "--read_placement_delay_lookup",
82 | vprargs.place_delay,
83 | ]
84 | + ([f"--{mode}"] if mode in ["pack", "place", "route", "analysis"] else [])
85 | + vprargs.optional
86 | ),
87 | cwd=cwd,
88 | print_stdout_on_fail=True,
89 | )
90 |
91 |
92 | vpr_specific_values = [
93 | "arch_def",
94 | "rr_graph_lookahead_bin",
95 | "rr_graph_real_bin",
96 | "vpr_place_delay",
97 | "vpr_grid_layout_name",
98 | "vpr_options?",
99 | ]
100 |
101 |
102 | def save_vpr_log(filename, build_dir=""):
103 | """
104 | Save VPR logic (moves the default output file into a desired path).
105 | """
106 | sh_mv(str(Path(build_dir) / "vpr_stdout.log"), filename)
107 |
--------------------------------------------------------------------------------
/f4pga/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.black]
2 | line-length = 120
3 |
--------------------------------------------------------------------------------
/f4pga/requirements.txt:
--------------------------------------------------------------------------------
1 | -r ./flows/requirements.txt
2 |
--------------------------------------------------------------------------------
/f4pga/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | from pathlib import Path
21 | from typing import List
22 | from shutil import which
23 | from subprocess import run
24 |
25 | from setuptools import setup as setuptools_setup
26 |
27 |
28 | packagePath = Path(__file__).resolve().parent
29 | requirementsFile = packagePath / "requirements.txt"
30 |
31 |
32 | # Read requirements file and add them to package dependency list
33 | def get_requirements(file: Path) -> List[str]:
34 | requirements = []
35 | with file.open("r") as fh:
36 | for line in fh.read().splitlines():
37 | if line.startswith("#") or line == "":
38 | continue
39 | elif line.startswith("-r"):
40 | # Remove the first word/argument (-r)
41 | filename = " ".join(line.split(" ")[1:])
42 | requirements += get_requirements(file.parent / filename)
43 | elif line.startswith("https"):
44 | # Convert 'URL#NAME' to 'NAME @ URL'
45 | splitItems = line.split("#")
46 | requirements.append("{} @ {}".format(splitItems[1], splitItems[0]))
47 | else:
48 | requirements.append(line)
49 | return requirements
50 |
51 |
52 | semver = "0.0.0"
53 | version = None
54 |
55 | gitcommit = packagePath.parent / ".gitcommit"
56 | if gitcommit.exists():
57 | with gitcommit.open("r") as rptr:
58 | sha = rptr.read().strip()
59 | if sha != "$Format:%h$":
60 | version = f"{semver}+{sha}"
61 |
62 | git = which("git")
63 | if git is not None:
64 | proc = run(["git", "rev-parse", "HEAD"], capture_output=True)
65 | if proc.returncode == 0:
66 | version = f'{semver}+{proc.stdout.decode("utf8")[0:8]}'
67 |
68 | if version is None:
69 | version = semver
70 |
71 |
72 | sf = "symbiflow"
73 | shwrappers = "f4pga.wrappers.sh.__init__"
74 |
75 |
76 | setuptools_setup(
77 | name=packagePath.name,
78 | version=version,
79 | license="Apache-2.0",
80 | author="F4PGA Authors",
81 | description="F4PGA.",
82 | url="https://github.com/chipsalliance/f4pga",
83 | package_dir={"f4pga": "."},
84 | package_data={
85 | "f4pga.flows": [
86 | "*.yml",
87 | ],
88 | "f4pga.wrappers.sh": [
89 | "xc7/*.f4pga.sh",
90 | "quicklogic/*.f4pga.sh",
91 | ],
92 | "f4pga.wrappers.tcl": [
93 | "*.f4pga.tcl",
94 | ],
95 | },
96 | classifiers=[],
97 | python_requires=">=3.6",
98 | install_requires=list(set(get_requirements(requirementsFile))),
99 | entry_points={
100 | "console_scripts": [
101 | "f4pga = f4pga.flows:main",
102 | # QuickLogic only
103 | f"ql_{sf} = {shwrappers}:ql",
104 | ]
105 | + [
106 | f"{sf}_{script} = {shwrappers}:{script}"
107 | for script in [
108 | "pack",
109 | "place",
110 | "route",
111 | "synth",
112 | "write_fasm",
113 | # Xilinx only
114 | "write_bitstream",
115 | # QuickLogic only
116 | "analysis",
117 | "fasm2bels",
118 | "generate_bitstream",
119 | "repack",
120 | "write_binary",
121 | "write_bitheader",
122 | "write_jlink",
123 | "write_openocd",
124 | ]
125 | ]
126 | },
127 | )
128 |
--------------------------------------------------------------------------------
/f4pga/utils/eblif.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2019-2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | # Top level keywords defining the begin of a cell definition.
21 | top_level = [
22 | "model",
23 | "inputs",
24 | "outputs",
25 | "names",
26 | "latch",
27 | "subckt",
28 | ]
29 |
30 | # Keywords defining cell attributes / parameters. Those can be specified for
31 | # each cell multiple times. Parameter names and values are stored in a dict
32 | # under the parsed blif data.
33 | #
34 | # For example: the construct ".param MODE SYNC" will add to the dict under
35 | # the key "param" entry "MODE":"SYNC".
36 | #
37 | sub_level = [
38 | "attr",
39 | "param",
40 | ]
41 |
42 |
43 | def parse_blif(f):
44 | current = None
45 |
46 | data = {}
47 |
48 | def add(d):
49 | if d["type"] not in data:
50 | data[d["type"]] = []
51 | data[d["type"]].append(d)
52 |
53 | current = None
54 | for oline in f:
55 | line = oline
56 | if "#" in line:
57 | line = line[: line.find("#")]
58 | line = line.strip()
59 | if not line:
60 | continue
61 |
62 | if line.startswith("."):
63 | args = line.split(" ", maxsplit=1)
64 | if len(args) < 2:
65 | args.append("")
66 |
67 | ctype = args.pop(0)
68 | assert ctype.startswith("."), ctype
69 | ctype = ctype[1:]
70 |
71 | if ctype in top_level:
72 | if current:
73 | add(current)
74 | current = {
75 | "type": ctype,
76 | "args": args[-1].split(),
77 | "data": [],
78 | }
79 | elif ctype in sub_level:
80 | if ctype not in current:
81 | current[ctype] = {}
82 | key, value = args[-1].split(maxsplit=1)
83 | current[ctype][key] = value
84 | else:
85 | current[ctype] = args[-1].split()
86 | continue
87 | current["data"].append(line.strip().split())
88 |
89 | if current:
90 | add(current)
91 |
92 | assert len(data["inputs"]) == 1
93 | data["inputs"] = data["inputs"][0]
94 | assert len(data["outputs"]) == 1
95 | data["outputs"] = data["outputs"][0]
96 | return data
97 |
--------------------------------------------------------------------------------
/f4pga/utils/pcf.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2019-2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | """
21 | Supported PCF commands:
22 | * set_io - constrain a given to a given physical in eFPGA pinout.
23 | * set_clk - constrain a given global clock to a given
24 | Every tile where is present will be constrained to use a given global clock.
25 | """
26 |
27 | from collections import namedtuple
28 | import re
29 |
30 | PcfIoConstraint = namedtuple("PcfIoConstraint", "net pad line_str line_num")
31 | PcfClkConstraint = namedtuple("PcfClkConstraint", "pin net")
32 |
33 |
34 | def parse_simple_pcf(f):
35 | """Parse a simple PCF file object and yield PcfIoConstraint objects."""
36 | for line_number, line in enumerate(f):
37 | line_number += 1
38 |
39 | # Remove comments.
40 | args = re.sub(r"#.*", "", line.strip()).split()
41 |
42 | if not args:
43 | continue
44 |
45 | # Ignore arguments.
46 | args = [arg for arg in args if arg[0] != "-"]
47 | assert len(args) == 3, args
48 |
49 | if args[0] == "set_io":
50 | yield PcfIoConstraint(
51 | net=args[1],
52 | pad=args[2],
53 | line_str=line.strip(),
54 | line_num=line_number,
55 | )
56 |
57 | if args[0] == "set_clk":
58 | yield PcfClkConstraint(
59 | pin=args[1],
60 | net=args[2],
61 | )
62 |
--------------------------------------------------------------------------------
/f4pga/utils/quicklogic/pp3/utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 | import re
20 |
21 | # =============================================================================
22 |
23 | # A regex used for fixing pin names
24 | RE_PIN_NAME = re.compile(r"^([A-Za-z0-9_]+)(?:\[([0-9]+)\])?$")
25 |
26 | # =============================================================================
27 |
28 |
29 | def get_pin_name(name):
30 | """
31 | Returns the pin name and its index in bus. If a pin is not a member of
32 | a bus then the index is None
33 |
34 | >>> get_pin_name("WIRE")
35 | ('WIRE', None)
36 | >>> get_pin_name("DATA[12]")
37 | ('DATA', 12)
38 | """
39 |
40 | match = re.match(r"(?P.*)\[(?P[0-9]+)\]$", name)
41 | if match:
42 | return match.group("name"), int(match.group("idx"))
43 | else:
44 | return name, None
45 |
46 |
47 | def fixup_pin_name(name):
48 | """
49 | Renames a pin to make its name suitable for VPR.
50 |
51 | >>> fixup_pin_name("A_WIRE")
52 | 'A_WIRE'
53 | >>> fixup_pin_name("ADDRESS[17]")
54 | 'ADDRESS_17'
55 | >>> fixup_pin_name("DATA[11]_X")
56 | Traceback (most recent call last):
57 | ...
58 | AssertionError: DATA[11]_X
59 | """
60 |
61 | match = RE_PIN_NAME.match(name)
62 | assert match is not None, name
63 |
64 | groups = match.groups()
65 | if groups[1] is None:
66 | return groups[0]
67 | else:
68 | return "{}_{}".format(*groups)
69 |
70 |
71 | # =============================================================================
72 |
73 |
74 | def yield_muxes(switchbox):
75 | """
76 | Yields all muxes of a switchbox. Returns tuples with:
77 | (stage, switch, mux)
78 | """
79 |
80 | for stage in switchbox.stages.values():
81 | for switch in stage.switches.values():
82 | for mux in switch.muxes.values():
83 | yield stage, switch, mux
84 |
85 |
86 | # =============================================================================
87 |
88 |
89 | def get_quadrant_for_loc(loc, quadrants):
90 | """
91 | Assigns a quadrant to the given location. Returns None if no one matches.
92 | """
93 |
94 | for quadrant in quadrants.values():
95 | if loc.x >= quadrant.x0 and loc.x <= quadrant.x1:
96 | if loc.y >= quadrant.y0 and loc.y <= quadrant.y1:
97 | return quadrant
98 |
99 | return None
100 |
101 |
102 | def get_loc_of_cell(cell_name, tile_grid):
103 | """
104 | Returns loc of a cell with the given name in the tilegrid.
105 | """
106 |
107 | # Look for a tile that has the cell
108 | for loc, tile in tile_grid.items():
109 | if tile is None:
110 | continue
111 |
112 | cell_names = [c.name for c in tile.cells]
113 | if cell_name in cell_names:
114 | return loc
115 |
116 | # Not found
117 | return None
118 |
119 |
120 | def find_cell_in_tile(cell_name, tile):
121 | """
122 | Finds a cell instance with the given name inside the given tile.
123 | Returns the Cell object if found and None otherwise.
124 | """
125 | for cell in tile.cells:
126 | if cell.name == cell_name:
127 | return cell
128 |
129 | return None
130 |
131 |
132 | # =============================================================================
133 |
134 |
135 | def add_named_item(item_dict, item, item_name):
136 | """
137 | Adds a named item to the given dict if not already there. If it is there
138 | then returns the one from the dict.
139 | """
140 |
141 | if item_name not in item_dict:
142 | item_dict[item_name] = item
143 |
144 | return item_dict[item_name]
145 |
146 |
147 | # =============================================================================
148 |
149 |
150 | def natural_keys(text):
151 | """
152 | alist.sort(key=natural_keys) sorts in human order
153 | http://nedbatchelder.com/blog/200712/human_sorting.html
154 | (See Toothy's implementation in the comments)
155 |
156 | https://stackoverflow.com/questions/5967500/how-to-correctly-sort-a-string-with-a-number-inside
157 | """
158 |
159 | def atoi(text):
160 | return int(text) if text.isdigit() else text
161 |
162 | return [atoi(c) for c in re.split(r"(\d+)", text)]
163 |
--------------------------------------------------------------------------------
/f4pga/utils/quicklogic/repacker/block_path.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 | """
20 | Block hierarchy path utilities.
21 | """
22 | import re
23 |
24 | # =============================================================================
25 |
26 | # A regular expression for parsing path nodes
27 | PATH_NODE_RE = re.compile(r"^(?P[^\s\[\]\.]+)(\[(?P[0-9]+)\])?" r"(\[(?P[^\s\[\]\.]+)\])?$")
28 |
29 | # =============================================================================
30 |
31 |
32 | class PathNode:
33 | """
34 | A class for representing a single path node.
35 |
36 | A string representation of a path node can have the following formats:
37 | - "[][]"
38 | - "[]"
39 | - "[]"
40 | - ""
41 |
42 | Differentiation between index and mode is based on the assumption that an
43 | index may only contain numbers while a mode any other character as well.
44 |
45 | FIXME: This may lead to ambiguity if a mode is named using digits only.
46 | """
47 |
48 | def __init__(self, name, index=None, mode=None):
49 | """
50 | Generic constructor
51 | """
52 |
53 | assert isinstance(name, str) or name is None, name
54 | assert isinstance(index, int) or index is None, index
55 | assert isinstance(mode, str) or mode is None, mode
56 |
57 | self.name = name
58 | self.index = index
59 | self.mode = mode
60 |
61 | @staticmethod
62 | def from_string(string):
63 | """
64 | Converts a string representation to a PathNode object
65 | """
66 |
67 | # Match the regex
68 | match = PATH_NODE_RE.fullmatch(string)
69 | assert match is not None, string
70 |
71 | name = match.group("name")
72 | index = match.group("index")
73 | mode = match.group("mode")
74 |
75 | if index is not None:
76 | index = int(index)
77 |
78 | return PathNode(name, index, mode)
79 |
80 | def to_string(self):
81 | """
82 | Converts the node into a string
83 | """
84 | string = self.name
85 |
86 | if self.index is not None:
87 | string += "[{}]".format(self.index)
88 |
89 | if self.mode is not None:
90 | string += "[{}]".format(self.mode)
91 |
92 | return string
93 |
94 | def __str__(self):
95 | return self.to_string()
96 |
97 | def __repr__(self):
98 | return self.to_string()
99 |
--------------------------------------------------------------------------------
/f4pga/utils/quicklogic/repacker/netlist_cleaning.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 | """
20 | Utilities for cleaning circuit netlists
21 | """
22 |
23 | import logging
24 |
25 | # =============================================================================
26 |
27 |
28 | def absorb_buffer_luts(netlist, outputs=False):
29 | """
30 | Performs downstream absorbtion of buffer LUTs. All buffer LUTs are absorbed
31 | except for those that drive top-level output ports not to change output
32 | net names.
33 |
34 | Returns a net map that defines the final net remapping after some nets
35 | got absorbed downstream.
36 | """
37 |
38 | INP_PORT = "lut_in[0]"
39 | OUT_PORT = "lut_out"
40 |
41 | def is_buffer_lut(cell):
42 | """
43 | Returns True when a cell is a buffer LUT to be absorbed.
44 | """
45 |
46 | # A pass-through LUT
47 | if cell.type == "$lut" and cell.init == [0, 1]:
48 | # Get input and output nets
49 | assert INP_PORT in cell.ports, cell
50 | net_inp = cell.ports[INP_PORT]
51 |
52 | assert OUT_PORT in cell.ports, cell
53 | net_out = cell.ports[OUT_PORT]
54 |
55 | # Must not be driving any top-level outputs unless explicitly
56 | # allowed
57 | if (net_inp in netlist.inputs or net_out in netlist.outputs) and not outputs:
58 | return False
59 |
60 | return True
61 |
62 | return False
63 |
64 | # Identify LUT buffers
65 | buffers = {key: cell for key, cell in netlist.cells.items() if is_buffer_lut(cell)}
66 |
67 | # Merge them downstream
68 | net_map = {}
69 | for cell in buffers.values():
70 | # Get input and output nets
71 | assert INP_PORT in cell.ports, cell
72 | net_inp = cell.ports[INP_PORT]
73 |
74 | assert OUT_PORT in cell.ports, cell
75 | net_out = cell.ports[OUT_PORT]
76 |
77 | # Cannot be a buffer connecting an input to an output directly
78 | if net_out in netlist.outputs and net_inp in netlist.inputs:
79 | continue
80 |
81 | # This cell drives a top-level output directly. Change input nets and
82 | # leave the output one
83 | if net_out in netlist.outputs:
84 | # Replace the output net in all cells with the input one
85 | for c in netlist.cells.values():
86 | for port, net in c.ports.items():
87 | if net == net_inp:
88 | c.ports[port] = net_out
89 |
90 | # Update net map
91 | for net in net_map:
92 | if net_map[net] == net_inp:
93 | net_map[net] = net_out
94 |
95 | net_map[net_inp] = net_out
96 |
97 | # A regular buffer
98 | else:
99 | # Replace the output net in all cells with the input one
100 | for c in netlist.cells.values():
101 | for port, net in c.ports.items():
102 | if net == net_out:
103 | c.ports[port] = net_inp
104 |
105 | # Update net map
106 | for net in net_map:
107 | if net_map[net] == net_out:
108 | net_map[net] = net_inp
109 |
110 | net_map[net_out] = net_inp
111 |
112 | # Remove the cell
113 | del netlist.cells[cell.name]
114 |
115 | logging.debug(" Absorbed {} buffer LUTs".format(len(buffers)))
116 | return net_map
117 |
118 |
119 | def sweep_dangling_cells(netlist):
120 | # TODO:
121 | pass
122 |
--------------------------------------------------------------------------------
/f4pga/utils/quicklogic/repacker/tests/eblif_roundtrip/netlist.golden.eblif:
--------------------------------------------------------------------------------
1 | .model top
2 | .inputs clk
3 | .outputs led[0] led[1] led[2] led[3]
4 | .names $false
5 | .names $true
6 | 1
7 | .names $undef
8 | .names cnt[1] cnt[0] $auto$alumacc.cc:485:replace_alu$5.Y[1]
9 | 01 1
10 | 10 1
11 | .names cnt[2] cnt[1] cnt[0] $auto$alumacc.cc:485:replace_alu$5.Y[2]
12 | 011 1
13 | 100 1
14 | 101 1
15 | 110 1
16 | .names cnt[3] cnt[2] cnt[1] cnt[0] $auto$alumacc.cc:485:replace_alu$5.Y[3]
17 | 0111 1
18 | 1000 1
19 | 1001 1
20 | 1010 1
21 | 1011 1
22 | 1100 1
23 | 1101 1
24 | 1110 1
25 | .names led[0] $abc$215$new_n22_ $auto$alumacc.cc:485:replace_alu$5.Y[4]
26 | 01 1
27 | 10 1
28 | .names cnt[2] cnt[3] cnt[1] cnt[0] $abc$215$new_n22_
29 | 1111 1
30 | .names led[1] led[0] $abc$215$new_n22_ $auto$alumacc.cc:485:replace_alu$5.Y[5]
31 | 011 1
32 | 100 1
33 | 101 1
34 | 110 1
35 | .names led[2] $abc$215$new_n25_ $abc$215$new_n22_ $auto$alumacc.cc:485:replace_alu$5.Y[6]
36 | 011 1
37 | 100 1
38 | 101 1
39 | 110 1
40 | .names led[0] led[1] $abc$215$new_n25_
41 | 11 1
42 | .names led[3] led[2] $abc$215$new_n25_ $abc$215$new_n22_ $auto$alumacc.cc:485:replace_alu$5.Y[7]
43 | 0111 1
44 | 1000 1
45 | 1001 1
46 | 1010 1
47 | 1011 1
48 | 1100 1
49 | 1101 1
50 | 1110 1
51 | .names cnt[0] $auto$alumacc.cc:485:replace_alu$5.X[0]
52 | 0 1
53 | .latch $auto$alumacc.cc:485:replace_alu$5.X[0] cnt[0] re clk 0
54 | .latch $auto$alumacc.cc:485:replace_alu$5.Y[1] cnt[1] re clk 0
55 | .latch $auto$alumacc.cc:485:replace_alu$5.Y[2] cnt[2] re clk 0
56 | .latch $auto$alumacc.cc:485:replace_alu$5.Y[3] cnt[3] re clk 0
57 | .latch $auto$alumacc.cc:485:replace_alu$5.Y[4] led[0] re clk 0
58 | .latch $auto$alumacc.cc:485:replace_alu$5.Y[5] led[1] re clk 0
59 | .latch $auto$alumacc.cc:485:replace_alu$5.Y[6] led[2] re clk 0
60 | .latch $auto$alumacc.cc:485:replace_alu$5.Y[7] led[3] re clk 0
61 | .names cnt[1] $auto$alumacc.cc:485:replace_alu$5.X[1]
62 | 1 1
63 | .names cnt[2] $auto$alumacc.cc:485:replace_alu$5.X[2]
64 | 1 1
65 | .names cnt[3] $auto$alumacc.cc:485:replace_alu$5.X[3]
66 | 1 1
67 | .names led[0] $auto$alumacc.cc:485:replace_alu$5.X[4]
68 | 1 1
69 | .names led[1] $auto$alumacc.cc:485:replace_alu$5.X[5]
70 | 1 1
71 | .names led[2] $auto$alumacc.cc:485:replace_alu$5.X[6]
72 | 1 1
73 | .names led[3] $auto$alumacc.cc:485:replace_alu$5.X[7]
74 | 1 1
75 | .names $auto$alumacc.cc:485:replace_alu$5.X[0] $auto$alumacc.cc:485:replace_alu$5.Y[0]
76 | 1 1
77 | .names led[0] cnt[4]
78 | 1 1
79 | .names led[1] cnt[5]
80 | 1 1
81 | .names led[2] cnt[6]
82 | 1 1
83 | .names led[3] cnt[7]
84 | 1 1
85 | .end
86 |
--------------------------------------------------------------------------------
/f4pga/utils/quicklogic/repacker/tests/eblif_roundtrip/test_eblif_roundtrip.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 | import os
20 | import sys
21 | import tempfile
22 |
23 | sys.path.append(os.path.join(os.path.dirname(__file__), "..", ".."))
24 | from eblif_netlist import Eblif # noqa: E402
25 |
26 | # =============================================================================
27 |
28 |
29 | def test_netlist_roundtrip():
30 | basedir = os.path.dirname(__file__)
31 | golden_file = os.path.join(basedir, "netlist.golden.eblif")
32 |
33 | # Load and parse the EBLIF file
34 | eblif = Eblif.from_file(golden_file)
35 |
36 | with tempfile.TemporaryDirectory() as tempdir:
37 | # Write the EBLIF back
38 | output_file = os.path.join(tempdir, "netlist.output.eblif")
39 | eblif.to_file(output_file)
40 |
41 | # Compare the two files
42 | with open(golden_file, "r") as fp:
43 | golden_data = fp.read().rstrip()
44 | with open(output_file, "r") as fp:
45 | output_data = fp.read().rstrip()
46 |
47 | assert golden_data == output_data
48 |
--------------------------------------------------------------------------------
/f4pga/utils/quicklogic/repacker/tests/identity.xsl:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/f4pga/utils/quicklogic/repacker/tests/lut_padding/lut1.eblif:
--------------------------------------------------------------------------------
1 | .model top
2 | .inputs a
3 | .outputs o
4 | .names a o
5 | 0 1
6 | .end
7 |
--------------------------------------------------------------------------------
/f4pga/utils/quicklogic/repacker/tests/lut_padding/lut1_0.golden.eblif:
--------------------------------------------------------------------------------
1 | .model top
2 | .inputs a
3 | .outputs o
4 | .subckt frac_lut4_arith in[0]=a lut4_out=o
5 | .param LUT 1010101010101010
6 | .param MODE 0
7 | .end
8 |
--------------------------------------------------------------------------------
/f4pga/utils/quicklogic/repacker/tests/lut_padding/lut1_0.net:
--------------------------------------------------------------------------------
1 |
2 |
3 | a
4 | out:o
5 |
6 |
7 |
8 | open open open open open open open open open open open open open open a open open open open open open open open open
9 | open
10 | open
11 | open
12 | open
13 | open
14 | open
15 |
16 |
17 | open open open open open open open fle[7].out[0]->clbouts2
18 | open
19 | open
20 | open
21 |
22 |
23 | open open open open
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | clb.I[14]->crossbar_fle[7].in[0] open open open
35 | open
36 | open
37 | open
38 | open
39 | open
40 |
41 |
42 | ble4[0].out[0]->direct2
43 | open
44 | open
45 | open
46 |
47 |
48 | open
49 |
50 |
51 |
52 | fle.in[0]->direct1 open open open
53 | open
54 | open
55 |
56 |
57 | lut4[0].out[0]->mux1
58 |
59 |
60 | open
61 |
62 |
63 |
64 | ble4.in[0]->direct1 open open open
65 |
66 |
67 | lut[0].out[0]->direct:lut4
68 |
69 |
70 |
71 |
72 |
73 |
74 | lut4.in[0]->direct:lut4 open open open
75 | 0 open open open
76 |
77 |
78 | o
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | o
90 | open
91 | open
92 |
93 |
94 | open
95 | open
96 |
97 |
98 | open open open open
99 |
100 |
101 |
102 | io.f2a_i[0]->io_output-f2a_i
103 | open
104 |
105 |
106 |
107 | open
108 |
109 |
110 |
111 |
112 |
113 |
114 | io_output.f2a_i[0]->mux1
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | open
124 | open
125 | open
126 |
127 |
128 | io_input[0].a2f_o[0]->io-a2f_o
129 | open
130 |
131 |
132 | open open open open
133 |
134 |
135 |
136 | open
137 |
138 |
139 | inpad[0].inpad[0]->mux2
140 |
141 |
142 | open
143 |
144 |
145 |
146 |
147 |
148 |
149 | a
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
--------------------------------------------------------------------------------
/f4pga/utils/quicklogic/repacker/tests/lut_padding/lut1_1.golden.eblif:
--------------------------------------------------------------------------------
1 | .model top
2 | .inputs a
3 | .outputs o
4 | .subckt frac_lut4_arith in[1]=a lut4_out=o
5 | .param LUT 1100110011001100
6 | .param MODE 0
7 | .end
8 |
--------------------------------------------------------------------------------
/f4pga/utils/quicklogic/repacker/tests/lut_padding/lut1_1.net:
--------------------------------------------------------------------------------
1 |
2 |
3 | a
4 | out:o
5 |
6 |
7 |
8 | open open open open open open open open open open open open open open a open open open open open open open open open
9 | open
10 | open
11 | open
12 | open
13 | open
14 | open
15 |
16 |
17 | open open open open open open open fle[7].out[0]->clbouts2
18 | open
19 | open
20 | open
21 |
22 |
23 | open open open open
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | open clb.I[14]->crossbar_fle[7].in[1] open open
35 | open
36 | open
37 | open
38 | open
39 | open
40 |
41 |
42 | ble4[0].out[0]->direct2
43 | open
44 | open
45 | open
46 |
47 |
48 | open
49 |
50 |
51 |
52 | open fle.in[1]->direct1 open open
53 | open
54 | open
55 |
56 |
57 | lut4[0].out[0]->mux1
58 |
59 |
60 | open
61 |
62 |
63 |
64 | open ble4.in[1]->direct1 open open
65 |
66 |
67 | lut[0].out[0]->direct:lut4
68 |
69 |
70 |
71 |
72 |
73 |
74 | open lut4.in[1]->direct:lut4 open open
75 | open 0 open open
76 |
77 |
78 | o
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | o
90 | open
91 | open
92 |
93 |
94 | open
95 | open
96 |
97 |
98 | open open open open
99 |
100 |
101 |
102 | io.f2a_i[0]->io_output-f2a_i
103 | open
104 |
105 |
106 |
107 | open
108 |
109 |
110 |
111 |
112 |
113 |
114 | io_output.f2a_i[0]->mux1
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | open
124 | open
125 | open
126 |
127 |
128 | io_input[0].a2f_o[0]->io-a2f_o
129 | open
130 |
131 |
132 | open open open open
133 |
134 |
135 |
136 | open
137 |
138 |
139 | inpad[0].inpad[0]->mux2
140 |
141 |
142 | open
143 |
144 |
145 |
146 |
147 |
148 |
149 | a
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
--------------------------------------------------------------------------------
/f4pga/utils/quicklogic/repacker/tests/lut_padding/lut1_2.golden.eblif:
--------------------------------------------------------------------------------
1 | .model top
2 | .inputs a
3 | .outputs o
4 | .subckt frac_lut4_arith in[2]=a lut4_out=o
5 | .param LUT 1111000011110000
6 | .param MODE 0
7 | .end
--------------------------------------------------------------------------------
/f4pga/utils/quicklogic/repacker/tests/lut_padding/lut1_2.net:
--------------------------------------------------------------------------------
1 |
2 |
3 | a
4 | out:o
5 |
6 |
7 |
8 | open open open open open open open open open open open open open open a open open open open open open open open open
9 | open
10 | open
11 | open
12 | open
13 | open
14 | open
15 |
16 |
17 | open open open open open open open fle[7].out[0]->clbouts2
18 | open
19 | open
20 | open
21 |
22 |
23 | open open open open
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | open open clb.I[14]->crossbar_fle[7].in[2] open
35 | open
36 | open
37 | open
38 | open
39 | open
40 |
41 |
42 | ble4[0].out[0]->direct2
43 | open
44 | open
45 | open
46 |
47 |
48 | open
49 |
50 |
51 |
52 | open open fle.in[2]->direct1 open
53 | open
54 | open
55 |
56 |
57 | lut4[0].out[0]->mux1
58 |
59 |
60 | open
61 |
62 |
63 |
64 | open open ble4.in[2]->direct1 open
65 |
66 |
67 | lut[0].out[0]->direct:lut4
68 |
69 |
70 |
71 |
72 |
73 |
74 | open open lut4.in[2]->direct:lut4 open
75 | open open 0 open
76 |
77 |
78 | o
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | o
90 | open
91 | open
92 |
93 |
94 | open
95 | open
96 |
97 |
98 | open open open open
99 |
100 |
101 |
102 | io.f2a_i[0]->io_output-f2a_i
103 | open
104 |
105 |
106 |
107 | open
108 |
109 |
110 |
111 |
112 |
113 |
114 | io_output.f2a_i[0]->mux1
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | open
124 | open
125 | open
126 |
127 |
128 | io_input[0].a2f_o[0]->io-a2f_o
129 | open
130 |
131 |
132 | open open open open
133 |
134 |
135 |
136 | open
137 |
138 |
139 | inpad[0].inpad[0]->mux2
140 |
141 |
142 | open
143 |
144 |
145 |
146 |
147 |
148 |
149 | a
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
--------------------------------------------------------------------------------
/f4pga/utils/quicklogic/repacker/tests/lut_padding/lut1_3.golden.eblif:
--------------------------------------------------------------------------------
1 | .model top
2 | .inputs a
3 | .outputs o
4 | .subckt frac_lut4_arith in[3]=a lut4_out=o
5 | .param LUT 1111111100000000
6 | .param MODE 0
7 | .end
8 |
--------------------------------------------------------------------------------
/f4pga/utils/quicklogic/repacker/tests/lut_padding/lut1_3.net:
--------------------------------------------------------------------------------
1 |
2 |
3 | a
4 | out:o
5 |
6 |
7 |
8 | open open open open open open open open open open open open open open a open open open open open open open open open
9 | open
10 | open
11 | open
12 | open
13 | open
14 | open
15 |
16 |
17 | open open open open open open open fle[7].out[0]->clbouts2
18 | open
19 | open
20 | open
21 |
22 |
23 | open open open open
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | open open open clb.I[14]->crossbar_fle[7].in[3]
35 | open
36 | open
37 | open
38 | open
39 | open
40 |
41 |
42 | ble4[0].out[0]->direct2
43 | open
44 | open
45 | open
46 |
47 |
48 | open
49 |
50 |
51 |
52 | open open open fle.in[3]->direct1
53 | open
54 | open
55 |
56 |
57 | lut4[0].out[0]->mux1
58 |
59 |
60 | open
61 |
62 |
63 |
64 | open open open ble4.in[3]->direct1
65 |
66 |
67 | lut[0].out[0]->direct:lut4
68 |
69 |
70 |
71 |
72 |
73 |
74 | open open open lut4.in[3]->direct:lut4
75 | open open open 0
76 |
77 |
78 | o
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | o
90 | open
91 | open
92 |
93 |
94 | open
95 | open
96 |
97 |
98 | open open open open
99 |
100 |
101 |
102 | io.f2a_i[0]->io_output-f2a_i
103 | open
104 |
105 |
106 |
107 | open
108 |
109 |
110 |
111 |
112 |
113 |
114 | io_output.f2a_i[0]->mux1
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | open
124 | open
125 | open
126 |
127 |
128 | io_input[0].a2f_o[0]->io-a2f_o
129 | open
130 |
131 |
132 | open open open open
133 |
134 |
135 |
136 | open
137 |
138 |
139 | inpad[0].inpad[0]->mux2
140 |
141 |
142 | open
143 |
144 |
145 |
146 |
147 |
148 |
149 | a
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
--------------------------------------------------------------------------------
/f4pga/utils/quicklogic/repacker/tests/lut_padding/test_lut_padding.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 | import sys
20 | import os
21 | import tempfile
22 |
23 | import pytest
24 |
25 | sys.path.append(os.path.join(os.path.dirname(__file__), "..", ".."))
26 | from repack import main as repack_main # noqa: E402
27 |
28 | # =============================================================================
29 |
30 |
31 | @pytest.mark.parametrize(
32 | "lut_width, lut_inputs",
33 | [
34 | ("1", "0"),
35 | ("1", "1"),
36 | ("1", "2"),
37 | ("1", "3"),
38 | ],
39 | )
40 | def test_lut_padding(monkeypatch, lut_width, lut_inputs):
41 | """
42 | Tests repacking of a single lut with some inputs unconnected. Verifies
43 | results against golden references.
44 | """
45 |
46 | basedir = os.path.dirname(__file__)
47 |
48 | qlfpga_plugins = os.path.join(
49 | basedir, "..", "..", "..", "..", "..", "..", "build", "quicklogic", "third_party", "qlfpga-symbiflow-plugins"
50 | )
51 |
52 | vpr_arch = os.path.join(qlfpga_plugins, "qlf_k4n8/slow/vpr_arch/UMC22nm_vpr.xml")
53 | repacking_rules = os.path.join(qlfpga_plugins, "qlf_k4n8/repacking_rules.json")
54 |
55 | eblif_ref = os.path.join(basedir, "lut{}_{}.golden.eblif".format(lut_width, lut_inputs))
56 | eblif_in = os.path.join(basedir, "lut{}.eblif".format(lut_width))
57 | net_in = os.path.join(basedir, "lut{}_{}.net".format(lut_width, lut_inputs))
58 |
59 | with tempfile.TemporaryDirectory() as tempdir:
60 | eblif_out = os.path.join(tempdir, "out.eblif")
61 | net_out = os.path.join(tempdir, "out.net")
62 |
63 | # Substitute commandline arguments
64 | monkeypatch.setattr(
65 | "sys.argv",
66 | [
67 | "repack.py",
68 | "--vpr-arch",
69 | vpr_arch,
70 | "--repacking-rules",
71 | repacking_rules,
72 | "--eblif-in",
73 | eblif_in,
74 | "--net-in",
75 | net_in,
76 | "--eblif-out",
77 | eblif_out,
78 | "--net-out",
79 | net_out,
80 | ],
81 | )
82 |
83 | # Invoke the repacker
84 | repack_main()
85 |
86 | # Compare output with the golden reference
87 | with open(eblif_ref, "r") as fp:
88 | golden_data = fp.read().rstrip()
89 | with open(eblif_out, "r") as fp:
90 | output_data = fp.read().rstrip()
91 |
92 | assert golden_data == output_data
93 |
--------------------------------------------------------------------------------
/f4pga/utils/quicklogic/repacker/tests/packed_netlist_roundtrip/test_netlist_roundtrip.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 | import os
20 | import sys
21 | import tempfile
22 |
23 | import lxml.etree as ET
24 |
25 | sys.path.append(os.path.join(os.path.dirname(__file__), "..", ".."))
26 | from packed_netlist import PackedNetlist # noqa: E402
27 |
28 | # =============================================================================
29 |
30 |
31 | def test_netlist_roundtrip():
32 | basedir = os.path.dirname(__file__)
33 | golden_file = os.path.join(basedir, "netlist.golden.net")
34 |
35 | # Load the XSLT for sorting
36 | xslt_file = os.path.join(basedir, "../sort_netlist.xsl")
37 | xslt = ET.parse(xslt_file)
38 | xform = ET.XSLT(xslt)
39 |
40 | # Load the golden netlist XML
41 | xml_tree = ET.parse(golden_file, ET.XMLParser(remove_blank_text=True))
42 |
43 | with tempfile.TemporaryDirectory() as tempdir:
44 | # Transform and save the golden file
45 | sorted_golden_file = os.path.join(tempdir, "netlist.golden.sorted.net")
46 | with open(sorted_golden_file, "w") as fp:
47 | et = xform(xml_tree)
48 | st = '\n' + ET.tostring(et, pretty_print=True).decode("utf-8")
49 | fp.write(st)
50 |
51 | # Build packed netlist
52 | netlist = PackedNetlist.from_etree(xml_tree.getroot())
53 |
54 | # Convert the netlist back to element tree
55 | xml_tree = ET.ElementTree(netlist.to_etree())
56 |
57 | # Transform and save the output file
58 | sorted_output_file = os.path.join(tempdir, "netlist.output.sorted.net")
59 | with open(sorted_output_file, "w") as fp:
60 | et = xform(xml_tree)
61 | st = '\n' + ET.tostring(et, pretty_print=True).decode("utf-8")
62 | fp.write(st)
63 |
64 | # Compare the two files
65 | with open(sorted_golden_file, "r") as fp:
66 | golden_data = fp.read()
67 | with open(sorted_output_file, "r") as fp:
68 | output_data = fp.read()
69 |
70 | assert golden_data == output_data
71 |
--------------------------------------------------------------------------------
/f4pga/utils/quicklogic/repacker/tests/sort_netlist.xsl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/f4pga/utils/quicklogic/yosys_fixup_cell_names.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2022 F4PGA Authors
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | """
21 | This is an utility script that performs cell instance renaming in a design in
22 | Yosys JSON format. Cell instance names containing dots are altered so that
23 | all dots are replaced with underscores.
24 | """
25 |
26 | import argparse
27 | import json
28 |
29 |
30 | def fixup_cell_names(design):
31 | """
32 | Scans Yosys' JSON data structure and replaces cell instance names that
33 | contains dots in names with other character.
34 | """
35 |
36 | # Process modules
37 | modules = design["modules"]
38 | for mod_name, mod_data in modules.items():
39 | print(mod_name)
40 |
41 | # Process cells
42 | cells = mod_data["cells"]
43 | for cell_name in list(cells.keys()):
44 | # Fixup name
45 | if "." in cell_name:
46 | new_name = cell_name.replace(".", "_")
47 | assert new_name not in cells, new_name
48 |
49 | cells[new_name] = cells[cell_name]
50 | del cells[cell_name]
51 |
52 | return design
53 |
54 |
55 | def main():
56 | parser = argparse.ArgumentParser()
57 | parser.add_argument("i", type=str, help="Yosys JSON in")
58 | parser.add_argument("o", type=str, help="Yosys JSON out")
59 |
60 | args = parser.parse_args()
61 |
62 | with open(args.i, "r") as fp:
63 | design = fixup_cell_names(json.load(fp))
64 |
65 | with open(args.o, "w") as fp:
66 | json.dump(design, fp, indent=2)
67 |
68 |
69 | if __name__ == "__main__":
70 | main()
71 |
--------------------------------------------------------------------------------
/f4pga/wrappers/sh/generate_constraints.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2020-2022 F4PGA Authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | from f4pga.wrappers.sh import generate_constraints
21 |
22 | if __name__ == "__main__":
23 | generate_constraints()
24 |
--------------------------------------------------------------------------------
/f4pga/wrappers/sh/quicklogic/synth.f4pga.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #
3 | # Copyright (C) 2020-2022 F4PGA Authors.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # https://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | #
17 | # SPDX-License-Identifier: Apache-2.0
18 |
19 | set -e
20 |
21 | print_usage () {
22 | echo "Usage: symbiflow_synth -v|--verilog "
23 | echo " [-t|--top ]"
24 | echo " [-F|--family ]"
25 | echo " [-d|--device ]"
26 | echo " [-P|--part ]"
27 | echo " [-p|--pcf ]"
28 | echo " [-y "
29 | echo " [+libext+]"
30 | echo " [+incdir+]"
31 | echo " [+define+[=]]"
32 | echo " [-f ]"
33 | echo ""
34 | exit 1
35 | }
36 |
37 | VERILOG_FILES=()
38 | TOP="top"
39 | DEVICE=""
40 | FAMILY=""
41 | PART=""
42 | PCF=""
43 | EXTRA_ARGS=()
44 |
45 | OPT=""
46 | for arg in $@; do
47 | case $arg in
48 | -t|--top) OPT="top" ;;
49 | -v|--verilog) OPT="vlog" ;;
50 | -d|--device) OPT="dev" ;;
51 | -F|--family) OPT="family" ;;
52 | -P|--part) OPT="part" ;;
53 | -p|--pcf) OPT="pcf" ;;
54 | -y|-f|+incdir+*|+libext+*|+define+*) OPT="xtra" ;;
55 | *)
56 | case $OPT in
57 | "top") TOP=$arg; OPT="" ;;
58 | "dev") DEVICE=$arg; OPT="" ;;
59 | "family") FAMILY=$arg; OPT="" ;;
60 | "part") PART=$arg; OPT="" ;;
61 | "pcf") PCF=$arg; OPT="" ;;
62 | "vlog") VERILOG_FILES+=($arg) ;;
63 | "xtra") ;;
64 | *) print_usage ;;
65 | esac
66 | ;;
67 | esac
68 | if [ "$OPT" == "xtra" ]; then EXTRA_ARGS+=($arg); fi
69 | done
70 |
71 | if [ -z ${FAMILY} ]; then echo "Please specify device family"; exit 1; fi
72 | if [ ${#VERILOG_FILES[@]} -eq 0 ]; then echo "Please provide at least one Verilog file"; exit 1; fi
73 |
74 | PINMAPCSV="pinmap_${PART}.csv"
75 |
76 | export USE_ROI="FALSE"
77 | export OUT_JSON=$TOP.json
78 | export SYNTH_JSON=${TOP}_io.json
79 | export OUT_SYNTH_V=${TOP}_synth.v
80 | export OUT_EBLIF=${TOP}.eblif
81 | export OUT_FASM_EXTRA=${TOP}_fasm_extra.fasm
82 | export PYTHON3=$(which python3)
83 |
84 | if [ -s $PCF ]; then
85 | export PCF_FILE=$PCF
86 | else
87 | export PCF_FILE=""
88 | fi
89 |
90 | DEVICE_PATH="${F4PGA_SHARE_DIR}/arch/${DEVICE}_${DEVICE}"
91 | export PINMAP_FILE=${DEVICE_PATH}/${PINMAPCSV}
92 | if [ -d "${DEVICE_PATH}/cells" ]; then
93 | export DEVICE_CELLS_SIM=`find ${DEVICE_PATH}/cells -name "*_sim.v"`
94 | export DEVICE_CELLS_MAP=`find ${DEVICE_PATH}/cells -name "*_map.v"`
95 | else
96 | # pp3 family has different directory naming scheme
97 | # the are named as ${DEVICE}_${PACKAGE}
98 | # ${PACKAGE} is not known because it is not passed down in add_binary_toolchain_test
99 | DEVICE_PATH=$(find "${F4PGA_SHARE_DIR}"/arch/ -type d -name "${DEVICE}*")
100 | export PINMAP_FILE=${DEVICE_PATH}/${PINMAPCSV}
101 | if [ -d "${DEVICE_PATH}/cells" ]; then
102 | export DEVICE_CELLS_SIM=`find ${DEVICE_PATH}/cells -name "*_sim.v"`
103 | export DEVICE_CELLS_MAP=`find ${DEVICE_PATH}/cells -name "*_map.v"`
104 | else
105 | export DEVICE_CELLS_SIM=
106 | export DEVICE_CELLS_MAP=
107 | fi
108 | fi
109 |
110 | yosys_cmds=`echo ${EXTRA_ARGS[*]} | python3 -m f4pga.utils.quicklogic.convert_compile_opts`
111 | if [ ! -z "${yosys_cmds}" ]; then yosys_cmds="${yosys_cmds//$'\n'/'; '}; "; fi
112 |
113 | yosys_read_cmds=''
114 | for f in ${VERILOG_FILES[*]}; do
115 | yosys_read_cmds="read_verilog ${f}; $yosys_read_cmds"
116 | done
117 |
118 | `which yosys` \
119 | -p "$yosys_cmds $yosys_read_cmds tcl $(python3 -m f4pga.wrappers.tcl "${FAMILY}")" \
120 | -l "${TOP}_synth.log"
121 |
--------------------------------------------------------------------------------
/f4pga/wrappers/sh/vpr_run.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2020-2022 F4PGA Authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | from sys import argv as sys_argv
21 |
22 | from f4pga.wrappers.sh import p_vpr_run
23 |
24 | if __name__ == "__main__":
25 | p_vpr_run(sys_argv[1:])
26 |
--------------------------------------------------------------------------------
/f4pga/wrappers/sh/xc7/synth.f4pga.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #
3 | # Copyright (C) 2020-2022 F4PGA Authors.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # https://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | #
17 | # SPDX-License-Identifier: Apache-2.0
18 |
19 | set -e
20 |
21 | VERILOG_FILES=()
22 | XDC_FILES=()
23 | TOP=top
24 | DEVICE="*"
25 | PART=""
26 | SURELOG_CMD=()
27 |
28 | VERILOGLIST=0
29 | XDCLIST=0
30 | TOPNAME=0
31 | DEVICENAME=0
32 | PARTNAME=0
33 | SURELOG=0
34 |
35 | for arg in $@; do
36 | echo $arg
37 | case "$arg" in
38 | -v|--verilog) VERILOGLIST=1 XDCLIST=0 TOPNAME=0 DEVICENAME=0 PARTNAME=0 SURELOG=0 ;;
39 | -x|--xdc) VERILOGLIST=0 XDCLIST=1 TOPNAME=0 DEVICENAME=0 PARTNAME=0 SURELOG=0 ;;
40 | -t|--top) VERILOGLIST=0 XDCLIST=0 TOPNAME=1 DEVICENAME=0 PARTNAME=0 SURELOG=0 ;;
41 | -d|--device) VERILOGLIST=0 XDCLIST=0 TOPNAME=0 DEVICENAME=1 PARTNAME=0 SURELOG=0 ;;
42 | -p|--part) VERILOGLIST=0 XDCLIST=0 TOPNAME=0 DEVICENAME=0 PARTNAME=1 SURELOG=0 ;;
43 | -s|--surelog) VERILOGLIST=0 XDCLIST=0 TOPNAME=0 DEVICENAME=0 PARTNAME=0 SURELOG=1 ;;
44 | *)
45 | if [ $VERILOGLIST -eq 1 ]; then
46 | VERILOG_FILES+=($arg)
47 | elif [ $XDCLIST -eq 1 ]; then
48 | XDC_FILES+=($arg)
49 | elif [ $TOPNAME -eq 1 ]; then
50 | TOP=$arg
51 | elif [ $DEVICENAME -eq 1 ]; then
52 | DEVICE=$arg
53 | elif [ $PARTNAME -eq 1 ]; then
54 | PART=$arg
55 | elif [ $SURELOG -eq 1 ]; then
56 | SURELOG_CMD+=($arg)
57 | else
58 | echo "Usage: synth [-t|--top -v|--verilog [-x|--xdc ]"
59 | echo " [-d|--device ] [-p|--part ] [-s|--surelog] "
60 | echo "note: device and part parameters are required if xdc is passed"
61 | exit 1
62 | fi
63 | ;;
64 | esac
65 | done
66 |
67 | if [ ${#VERILOG_FILES[@]} -eq 0 ]; then echo "Please provide at least one Verilog file"; exit 1; fi
68 |
69 | export TOP="${TOP}"
70 | export USE_ROI='FALSE'
71 | export INPUT_XDC_FILES="${XDC_FILES[*]}"
72 | export OUT_JSON="$TOP.json"
73 | export OUT_SDC="${TOP}.sdc"
74 | export SYNTH_JSON="${TOP}_io.json"
75 | export OUT_SYNTH_V="${TOP}_synth.v"
76 | export OUT_EBLIF="${TOP}.eblif"
77 | export PART_JSON=`realpath ${DATABASE_DIR:-$(prjxray-config)}/$DEVICE/$PART/part.json`
78 | export OUT_FASM_EXTRA="${TOP}_fasm_extra.fasm"
79 | export PYTHON3="${PYTHON3:-$(which python3)}"
80 |
81 | yosys_read_cmds=""
82 | yosys_files="${VERILOG_FILES[*]}"
83 | if [ -n "$SURELOG_CMD" ]; then
84 | yosys_read_cmds="plugin -i systemverilog; read_systemverilog ${SURELOG_CMD[*]} ${VERILOG_FILES[*]}"
85 | yosys_files=""
86 | fi
87 | yosys \
88 | -p "$yosys_read_cmds; tcl $(python3 -m f4pga.wrappers.tcl)" \
89 | -l "${TOP}_synth.log" \
90 | $yosys_files
91 |
--------------------------------------------------------------------------------
/f4pga/wrappers/tcl/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2020-2022 F4PGA Authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 | #
20 | # TCL scripts moved from f4pga-arch-defs
21 |
22 | from pathlib import Path
23 |
24 | from f4pga.context import FPGA_FAM
25 |
26 | ROOT = Path(__file__).resolve().parent
27 |
28 | ARCHS = {"xc7": ["artix7", "artix7_100t", "artix7_200t", "zynq7", "zynq7_z020", "spartan7"], "eos-s3": ["ql-s3", "pp3"]}
29 |
30 |
31 | def get_script_path(arch=None, pnrtool="vpr"):
32 | if arch is None:
33 | arch = FPGA_FAM
34 | for key, val in ARCHS.items():
35 | if arch in val:
36 | arch = key
37 | break
38 | if arch not in ["xc7", "eos-s3", "qlf_k4n8", "ice40"]:
39 | raise (Exception(f"Unsupported arch <{arch}>!"))
40 | suffix = f".{pnrtool}" if arch == "ice40" else ""
41 | return ROOT / f"{arch}{suffix}.f4pga.tcl"
42 |
--------------------------------------------------------------------------------
/f4pga/wrappers/tcl/__main__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2020-2022 F4PGA Authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 |
21 | from sys import argv as sys_argv
22 |
23 | from f4pga.wrappers.tcl import get_script_path
24 |
25 |
26 | if __name__ == "__main__":
27 | print(get_script_path(sys_argv[1] if len(sys_argv) > 1 else None))
28 |
--------------------------------------------------------------------------------
/f4pga/wrappers/tcl/ice40.nextpnr.f4pga.tcl:
--------------------------------------------------------------------------------
1 | yosys -import
2 |
3 | synth_ice40 -nocarry
4 |
5 | opt_expr -undriven
6 | opt_clean
7 |
8 | attrmap -remove hdlname
9 | setundef -zero -params
10 |
11 | write_json $::env(OUT_JSON)
12 | #write_verilog $::env(OUT_SYNTH_V)
13 |
--------------------------------------------------------------------------------
/f4pga/wrappers/tcl/ice40.vpr.f4pga.tcl:
--------------------------------------------------------------------------------
1 | yosys -import
2 |
3 | proc clean_processes {} {
4 | proc_clean
5 | proc_rmdead
6 | proc_prune
7 | proc_init
8 | proc_arst
9 | proc_mux
10 | proc_dlatch
11 | proc_dff
12 | proc_memwr
13 | proc_clean
14 | }
15 |
16 | synth_ice40 -nocarry
17 |
18 | # opt_expr -undriven makes sure all nets are driven, if only by the $undef
19 | # net.
20 | opt_expr -undriven
21 | opt_clean
22 |
23 | # TODO: remove this as soon as new VTR master+wip is pushed: https://github.com/SymbiFlow/vtr-verilog-to-routing/pull/525
24 | attrmap -remove hdlname
25 |
26 | setundef -zero -params
27 | clean_processes
28 | write_json $::env(OUT_JSON)
29 | write_verilog $::env(OUT_SYNTH_V)
30 |
31 | design -reset
32 | exec $::env(PYTHON3) -m f4pga.utils.yosys_split_inouts -i $::env(OUT_JSON) -o $::env(SYNTH_JSON)
33 | read_json $::env(SYNTH_JSON)
34 | yosys -import
35 | opt_clean
36 | write_blif -attr -cname -param $::env(OUT_EBLIF)
37 |
--------------------------------------------------------------------------------
/f4pga/wrappers/tcl/qlf_k4n8.f4pga.tcl:
--------------------------------------------------------------------------------
1 | yosys -import
2 |
3 | # Load the QuickLogic qlf_k4n8 support plugin. Note that this is only temporary
4 | # until support for the device is merged into the upstream Yosys
5 | plugin -i ql-qlf
6 | yosys -import
7 |
8 | # Read VPR cells library
9 | read_verilog -lib $::env(TECHMAP_PATH)/cells_sim.v
10 |
11 | # Synthesize
12 | if {[info exists ::env(SYNTH_OPTS)]} {
13 | synth_quicklogic -family qlf_k4n8 $::env(SYNTH_OPTS)
14 | } else {
15 | synth_quicklogic -family qlf_k4n8
16 | }
17 |
18 | # Write a pre-mapped design
19 | write_verilog $::env(OUT_SYNTH_V).premap.v
20 |
21 | # Map to the VPR cell library
22 | techmap -map $::env(TECHMAP_PATH)/cells_map.v
23 |
24 | # opt_expr -undriven makes sure all nets are driven, if only by the $undef
25 | # net.
26 | opt_expr -undriven
27 | opt_clean
28 |
29 | stat
30 |
31 | write_json $::env(OUT_JSON)
32 | write_verilog $::env(OUT_SYNTH_V)
33 |
34 | design -reset
35 | exec $::env(PYTHON3) -m f4pga.utils.yosys_split_inouts -i $::env(OUT_JSON) -o $::env(SYNTH_JSON)
36 | read_json $::env(SYNTH_JSON)
37 | yosys -import
38 | opt_clean
39 | write_blif -attr -cname -param $::env(OUT_EBLIF)
40 |
--------------------------------------------------------------------------------
/readthedocs.yml:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2020-2022 F4PGA Authors.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 | # SPDX-License-Identifier: Apache-2.0
16 | #
17 | # Read the Docs configuration file
18 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
19 |
20 | version: 2
21 |
22 | build:
23 | os: ubuntu-22.04
24 | tools:
25 | python: "mambaforge-22.9"
26 |
27 | sphinx:
28 | configuration: docs/conf.py
29 |
30 | formats: []
31 |
32 | conda:
33 | environment: docs/environment.yml
34 |
35 | submodules:
36 | include: all
37 | recursive: true
38 |
--------------------------------------------------------------------------------
/scripts/activate.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Copyright (C) 2020-2022 F4PGA Authors.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # https://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | #
17 | # SPDX-License-Identifier: Apache-2.0
18 |
19 | set -e
20 |
21 | source "${F4PGA_INSTALL_DIR}/${FPGA_FAM}/conda/etc/profile.d/conda.sh"
22 | conda activate $FPGA_FAM
23 |
--------------------------------------------------------------------------------
/scripts/prepare_environment.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Copyright (C) 2020-2022 F4PGA Authors.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # https://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | #
17 | # SPDX-License-Identifier: Apache-2.0
18 |
19 | set -e
20 |
21 |
22 | echo '::group::Install dependencies'
23 |
24 | sudo apt update -y
25 | sudo apt install -y git wget xz-utils
26 |
27 | echo '::endgroup::'
28 |
29 |
30 | echo '::group::Environment variables'
31 |
32 | FPGA_FAM="${FPGA_FAM:=xc7}"
33 | F4PGA_INSTALL_DIR="${F4PGA_INSTALL_DIR:=/opt/f4pga}"
34 | F4PGA_INSTALL_DIR_FAM="${F4PGA_INSTALL_DIR}/${FPGA_FAM}"
35 |
36 | echo "FPGA_FAM: $FPGA_FAM"
37 | echo "F4PGA_INSTALL_DIR: $F4PGA_INSTALL_DIR"
38 | echo "F4PGA_INSTALL_DIR_FAM: $F4PGA_INSTALL_DIR_FAM"
39 |
40 | echo '::endgroup::'
41 |
42 |
43 | echo '::group::Install Miniconda3'
44 |
45 | wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O conda_installer.sh
46 |
47 | bash conda_installer.sh -u -b -p "${F4PGA_INSTALL_DIR_FAM}"/conda
48 | source "${F4PGA_INSTALL_DIR_FAM}"/conda/etc/profile.d/conda.sh
49 |
50 | echo '::endgroup::'
51 |
52 |
53 | echo '::group::Install arch-defs'
54 |
55 | mkdir -p "$F4PGA_INSTALL_DIR_FAM"
56 |
57 | F4PGA_TIMESTAMP='20220907-210059'
58 | F4PGA_HASH='66a976d'
59 |
60 | case "$FPGA_FAM" in
61 | xc7) PACKAGES='install-xc7 xc7a50t_test';;
62 | eos-s3) PACKAGES='install-ql ql-eos-s3_wlcsp';;
63 | *)
64 | echo "Unknowd FPGA_FAM <${FPGA_FAM}>!"
65 | exit 1
66 | esac
67 |
68 | for PKG in $PACKAGES; do
69 | wget -qO- https://storage.googleapis.com/symbiflow-arch-defs/artifacts/prod/foss-fpga-tools/symbiflow-arch-defs/continuous/install/${F4PGA_TIMESTAMP}/symbiflow-arch-defs-${PKG}-${F4PGA_HASH}.tar.xz \
70 | | tar -xJC $F4PGA_INSTALL_DIR_FAM
71 | done
72 |
73 | rm -vrf $F4PGA_INSTALL_DIR_FAM/share/f4pga/scripts
74 |
75 | echo '::endgroup::'
76 |
77 |
78 | echo '::group::Create environment'
79 |
80 | conda env create -f $F4PGA_INSTALL_DIR_FAM/"$FPGA_FAM"_env/"$FPGA_FAM"_environment.yml
81 |
82 | echo '::endgroup::'
83 |
--------------------------------------------------------------------------------
/test/constraints/arty.xdc:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2020-2022 F4PGA Authors.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # https://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 | # SPDX-License-Identifier: Apache-2.0
16 |
17 | # Clock pin
18 | set_property PACKAGE_PIN E3 [get_ports {CLK}]
19 | set_property IOSTANDARD LVCMOS33 [get_ports {CLK}]
20 |
21 | # LEDs
22 | set_property PACKAGE_PIN H5 [get_ports {LEDs[0]}]
23 | set_property PACKAGE_PIN J5 [get_ports {LEDs[1]}]
24 | set_property PACKAGE_PIN T9 [get_ports {LEDs[2]}]
25 | set_property PACKAGE_PIN T10 [get_ports {LEDs[3]}]
26 | set_property IOSTANDARD LVCMOS33 [get_ports {LEDs[0]}]
27 | set_property IOSTANDARD LVCMOS33 [get_ports {LEDs[1]}]
28 | set_property IOSTANDARD LVCMOS33 [get_ports {LEDs[2]}]
29 | set_property IOSTANDARD LVCMOS33 [get_ports {LEDs[3]}]
30 |
31 | # Clock constraints
32 | create_clock -period 10.0 [get_ports {CLK}]
33 |
--------------------------------------------------------------------------------
/test/requirements.txt:
--------------------------------------------------------------------------------
1 | black
2 | pytest
3 |
--------------------------------------------------------------------------------
/test/test_wrappers.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright (C) 2020-2022 F4PGA Authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 |
20 | from os import environ
21 | from pytest import mark
22 | from sys import stdout, stderr
23 |
24 | from subprocess import check_call
25 |
26 |
27 | wrappers = [
28 | 'symbiflow_generate_constraints',
29 | 'symbiflow_pack',
30 | 'symbiflow_place',
31 | 'symbiflow_route',
32 | 'symbiflow_synth',
33 | 'symbiflow_write_bitstream',
34 | 'symbiflow_write_fasm',
35 | 'symbiflow_analysis',
36 | 'symbiflow_repack',
37 | 'symbiflow_generate_bitstream',
38 | 'symbiflow_generate_libfile',
39 | 'ql_symbiflow'
40 | ]
41 |
42 | @mark.xfail
43 | @mark.parametrize(
44 | "wrapper",
45 | wrappers
46 | )
47 | def test_shell_wrapper(wrapper):
48 | print(f"\n::group::Test {wrapper}")
49 | stdout.flush()
50 | stderr.flush()
51 | try:
52 | check_call(f"{wrapper}")
53 | finally:
54 | print("\n::endgroup::")
55 |
56 | @mark.xfail
57 | @mark.parametrize(
58 | "wrapper",
59 | wrappers
60 | )
61 | def test_shell_wrapper_without_F4PGA_INSTALL_DIR(wrapper):
62 | test_environ = environ.copy()
63 | del test_environ['F4PGA_INSTALL_DIR']
64 |
65 | print(f"\n::group::Test {wrapper}")
66 | stdout.flush()
67 | stderr.flush()
68 | try:
69 | check_call(f"{wrapper}", env=test_environ)
70 | finally:
71 | print("\n::endgroup::")
72 |
--------------------------------------------------------------------------------
/test/verilog/counter/arty_35.json:
--------------------------------------------------------------------------------
1 | {
2 | "default_part": "XC7A35TCSG324-1",
3 | "values": {
4 | "top": "top"
5 | },
6 | "dependencies": {
7 | "sources": [
8 | "counter.v"
9 | ],
10 | "synth_log": "synth.log",
11 | "pack_log": "pack.log"
12 | },
13 | "XC7A35TCSG324-1": {
14 | "default_target": "bitstream",
15 | "dependencies": {
16 | "build_dir": "build/arty_35",
17 | "xdc": [
18 | "../../constraints/arty.xdc"
19 | ]
20 | },
21 | "values": {
22 | "part": "xc7a35tcpg236-1"
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/test/verilog/counter/counter.v:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2020-2022 F4PGA Authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * SPDX-License-Identifier: Apache-2.0
17 | */
18 |
19 | module top (
20 | input CLK,
21 | output [3:0] LEDs
22 | );
23 |
24 | localparam BITS = 4;
25 | localparam LOG2DELAY = 22;
26 |
27 | wire bufg;
28 | BUFG bufgctrl (
29 | .I(CLK),
30 | .O(bufg)
31 | );
32 |
33 | reg [BITS+LOG2DELAY-1:0] counter = 0;
34 |
35 | always @(posedge bufg) begin
36 | counter <= counter + 1;
37 | end
38 |
39 | assign LEDs[3:0] = counter >> LOG2DELAY;
40 | endmodule
41 |
--------------------------------------------------------------------------------
/test/vhdl/counter/.gitignore:
--------------------------------------------------------------------------------
1 | top.v
2 |
--------------------------------------------------------------------------------
/test/vhdl/counter/Makefile:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2020-2022 F4PGA Authors.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # https://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 | # SPDX-License-Identifier: Apache-2.0
16 |
17 | synth:
18 | docker run --rm \
19 | -v /$(shell pwd)://wrk -w //wrk \
20 | gcr.io/hdl-containers/ghdl \
21 | ghdl synth --std=08 --out=verilog counter.vhd -e Arty_Counter > Arty_Counter.v
22 |
23 | synth-plugin:
24 | docker run --rm \
25 | -v /$(shell pwd)://wrk -w //wrk \
26 | gcr.io/hdl-containers/ghdl/yosys \
27 | yosys -m ghdl -p 'ghdl --std=08 counter.vhd -e Arty_Counter; write_verilog Arty_Counter.v'
28 |
--------------------------------------------------------------------------------
/test/vhdl/counter/arty_35.json:
--------------------------------------------------------------------------------
1 | {
2 | "default_part": "XC7A35TCSG324-1",
3 | "values": {
4 | "top": "Arty_Counter"
5 | },
6 | "dependencies": {
7 | "sources": [
8 | "Arty_Counter.v"
9 | ],
10 | "synth_log": "synth.log",
11 | "pack_log": "pack.log"
12 | },
13 | "XC7A35TCSG324-1": {
14 | "default_target": "bitstream",
15 | "dependencies": {
16 | "build_dir": "build/arty_35",
17 | "xdc": [
18 | "../../constraints/arty.xdc"
19 | ]
20 | },
21 | "values": {
22 | "part": "xc7a35tcpg236-1"
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/test/vhdl/counter/counter.vhd:
--------------------------------------------------------------------------------
1 | -- Copyright (C) 2020-2022 F4PGA Authors.
2 | --
3 | -- Licensed under the Apache License, Version 2.0 (the "License");
4 | -- you may not use this file except in compliance with the License.
5 | -- You may obtain a copy of the License at
6 | --
7 | -- https://www.apache.org/licenses/LICENSE-2.0
8 | --
9 | -- Unless required by applicable law or agreed to in writing, software
10 | -- distributed under the License is distributed on an "AS IS" BASIS,
11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | -- See the License for the specific language governing permissions and
13 | -- limitations under the License.
14 | --
15 | -- SPDX-License-Identifier: Apache-2.0
16 |
17 | library ieee;
18 | context ieee.ieee_std_context;
19 |
20 | entity Arty_Counter is
21 | port (
22 | CLK : in std_logic;
23 | LEDs : out std_logic_vector(3 downto 0)
24 | );
25 | end;
26 |
27 | architecture arch of Arty_Counter is
28 |
29 | constant LOG2DELAY : natural := 22;
30 |
31 | signal counter : unsigned(LEDs'length+LOG2DELAY-1 downto 0) := (others=>'0');
32 |
33 | begin
34 |
35 | process (CLK) begin
36 | counter <= counter + 1 when rising_edge(CLK);
37 | end process;
38 |
39 | LEDs <= std_logic_vector(resize(shift_right(counter, LOG2DELAY), LEDs'length));
40 |
41 | end;
42 |
--------------------------------------------------------------------------------