├── .gitattributes ├── .gitcommit ├── .github ├── dependabot.yml ├── eos-s3_test.json ├── ice40_test.json ├── workflows │ ├── Action.yml │ ├── Automerge.yml │ └── Pipeline.yml └── xc7_test.json ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── action └── action.yml ├── docs ├── .gitignore ├── Makefile ├── _static │ ├── favicon.svg │ ├── images │ │ ├── EDA.svg │ │ ├── flow.png │ │ ├── hero.svg │ │ ├── parts.svg │ │ ├── step.png │ │ ├── tool.png │ │ └── toolchain-flow.svg │ └── logo.svg ├── changes.py ├── changes.yml ├── community.rst ├── conf.py ├── development │ ├── building-docs.rst │ ├── changes.rst │ └── venv.rst ├── environment.yml ├── f4pga │ ├── Deprecated.rst │ ├── DevNotes.md │ ├── Usage.md │ ├── browse_pydoc.sh │ ├── index.rst │ └── modules │ │ ├── fasm.md │ │ ├── generic_script_wrapper.md │ │ ├── index.md │ │ ├── io_rename.md │ │ ├── mkdirs.md │ │ ├── pack.md │ │ ├── place.md │ │ ├── place_constraints.md │ │ ├── route.md │ │ └── synth.md ├── flows │ ├── bitstream.rst │ ├── f4pga.rst │ ├── index.rst │ ├── pnr.rst │ └── synthesis.rst ├── getting-started.rst ├── glossary.rst ├── how.rst ├── index.rst ├── references.rst ├── refs.bib ├── requirements.txt └── status.rst ├── f4pga ├── __main__.py ├── context.py ├── flows │ ├── __init__.py │ ├── argparser.py │ ├── cache.py │ ├── commands.py │ ├── common.py │ ├── flow.py │ ├── flow_config.py │ ├── inspector.py │ ├── module.py │ ├── modules │ │ ├── analysis.py │ │ ├── fasm.py │ │ ├── generic_script_wrapper.py │ │ ├── io_rename.py │ │ ├── mkdirs.py │ │ ├── nextpnr_ice40.py │ │ ├── pack.py │ │ ├── place.py │ │ ├── place_constraints.py │ │ ├── route.py │ │ └── yosys.py │ ├── part_db.yml │ ├── platforms.yml │ ├── requirements.txt │ ├── runner.py │ ├── stage.py │ └── tools │ │ ├── nextpnr.py │ │ └── vpr.py ├── pyproject.toml ├── requirements.txt ├── setup.py ├── utils │ ├── eblif.py │ ├── pcf.py │ ├── quicklogic │ │ ├── convert_compile_opts.py │ │ ├── create_lib.py │ │ ├── pinmap_parse.py │ │ ├── pp3 │ │ │ ├── arch_import.py │ │ │ ├── connections.py │ │ │ ├── create_default_fasm.py │ │ │ ├── create_ioplace.py │ │ │ ├── create_place_constraints.py │ │ │ ├── data_import.py │ │ │ ├── data_structs.py │ │ │ ├── eos-s3 │ │ │ │ └── iomux_config.py │ │ │ ├── fasm2bels.py │ │ │ ├── prepare_vpr_database.py │ │ │ ├── routing_import.py │ │ │ ├── rr_utils.py │ │ │ ├── switchbox_model.py │ │ │ ├── tile_import.py │ │ │ ├── timing.py │ │ │ ├── utils.py │ │ │ ├── verilogmodule.py │ │ │ └── vis_switchboxes.py │ │ ├── process_sdc_constraints.py │ │ ├── qlf_k4n8 │ │ │ └── create_ioplace.py │ │ ├── repacker │ │ │ ├── README.md │ │ │ ├── arch_xml_utils.py │ │ │ ├── block_path.py │ │ │ ├── eblif_netlist.py │ │ │ ├── netlist_cleaning.py │ │ │ ├── packed_netlist.py │ │ │ ├── pb_rr_graph.py │ │ │ ├── pb_rr_graph_netlist.py │ │ │ ├── pb_rr_graph_router.py │ │ │ ├── pb_type.py │ │ │ ├── repack.py │ │ │ └── tests │ │ │ │ ├── eblif_roundtrip │ │ │ │ ├── netlist.golden.eblif │ │ │ │ └── test_eblif_roundtrip.py │ │ │ │ ├── identity.xsl │ │ │ │ ├── lut_padding │ │ │ │ ├── lut1.eblif │ │ │ │ ├── lut1_0.golden.eblif │ │ │ │ ├── lut1_0.net │ │ │ │ ├── lut1_1.golden.eblif │ │ │ │ ├── lut1_1.net │ │ │ │ ├── lut1_2.golden.eblif │ │ │ │ ├── lut1_2.net │ │ │ │ ├── lut1_3.golden.eblif │ │ │ │ ├── lut1_3.net │ │ │ │ └── test_lut_padding.py │ │ │ │ ├── packed_netlist_roundtrip │ │ │ │ ├── netlist.golden.net │ │ │ │ └── test_netlist_roundtrip.py │ │ │ │ └── sort_netlist.xsl │ │ └── yosys_fixup_cell_names.py │ ├── vpr_io_place.py │ ├── xc7 │ │ ├── create_ioplace.py │ │ ├── create_place_constraints.py │ │ └── fix_xc7_carry.py │ └── yosys_split_inouts.py └── wrappers │ ├── sh │ ├── __init__.py │ ├── generate_constraints.py │ ├── quicklogic │ │ ├── ql.f4pga.sh │ │ └── synth.f4pga.sh │ ├── vpr_run.py │ └── xc7 │ │ └── synth.f4pga.sh │ └── tcl │ ├── __init__.py │ ├── __main__.py │ ├── eos-s3.f4pga.tcl │ ├── ice40.nextpnr.f4pga.tcl │ ├── ice40.vpr.f4pga.tcl │ ├── qlf_k4n8.f4pga.tcl │ └── xc7.f4pga.tcl ├── readthedocs.yml ├── scripts ├── activate.sh └── prepare_environment.sh └── test ├── constraints └── arty.xdc ├── requirements.txt ├── test_wrappers.py ├── verilog └── counter │ ├── arty_35.json │ └── counter.v └── vhdl └── counter ├── .gitignore ├── Makefile ├── arty_35.json └── counter.vhd /.gitattributes: -------------------------------------------------------------------------------- 1 | /.gitcommit export-subst 2 | -------------------------------------------------------------------------------- /.gitcommit: -------------------------------------------------------------------------------- 1 | 0723f9ca4 2 | -------------------------------------------------------------------------------- /.github/dependabot.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 | version: 2 18 | updates: 19 | 20 | - package-ecosystem: gitsubmodule 21 | directory: / 22 | schedule: 23 | interval: daily 24 | open-pull-requests-limit: 99 25 | labels: 26 | - dependencies 27 | - third-party 28 | 29 | - package-ecosystem: github-actions 30 | directory: / 31 | schedule: 32 | interval: weekly 33 | open-pull-requests-limit: 99 34 | -------------------------------------------------------------------------------- /.github/eos-s3_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "default_part": "EOS3FF512-PDN64", 3 | "values": { 4 | "top": "top" 5 | }, 6 | "dependencies": { 7 | "sources": [ 8 | "btn_counter/btn_counter.v" 9 | ], 10 | "synth_log": "synth.log", 11 | "pack_log": "pack.log", 12 | "analysis_log": "analysis.log" 13 | }, 14 | "EOS3FF512-PDN64": { 15 | "default_target": "bitstream", 16 | "dependencies": { 17 | "build_dir": "build", 18 | "pcf": "btn_counter/chandalar.pcf", 19 | "sdc-in": "btn_counter/dummy.sdc" 20 | }, 21 | "values": { 22 | "part": "ql-eos-s3", 23 | "package": "PD64" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/ice40_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "default_part": "ICE40UP5K-UWG30", 3 | "values": { 4 | "top": "top", 5 | "extra_args": ["-D", "PVT=1"] 6 | }, 7 | "dependencies": { 8 | "sources": [ 9 | "blink.v" 10 | ], 11 | "synth_log": "synth.log", 12 | "nextpnr_log": "nextpnr.log" 13 | }, 14 | "ICE40UP5K-UWG30": { 15 | "default_target": "bitstream", 16 | "dependencies": { 17 | "build_dir": "build", 18 | "pcf": "../../../pcf/fomu-pvt.pcf" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/Automerge.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: Automerge 18 | 19 | on: 20 | pull_request: 21 | push: 22 | schedule: 23 | - cron: '0 0 * * *' 24 | workflow_dispatch: 25 | 26 | jobs: 27 | 28 | Pipeline: 29 | if: ${{ !(github.event_name != 'pull_request' && github.actor == 'dependabot[bot]') }} 30 | uses: ./.github/workflows/Pipeline.yml 31 | permissions: 32 | contents: write 33 | 34 | Action: 35 | if: ${{ !(github.event_name != 'pull_request' && github.actor == 'dependabot[bot]') }} 36 | uses: ./.github/workflows/Action.yml 37 | 38 | Automerge: 39 | needs: Pipeline 40 | if: github.event_name == 'pull_request' && github.actor == 'dependabot[bot]' && github.repository == 'chipsalliance/f4pga' 41 | runs-on: ubuntu-latest 42 | name: Automerge dependabot PRs 43 | permissions: 44 | contents: write 45 | 46 | steps: 47 | 48 | - name: Auto-merge Dependabot PR 49 | run: GITHUB_TOKEN='${{ github.token }}' gh pr merge '${{ github.event.pull_request.html_url }}' --squash 50 | -------------------------------------------------------------------------------- /.github/xc7_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "default_part": "XC7A35TCSG324-1", 3 | "values": { 4 | "top": "top" 5 | }, 6 | "dependencies": { 7 | "sources": [ 8 | "counter_test/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", 17 | "xdc": [ 18 | "counter_test/arty.xdc" 19 | ] 20 | }, 21 | "values": { 22 | "part": "xc7a35tcpg236-1" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.sw* 3 | /f4pga/build/ 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/make-env"] 2 | path = third_party/make-env 3 | url = https://github.com/SymbiFlow/make-env/ 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 4 | 6 | 8 |

9 | 10 | # FOSS Flows For FPGA (F4PGA) project 11 | 12 |

13 | 'Automerge' workflow status 15 |

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 | 15 | 37 | 44 | 50 | 54 | 55 | 57 | 59 | 64 | 65 | 66 | 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 | --------------------------------------------------------------------------------