-
Notifications
You must be signed in to change notification settings - Fork 8
Development Guide
Just as for normal use, you need to install all packages into the Poetry environment:
poetry installTip
Consider setting your Poetry cache directory somewhere else than the default if you have limited space!
We recommend sourcing the environment. Usually, its location can be found by running poetry env info. If you have issues here, please check out the Poetry documentation.
Now that the environment is sourced:
- In your IDE of choice (e.g., VS Code), select the interpreter from the environment for your workspace
- This ensures you get the correct hints and autocompletes
If you need to edit another package as part of your development, change the package to editable in the pyproject.toml. Further information is found in the Poetry documentation.
Before running FINN+ for the first time, we recommend running:
finn config create .This creates a template config in your cloned FINN+ repository. In this configuration, set:
FINN_DEPS: ./depsThis places all dependencies local to your FINN+ repository, enabling you to work on multiple FINN versions simultaneously. If you don't plan on changing dependencies, you can leave this to the default value to maintain a single shared dependency directory.
If you want your temporary build files to stay local to your repository as well, add the absolute path to your settings.yaml:
FINN_BUILD_DIR: <path>For more information about configuration, check the Settings wiki page.
You can now start finn using:
finn run build.py
# or
finn build cfg.yaml model.onnxBefore starting to do development on FINN it is a good idea to start with understanding the basics as a user. Going through all of the Tutorials is strongly recommended if you havenβt already done so. Additionally, please review the documentation available on Internals.
All of the FINN repositories mentioned above use a variant of the GitHub flow from https://guides.github.com/introduction/flow as further detailed below:
- The master or main branch contains the latest released version, with a version tag.
- The dev branch is where new feature branches get merged after testing. dev is βalmost ready to releaseβ at any time, and is tested with regular Jenkins/GitLab builds β including all unit tests and end-to-end tests.
- New features or fixes are developed in branches that split from dev and are named similar to feature/name_of_feature. Single-commit fixes may be made without feature branches.
- New features must come with unit tests and docstrings. If applicable, it must also be tested as part of an end-to-end flow, preferably with a new standalone test. Make sure the existing test suite (including end-to-end tests) still pass. When in doubt, consult with the FINN+ maintainers.
- When a new feature is ready, a pull request (PR) can be opened targeting the dev branch, with a brief description of what the PR changes or introduces.
- Larger features must be broken down into several, smaller PRs. If your PRs have dependencies on each other please state in which order they should be reviewed and merged.
When you run the finn command, the system performs several initialization steps:
- Entry point:
src/finn/interface/run_finn.pyinitializes the execution - Configuration: System reads
settings.yamlfiles (local and global) - Dependencies resolution:
- Poetry dependencies (from
pyproject.toml) - External dependencies (configured in
external_dependencies.yaml, or, depending on the version used, insrc/finn/interface/manage_deps.py)
- Poetry dependencies (from
- Environment setup:
-
FINN_DEPS: Points to dependency location -
FINN_BUILD_DIR: Specifies where generated files are stored
-
After initialization, the system:
- Creates a
DataflowBuildConfigobject - Starts the build flow
Warning
During testing, finn is not called directly. See the VS Code Integration section for testing-specific setup.
We use a pre-commit hook to auto-format Python code and check for issues. See https://pre-commit.com/ for installation. Once you have pre-commit, you can install the hooks into your local clone of the FINN+ repo:
pre-commit installEvery time you commit some code, the pre-commit hooks will first run, performing various checks and fixes. In some cases pre-commit wonβt be able to fix the issues and you may have to fix it manually, then run git commit once again. The checks are configured in .pre-commit-config.yaml under the repo root.
The pre-commit hooks use black, isort and flake8. However we also added linting support for Ruff,
which can be found and customized in pyproject.toml. This is quite helpful during development.
It is also a good idea to have a type-checker or LSP available (such as Pylance, pyright or ty). Since FINN is a complex multi-repo project, it is helpful to have type annotations available everywhere. We strongly encourage you to annotate your own added code with type annotations as well.
To have a PR merged, it is required that everything is documented using docstrings. Use existing files as a guideline on how the docstrings should look like. (A notable exception for this is src/finn/builder/build_dataflow_config.py, which uses a different documentation style.)
To ensure good formatting, Ruff is quite helpful here as well.
For custom transformations and steps it is common to also describe what the requisites for this transformation are, and what the expected result will be. This can help prevent bugs where transformation B requires transformation A to be run first, but A was not executed before B. (It is however good practice to also check this in code, as far as possible.)
To enable debug mode for the FINN+ command line interface:
FINN_DEBUG=1 finn <your-command>This activates some basic debugging information during FINN+'s CLI initialization stage.
Feel free, when developing your own CLI extension, to include debugging statements checked by
FINN_DEBUG as well (the finn.interface subpackage contains a flag DEBUG that you can check).
When building custom steps and transformations:
- Use the debug environment variable for basic output
- For more detailed logging, enable the
verboseflag in theDataflowBuildConfig - Make use of the logging system. At any point simply run
from finn.util.logging import log- As a rough guideline use
log.debugfor information the user will never see -
log.infofor detailed information about what FINN is currently doing -
log.warningfor warnings that the user should always see (this can be anything from simply informing the user about a certain decision that was made by the compiler, to important changes that the user might want to object to) -
log.errorfor when something goes wrong within the context of the compilation (no solution found for something, incorrect graph, etc.) -
log.fatalfor unexpected or external errors, or when something goes completely wrong (Something could not be parsed, a dependency is missing, etc.)
- As a rough guideline use
Note that these are only guidelines. Feel free to adjust them to your situation.
Furthermore, proper error handling should be done as well. As a developer, if you can expect an error somewhere, you should always catch it, and re-emit a fitting subclass of FINNError (a complete list of FINN errors can be found in src/finn/util/exception.py).
If your error is not expected to appear during normal usage, use a subclass of FINNInternalError. This prints out a formatted stack trace and some of the surrounding lines of code for debugging.
If the error is expected to appear during normal usage, use a subclass of FINNUserError instead. These are errors that directly concern the user and might point to errors in the model or the configuration and can be fixed without knowledge of FINNs internals. These errors will, when emitted, only produce a one-line summary of the error and exit FINN, instead of printing a whole stack trace.
Important
When possible, avoid using assert. Replace assert with a check and the matching FINNError type. For example replace:
assert len(model.graph.node) != 0with
if len(model.graph.node) == 0:
raise FINNUserError("The model does not contain any nodes!")This should provide a short and hopefully helpful overview of things to think about before finalizing a PR:
- Is the new code integrated properly into the codebase? (Reuse of existing files, functions, types, etc.)
- Is the new functionality documented? (For Transformations this would be the class docstring for example)
- Is the code itself documented?
- Classes
- Methods
- Functions
- Modules
- (Variables, if complex)
- Is the code type-hinted? (Function/Method signatures, variables if necessary)
- Is the code correctly linted / formatted? (pre-commit will take care of formatting otherwise)
- Is the new functionality tested (both unit-tested and end-to-end)?
- Do all tests pass?
- Do all existing tests pass?
- (If the new code requires configuration in
build_dataflow_config.py)- Is the new configuration variable documented similar to the others?
- Did you remove all open TODOs from your code?
Create an .env file with the following variables:
FINN_ROOT # Your repository path
FINN_BUILD_DIR # Temporary files location (e.g., /tmp/FINN_TEST_BUILD_DIR)
FINN_DEPS # Dependency location
NUM_DEFAULT_WORKERS # Number of parallel transformation workers
VIVADO_PATH # Path to Vivado (e.g., .../Vivado/2023.1)
VITIS_PATH # Path to Vitis
HLS_PATH # Path to Vitis HLSTip
See the Settings wiki page for detailed information about these variables.
Important
Run finn deps update at least once manually before testing, as the test setup doesn't automatically fetch dependencies.
Important
FINN_BUILD_DIR determines where test-generated files are placed. Consider the resolution order.
Configure VS Code's debugger by adding this to .vscode/launch.json:
{
"name": "Python Debugger: Run FINN",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/src/finn/interface/run_finn.py",
"console": "integratedTerminal",
"args": "${command:pickArgs}",
"subProcess": true
}Tests are vital to keep FINN+ running. All the FINN+ tests can be found in the test folder. These tests can be roughly grouped into three categories:
- Unit tests: targeting unit functionality, e.g. a single transformation. Example: tests/transformation/streamline/test_sign_to_thres.py tests the expected behavior of the ConvertSignToThres transformation pass.
- Small-scale integration tests: targeting a group of related classes or functions that to test how they behave together. Example: tests/fpgadataflow/test_convert_to_hls_conv_layer.py sets up variants of ONNX Conv nodes that are first lowered and then converted to FINN HLS layers.
- End-to-end tests: testing a typical βend-to-endβ compilation flow in FINN, where one end is a trained QNN and the other end is a hardware implementation. These tests can be quite large and are typically broken into several steps that depend on prior ones. Examples: tests/end2end
Additionally, qonnx, brevitas and finn-hlslib also include their own test suites.
Configure VS Code to use pytest by adding this to .vscode/settings.json:
"python.testing.pytestArgs": [
"tests",
"src/finn/transformation/fpgadataflow",
"-n",
"10",
"--doctest-modules"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,Important
Settings in .vscode/settings.json override your pyproject.toml testing settings. Update both when changing test configurations.
Control test execution with these environment variables:
-
FINN_TESTS_ISOLATE_BUILD_DIRS(default: 1)- When set to 1, creates a separate directory for each test
- Example:
/tmp/FINN_TESTS/test_operator_a,/tmp/FINN_TESTS/test_operator_b, etc. - During the test,
FINN_BUILD_DIRpoints to the test-specific directory
-
FINN_TESTS_CLEANUP_BUILD_DIRS(default: 0)- When set to 1, removes the test build directory after execution
- Requires
FINN_TESTS_ISOLATE_BUILD_DIRS=1 - Set to 0 by default to allow post-test debugging
π Home
- Migration Guide
- Building an Accelerator
- DataflowBuildConfig Documentation
- Example Models
- Build Guides:
- Brevitas - Quantization library
- FINN+ Repository
- Custom Steps Library