Compare commits

...

58 Commits

Author SHA1 Message Date
5d8d995a04 chore(changelog): update changelog for v0.6.1
All checks were successful
Build and Publish nightly package / build-and-publish (release) Successful in 2m11s
2025-07-16 09:50:07 +00:00
36f6e7314a chore(version): bump to 0.6.1
All checks were successful
Auto Changelog & (Release) / release (push) Successful in 1m8s
Build and Publish nightly package / build-and-publish (push) Successful in 2m24s
2025-07-16 11:48:54 +02:00
bc8ae46b4a chore(changelog): update unreleased changelog 2025-07-16 09:47:41 +00:00
dde2363ad7 chore(config): add git-cliff configuration file
All checks were successful
Auto Changelog & (Release) / release (push) Successful in 1m15s
Build and Publish nightly package / build-and-publish (push) Successful in 2m35s
2025-07-16 11:46:06 +02:00
32e3b51a77 chore(version): add version file for tracking 2025-07-16 11:41:54 +02:00
da387f2ee6 feat(ci): add workflows for nightly builds and releases
- Introduce separate workflows for nightly and release builds
- Add scripts for version management, asset uploads, and cleanup
- Improve Python environment setup and dependency caching
2025-07-16 11:41:35 +02:00
08679c2680 style(pyproject): simplify include array formatting 2025-07-16 11:41:18 +02:00
7b6f9ef224 chore: Updates project version to 0.6.0
All checks were successful
Build and Publish / build-and-publish (push) Successful in 1m55s
- Updates the project version in pyproject.toml
- Reflects changes for a new release
2025-07-16 11:30:18 +02:00
6ca389d5cb refactor: use typer for CLI argument parsing
- Replaces `argparse` with `typer` for command-line argument
  parsing to improve CLI interface and maintainability.
- Converts commands to subcommands using `typer.Typer()`.
- Streamlines CLI structure and enhances user experience.
2025-07-16 11:29:31 +02:00
1d7bc19965 style: Updates VS Code editor color scheme
- Updates the VS Code settings to customize the color scheme for
  the activity bar and activity bar badge.
- Enhances the visual appearance and user experience of the editor.
2025-07-16 11:04:58 +02:00
175bf4882a refactor: improves project configuration
- Removes unnecessary fields and configures the project.
- Streamlines tool options for clarity.
- Simplifies dependencies.
2025-07-16 11:04:44 +02:00
0d26c42f8a chore: remove build-deb.yml workflow file
Signed-off-by: Max P. <Mail@MPassarello.de>
2025-05-01 17:44:36 +02:00
fa7e738b7e docs(readme): expand README with detailed usage and setup
- Add comprehensive overview of HDLBuild features and functionality
- Include installation prerequisites and step-by-step instructions
- Document CLI commands with examples for common tasks
- Provide example `project.yml` configuration for customization
- Add sections on contributing, license, and GitHub Actions
- Enhance clarity and usability for new users and contributors

Signed-off-by: Max P. <Mail@MPassarello.de>
2025-05-01 17:43:12 +02:00
60c4ca19b1 Remove push trigger for main branch in build-deb.yml 2025-04-28 10:37:37 +02:00
38d4c7d5f3 Add twine to development dependencies in pyproject.toml
Some checks failed
Build Wheels / build (push) Has been cancelled
Build and Publish / build-and-publish (push) Successful in 30s
2025-04-28 10:36:30 +02:00
9d33b6e8be Update workflow to use Ubuntu 22.04 for build and publish job 2025-04-28 10:35:13 +02:00
dde3b5dc5d Add GitHub Actions workflow for building and publishing to Gitea 2025-04-28 10:34:33 +02:00
6d6b731033 Bump version from 0.5.2 to 0.5.3 in pyproject.toml
All checks were successful
Build Wheels / build (push) Successful in 3m36s
2025-04-27 18:56:40 +00:00
8196eb3929 Update gitignore to include .locale and vhdl_ls.toml 2025-04-27 18:56:34 +00:00
5083852c29 Bump version from 0.5.1 to 0.5.2 in pyproject.toml
All checks were successful
Build Wheels / build (push) Successful in 4m8s
2025-04-27 18:52:00 +00:00
215e4aa545 Refactors project initialization logic
Simplifies file handling by consolidating template copying logic
into a single loop. Corrects the resolution of the templates
directory path for improved reliability.

Improves maintainability and reduces redundancy in code.
2025-04-27 18:51:46 +00:00
d78bfcc408 Bump version from 0.5.0 to 0.5.1 in pyproject.toml
All checks were successful
Build Wheels / build (push) Successful in 3m38s
2025-04-27 18:40:23 +00:00
8ed550f451 Defers project loading to execution phase
Removes project loading from command initialization and shifts it to the execution phase for better resource management and initialization performance.

Improves flexibility by ensuring the project is only loaded when needed.
2025-04-27 18:39:58 +00:00
37b684fd2d Bump version from 0.4.0 to 0.5.0 in pyproject.toml
All checks were successful
Build Wheels / build (push) Successful in 4m33s
2025-04-27 18:03:57 +00:00
7cf69aa16f Adds project initialization command
All checks were successful
Build Wheels / build (push) Successful in 4m44s
Introduces an `init` command to initialize new HDLBuild projects.
Includes creation of default `.gitignore` and `project.yml` files
from templates if they do not already exist. Updates command
registration to include the new `init` command.
2025-04-27 17:58:11 +00:00
9274461d7a Updates package inclusion and dependency list
Adds template files to the package inclusion list to ensure they are distributed with the build.

Removes an unused dependency to streamline the dependency list.
2025-04-27 17:57:52 +00:00
b79b7559f2 Add template files for gitignore and project configuration 2025-04-27 17:57:40 +00:00
22266f2947 Bump version from 0.3.0 to 0.4.0 in pyproject.toml
All checks were successful
Build Wheels / build (push) Successful in 4m20s
2025-04-27 14:17:01 +00:00
c7b2970949 Modularizes CLI commands for maintainability
Refactors CLI command handlers into separate classes under a new `commands` module for improved organization and scalability.

Introduces a `register_commands` function to centralize command registration. Adds version retrieval for better tool identification in CLI descriptions.
2025-04-27 14:16:54 +00:00
6271eb0998 Refines build configuration and updates file paths
Updates source and testbench file paths for better organization
Enhances Xilinx tool options with detailed customization
Adds and documents advanced synthesis and optimization settings

Improves maintainability and aligns with modern design practices
2025-04-27 14:00:15 +00:00
308de700a2 Bump version from 0.2.0 to 0.3.0 in pyproject.toml
All checks were successful
Build Wheels / build (push) Successful in 4m1s
2025-04-27 12:02:20 +00:00
f1315dc458 Refactors testbench build and execution logic
Updates `build_testbench` to use a shared tool execution utility, improving maintainability and consistency.
Refactors `run_testbench` to accept project configurations, enabling better customization and handling of tool options.

Enhances modularity and reduces redundancy in the simulation workflow.
2025-04-27 12:02:05 +00:00
ba8cc2ef94 Adds ISim support to tool options
Extends tool configuration to include ISim options by adding a new field to the model and example configuration file. Supports specifying ISim arguments for enhanced simulation workflows.
2025-04-27 12:01:38 +00:00
f87b08441f Refactor VGA project configuration and update source paths 2025-04-27 11:46:05 +00:00
52f5a8fe24 Change build environment from ubuntu-20.04 to ubuntu-latest 2025-04-27 11:43:10 +00:00
836a5bf643 Change build environment from xilinx-ise to ubuntu-20.04 2025-04-27 11:41:40 +00:00
ffd8d6eac9 Change build environment from ubuntu-latest to xilinx-ise 2025-04-27 11:38:31 +00:00
44f2f15dc5 Updates dependencies and downgrades version
All checks were successful
Build Wheels / build (push) Successful in 4m7s
Bumps `pyyaml` dependency to `^6.0.2` for compatibility and security improvements. Downgrades package version to `0.2.0`, potentially signaling a rollback or version correction.
2025-04-27 11:18:08 +00:00
d6d792c9e6 Revert pyyaml dependency specification
Some checks failed
Build Wheels / build (push) Failing after 36s
2025-04-27 11:16:44 +00:00
07922f8a54 Updates dependencies and bumps version to 0.2.1
Some checks failed
Build Wheels / build (push) Failing after 37s
Downgrades PyYAML to version 5.4.1 for compatibility reasons.
Updates project version from 0.2.0 to 0.2.1 to reflect dependency changes.
2025-04-27 11:11:23 +00:00
29d04a25f0 Use Poetry to install wheel and download dependencies in build-deb workflow 2025-04-27 10:57:19 +00:00
bbe5d7f2b1 Fix Python version in setup to 3.10 2025-04-27 10:44:07 +00:00
84800c25a5 Triggers build on main branch changes
Updates workflow to trigger on pushes to the main branch when pyproject.toml is modified, in addition to manual dispatch.

Improves automation by ensuring builds occur for relevant changes.
2025-04-27 10:32:25 +00:00
e58c0897ce Fix repository URL construction in build-deb workflow to use secrets for user authentication 2025-04-27 10:25:48 +00:00
277a16e126 Enhance build-deb workflow to clone packages branch and commit DEB files 2025-04-27 10:20:04 +00:00
ad4ba6e412 Fix wheel2deb installation step by adding missing dependencies 2025-04-27 10:01:35 +00:00
2f3e0d375f Enhance wheel2deb command to include verbose output during DEB conversion 2025-04-27 09:58:21 +00:00
528907fabe Fix pip download destination and update wheel2deb command for improved output 2025-04-27 09:55:29 +00:00
fae19340cc Fix wheel2deb command to use output option for DEB conversion 2025-04-27 09:53:00 +00:00
a57062a3f8 Fix wheel2deb command to specify default option for conversion 2025-04-27 09:47:47 +00:00
cef035329a Adds poetry-plugin-export to Poetry installation step 2025-04-27 09:43:24 +00:00
93e67c0380 Adds testbench support and refines file handling
Introduces a test process with commands to build and run testbenches. Adds utilities for generating simulation project files and resolving testbench sources. Updates file handling logic to support configurable destination directories.

Improves organizational structure of CLI commands and enhances flexibility in managing project artifacts.
2025-04-27 09:43:17 +00:00
8caafe862e Bumps version to 0.2.0 and removes unused plugin requirement from pyproject.toml 2025-04-27 09:43:06 +00:00
cfa62a5624 Adds vhdltests/ and .devcontainer/ to .gitignore 2025-04-27 09:42:53 +00:00
263c3fac5e Reorders steps in GitHub Actions workflow to ensure environment variables are prepared before checking out the repository 2025-04-26 22:01:30 +02:00
961d182bf7 Refines GitHub Actions workflow for building wheels, improving comments and organization 2025-04-26 21:25:41 +02:00
5263a31c07 Merge pull request 'Adds GitHub Actions workflow for building wheels and updates pyproject.toml for plugin requirements' (#1) from deb into main
Reviewed-on: #1
2025-04-26 21:19:19 +02:00
fb5c3127b5 Adds GitHub Actions workflow for building wheels and updates pyproject.toml for plugin requirements 2025-04-26 21:17:51 +02:00
30 changed files with 1496 additions and 108 deletions

View File

@@ -0,0 +1,5 @@
chore(pr): ${PullRequestTitle} ${PullRequestReference}
${PullRequestDescription}
Merged from ${HeadBranch} into ${BaseBranch}

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env bash
set -euo pipefail
# cleanup_dev_versions.sh - Delete old PyPI dev versions from Gitea package registry
# Required environment variables
USERNAME="${TWINE_USERNAME}"
TOKEN="${TWINE_PASSWORD}"
REPO="${GITHUB_REPOSITORY}" # e.g., maxp/repocat
API_BASE="${GITHUB_API_URL%/}" # Strip trailing slash if present
OWNER="${REPO%%/*}"
PACKAGE_NAME="${REPO##*/}"
API_URL="${API_BASE}/packages/${OWNER}/pypi/${PACKAGE_NAME}"
# Fetch the list of versions
response=$(curl -s -u "$USERNAME:$TOKEN" "$API_URL")
# Extract all .dev versions, sort by creation time
mapfile -t versions_to_delete < <(echo "$response" | jq -r '
map(select(.version | test("\\.dev"))) |
sort_by(.created_at) |
.[0:-1][] |
.version')
# Determine latest version to keep
latest_version=$(echo "$response" | jq -r '
map(select(.version | test("\\.dev"))) |
sort_by(.created_at) |
last.version')
if [[ -z "$latest_version" || ${#versions_to_delete[@]} -eq 0 ]]; then
echo "No old .dev versions to delete."
exit 0
fi
echo "Keeping latest .dev version: $latest_version"
# Delete old .dev versions
for version in "${versions_to_delete[@]}"; do
echo "Deleting old .dev version: $version"
curl -s -X DELETE -u "$USERNAME:$TOKEN" "$API_URL/$version"
done
echo "Cleanup complete."

View File

@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail
# Eingaben
TAG="$1"
TOKEN="${ACTIONS_RUNTIME_TOKEN:-<fallback_token>}"
REPO="${GITHUB_REPOSITORY:-owner/example}"
API="${GITHUB_API_URL:-https://gitea.example.tld/api/v1}"
OWNER=$(echo "$REPO" | cut -d/ -f1)
NAME=$(echo "$REPO" | cut -d/ -f2)
RESPONSE=$(curl -sf \
-H "Authorization: token $TOKEN" \
"$API/repos/$OWNER/$NAME/releases/tags/$TAG")
RELEASE_ID=$(echo "$RESPONSE" | jq -r '.id')
echo "Release-ID für $TAG ist: $RELEASE_ID"
# Für GitHub Actions als Umgebungsvariable
echo "GT_RELEASE_ID=$RELEASE_ID" >> "$GITHUB_ENV"

View File

@@ -0,0 +1,14 @@
#!/bin/bash
BASE_VERSION=$(cat VERSION)
NIGHTLY_SUFFIX=""
if [[ "$1" == "nightly" ]]; then
# Beispiel: 20240511.1358 → 11. Mai, 13:58 Uhr
NIGHTLY_SUFFIX=".dev$(date +%Y%m%d%H%M)"
fi
FULL_VERSION="${BASE_VERSION}${NIGHTLY_SUFFIX}"
echo "Using version: $FULL_VERSION"
poetry version "$FULL_VERSION"

View File

@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail
# Stelle sicher, dass wir im Projektverzeichnis sind
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$ROOT_DIR"
PYPROJECT="pyproject.toml"
VERSION_FILE="VERSION"
# Extrahiere die Version mit grep + sed (keine externen Abhängigkeiten nötig)
VERSION=$(grep -E '^version\s*=' "$PYPROJECT" | head -n1 | sed -E 's/.*=\s*"([^"]+)".*/\1/')
if [[ -z "$VERSION" ]]; then
echo "❌ Version konnte nicht aus $PYPROJECT gelesen werden."
exit 1
fi
printf "%s" "$VERSION" > "$VERSION_FILE"
echo "✅ Version synchronisiert: $VERSION$VERSION_FILE"

40
.gitea/scripts/upload-asset.sh Executable file
View File

@@ -0,0 +1,40 @@
#!/usr/bin/env bash
set -euo pipefail
# Eingabeparameter
FILE_PATH="$1" # z. B. ./dist/build.zip
CUSTOM_NAME="${2:-}" # optional: anderer Name unter dem das Asset gespeichert werden soll
RELEASE_ID="${GT_RELEASE_ID:-}" # aus Umgebung
# Validierung
if [[ -z "$RELEASE_ID" ]]; then
echo "❌ RELEASE_ID ist nicht gesetzt. Abbruch."
exit 1
fi
if [[ ! -f "$FILE_PATH" ]]; then
echo "❌ Datei '$FILE_PATH' existiert nicht. Abbruch."
exit 1
fi
# Default-Konfiguration
TOKEN="${ACTIONS_RUNTIME_TOKEN:-<fallback_token>}"
REPO="${GITHUB_REPOSITORY:-owner/example}"
API="${GITHUB_API_URL:-https://gitea.example.tld/api/v1}"
# Owner/Repo auflösen
OWNER=$(echo "$REPO" | cut -d/ -f1)
NAME=$(echo "$REPO" | cut -d/ -f2)
# Dateiname setzen
FILENAME="${CUSTOM_NAME:-$(basename "$FILE_PATH")}"
echo "🔼 Uploading '$FILE_PATH' as '$FILENAME' to release ID $RELEASE_ID"
# Upload
curl -sf -X POST \
-H "Authorization: token $TOKEN" \
-F "attachment=@$FILE_PATH" \
"$API/repos/$OWNER/$NAME/releases/$RELEASE_ID/assets?name=$FILENAME"
echo "✅ Upload abgeschlossen: $FILENAME"

View File

@@ -0,0 +1,62 @@
name: Build and Publish nightly package
on:
workflow_dispatch:
push:
branches:
- main
paths-ignore:
- 'CHANGELOG.md'
jobs:
build-and-publish:
runs-on: ubuntu-22.04
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: 🐍 Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: 🔄 Restore cache
uses: https://git.0xmax42.io/actions/cache@v1
with:
key: poetry-v1-${{ runner.os }}-${{ hashFiles('poetry.lock') }}
paths: |
~/.cache/pypoetry
~/.cache/pip
- name: Install Poetry
run: |
pip install poetry
- name: Install Project Dependencies
working-directory: .
run: |
poetry install
- name: Set version from VERSION file (with nightly suffix)
run: ./.gitea/scripts/set_poetry_version.sh nightly
- name: Build Package
working-directory: .
run: |
poetry build
- name: Publish to Gitea Package Registry
working-directory: .
env:
TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }}
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
run: |
poetry run twine upload --repository-url ${{ secrets.TWINE_URL }} dist/*
- name: Cleanup old dev versions
run: |
.gitea/scripts/cleanup_versions.sh '\.dev'
env:
TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }}
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}

View File

@@ -0,0 +1,64 @@
name: Build and Publish nightly package
on:
release:
types: [published]
jobs:
build-and-publish:
runs-on: ubuntu-22.04
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ github.event.release.tag_name }}
- name: 🐍 Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: 🔄 Restore cache
uses: https://git.0xmax42.io/actions/cache@v1
with:
key: poetry-v1-${{ runner.os }}-${{ hashFiles('poetry.lock') }}
paths: |
~/.cache/pypoetry
~/.cache/pip
- name: Install Poetry
run: |
pip install poetry
- name: Install Project Dependencies
working-directory: .
run: |
poetry install
- name: Build Package
working-directory: .
run: |
poetry build
- name: Get built wheel filename
id: get_whl
run: |
echo "whl_file=$(basename dist/*.whl)" >> $GITHUB_OUTPUT
echo "sdist_file=$(basename dist/*.tar.gz)" >> $GITHUB_OUTPUT
- name: Publish to Gitea Package Registry
working-directory: .
env:
TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }}
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
run: |
poetry run twine upload --repository-url ${{ secrets.TWINE_URL }} dist/*
- name: Get Release ID from tag
run: .gitea/scripts/get-release-id.sh "${{ github.event.release.tag_name }}"
- name: Upload assets
run: |
.gitea/scripts/upload-asset.sh ./dist/${{ steps.get_whl.outputs.whl_file }}
.gitea/scripts/upload-asset.sh ./dist/${{ steps.get_whl.outputs.sdist_file }}

View File

@@ -0,0 +1,18 @@
name: Auto Changelog & (Release)
on:
push:
branches:
- main
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Release
uses: https://git.0xmax42.io/actions/auto-changelog-release-action@v0
with:
token: ${{ secrets.RELEASE_PUBLISH_TOKEN }}

3
.gitignore vendored
View File

@@ -184,4 +184,5 @@ vhdl/
poetry.lock
project.yml
.project/
.devcontainer/
.devcontainer/
vhdltests/

11
.vscode/settings.json vendored
View File

@@ -2,5 +2,14 @@
"python.envFile": "${workspaceFolder}/.env",
"python.analysis.extraPaths": [
"src"
]
],
"workbench.colorCustomizations": {
"activityBar.activeBackground": "#8dc4ff",
"activityBar.background": "#8dc4ff",
"activityBar.foreground": "#15202b",
"activityBar.inactiveForeground": "#15202b99",
"activityBarBadge.background": "#ff007b",
"activityBarBadge.foreground": "#e7e7e7"
},
"peacock.color": "#5aaaff"
}

31
CHANGELOG.md Normal file
View File

@@ -0,0 +1,31 @@
# Changelog
All notable changes to this project will be documented in this file.
## [0.6.1] - 2025-07-16
### 🚀 Features
- *(ci)* Add workflows for nightly builds and releases - ([da387f2](https://git.0xmax42.io/maxp/hdlbuild/commit/da387f2ee602390d616c79bf4057ccf941e21462))
### 🚜 Refactor
- Use typer for CLI argument parsing - ([6ca389d](https://git.0xmax42.io/maxp/hdlbuild/commit/6ca389d5cbbeff53faab9d61376a8c77ed097b6c))
- Improves project configuration - ([175bf48](https://git.0xmax42.io/maxp/hdlbuild/commit/175bf4882a8f172ee536d726b31136690572be36))
### 📚 Documentation
- *(readme)* Expand README with detailed usage and setup - ([fa7e738](https://git.0xmax42.io/maxp/hdlbuild/commit/fa7e738b7eade5a627218741a6fb4bd1617f7801))
### 🎨 Styling
- *(pyproject)* Simplify include array formatting - ([08679c2](https://git.0xmax42.io/maxp/hdlbuild/commit/08679c2680b49119e0414688a80e8dc2659236b4))
- Updates VS Code editor color scheme - ([1d7bc19](https://git.0xmax42.io/maxp/hdlbuild/commit/1d7bc1996522ab54970348b5118ad319849a6a1f))
### ⚙️ Miscellaneous Tasks
- *(config)* Add git-cliff configuration file - ([dde2363](https://git.0xmax42.io/maxp/hdlbuild/commit/dde2363ad7dd2fd2d48c6154e3b88c1c4a6867fd))
- Updates project version to 0.6.0 - ([7b6f9ef](https://git.0xmax42.io/maxp/hdlbuild/commit/7b6f9ef2240864b103903e79c895a76db59c14fa))
- Remove build-deb.yml workflow file - ([0d26c42](https://git.0xmax42.io/maxp/hdlbuild/commit/0d26c42f8ae419d509aa47d3f7a23bfdd08cf79b))

168
README.md
View File

@@ -1,2 +1,168 @@
# hdlbuild
# HDLBuild
HDLBuild is a flexible build management tool for FPGA projects. It simplifies the process of managing dependencies, building, testing, and deploying FPGA designs using Xilinx ISE tools.
## Features
- **Dependency Management**: Automatically resolves and manages project dependencies from Git repositories.
- **Build Automation**: Supports synthesis, implementation, and bitstream generation for FPGA designs.
- **Testbench Execution**: Automates the process of building and running testbenches.
- **Customizable Tool Options**: Provides extensive configuration options for Xilinx ISE tools.
- **Project Initialization**: Quickly set up new projects with predefined templates.
- **Rich Console Output**: Provides detailed and interactive console feedback using `rich`.
---
## Installation
### Prerequisites
- Python 3.10 or higher
- Poetry (for dependency management)
- Xilinx ISE (14.7) installed and configured
### Steps
1. Clone the repository:
```bash
git clone https://github.com/your-repo/hdlbuild.git
cd hdlbuild
```
2. Install dependencies:
```bash
poetry install
```
3. Add the `hdlbuild` CLI to your PATH:
```bash
poetry shell
```
---
## Usage
### CLI Commands
HDLBuild provides a command-line interface (CLI) for managing FPGA projects. Below are the available commands:
#### 1. **Initialize a New Project**
```bash
hdlbuild init
```
- Creates a new project with a project.yml configuration file and a .gitignore file.
#### 2. **Resolve Dependencies**
```bash
hdlbuild dep
```
- Clones and resolves all project dependencies defined in project.yml.
#### 3. **Build the Project**
```bash
hdlbuild build
```
- Runs the full build process, including synthesis, implementation, and bitstream generation.
- To only synthesize the design:
```bash
hdlbuild build synth
```
#### 4. **Run Testbenches**
```bash
hdlbuild test <testbench_name>
```
- Builds and runs the specified testbench.
#### 5. **Clean Build Artifacts**
```bash
hdlbuild clean
```
- Removes build artifacts.
- To clean all generated files:
```bash
hdlbuild clean all
```
---
## Configuration
The project is configured using a project.yml file. Below is an example configuration:
```yml
name: MyFPGAProject
topmodule: top_module
target_device: xc3s1200e-4-fg320
xilinx_path: /opt/Xilinx/14.7/ISE_DS/ISE
constraints: constraints.ucf
sources:
vhdl:
- path: src/*.vhd
library: work
testbenches:
vhdl:
- path: tests/*.vhd
library: work
dependencies:
- git: "https://github.com/example/dependency.git"
rev: "main"
build:
build_dir: working
report_dir: reports
copy_target_dir: output
tool_options:
xst:
- "-opt_mode Speed"
- "-opt_level 2"
map:
- "-detail"
- "-timing"
par: []
bitgen:
- "-g StartupClk:JtagClk"
```
---
## Development
### Building the Package
To build the Python package:
```bash
poetry build
```
---
## GitHub Actions
The project includes GitHub workflows for building and deploying the package:
1. **Build and Publish**: build-and-deploy.yml
---
## License
This project is licensed under the MIT License.
---
## Contributing
Contributions are welcome! Please follow these steps:
1. Fork the repository.
2. Create a new branch for your feature or bugfix.
3. Submit a pull request.

1
VERSION Normal file
View File

@@ -0,0 +1 @@
0.6.1

104
cliff.toml Normal file
View File

@@ -0,0 +1,104 @@
# CLIFF_VERSION=2.8.0
# git-cliff ~ default configuration file
# https://git-cliff.org/docs/configuration
#
# Lines starting with "#" are comments.
# Configuration options are organized into tables and keys.
# See documentation for more information on available options.
[remote.gitea]
owner = "maxp"
repo = "hdlbuild"
[changelog]
# postprocessors
postprocessors = [
{ pattern = '<GITEA_URL>', replace = "https://git.0xmax42.io" }, # replace gitea url
]
# template for the changelog header
header = """
# Changelog\n
All notable changes to this project will be documented in this file.\n
"""
# template for the changelog body
# https://keats.github.io/tera/docs/#introduction
body = """
{%- macro remote_url() -%}
<GITEA_URL>/{{ remote.gitea.owner }}/{{ remote.gitea.repo }}
{%- endmacro -%}
{% if version %}\
{% if previous.version %}\
## [{{ version | trim_start_matches(pat="v") }}]\
({{ self::remote_url() }}/compare/{{ previous.version }}..{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% endif %}\
{% else %}\
## [unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | striptags | trim | upper_first }}
{% for commit in commits %}
- {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
{% if commit.breaking %}[**breaking**] {% endif %}\
{{ commit.message | upper_first }} - \
([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\
{% endfor %}
{% endfor %}\n
"""
# template for the changelog footer
footer = """
"""
# remove the leading and trailing s
trim = true
# render body even when there are no releases to process
# render_always = true
# output file path
# output = "test.md"
[git]
# parse the commits based on https://www.conventionalcommits.org
conventional_commits = true
# filter out the commits that are not conventional
filter_unconventional = true
# process each line of a commit as an individual commit
split_commits = false
# regex for preprocessing the commit messages
commit_preprocessors = [
# Replace issue numbers
#{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](<REPO>/issues/${2}))"},
# Check spelling of the commit with https://github.com/crate-ci/typos
# If the spelling is incorrect, it will be automatically fixed.
#{ pattern = '.*', replace_command = 'typos --write-changes -' },
]
# regex for parsing and grouping commits
commit_parsers = [
{ message = "^feat", group = "<!-- 0 -->🚀 Features" },
{ message = "^fix", group = "<!-- 1 -->🐛 Bug Fixes" },
{ message = "^doc", group = "<!-- 3 -->📚 Documentation" },
{ message = "^perf", group = "<!-- 4 -->⚡ Performance" },
{ message = "^refactor", group = "<!-- 2 -->🚜 Refactor" },
{ message = "^style", group = "<!-- 5 -->🎨 Styling" },
{ message = "^test", group = "<!-- 6 -->🧪 Testing" },
{ message = "^chore\\(changelog\\)", skip = true },
{ message = "^chore\\(version\\)", skip = true },
{ message = "^chore\\(release\\): prepare for", skip = true },
{ message = "^chore\\(deps.*\\)", skip = true },
{ message = "^chore\\(pr\\)", skip = true },
{ message = "^chore\\(pull\\)", skip = true },
{ message = "^chore|^ci", group = "<!-- 7 -->⚙️ Miscellaneous Tasks" },
{ body = ".*security", group = "<!-- 8 -->🛡️ Security" },
{ message = "^revert", group = "<!-- 9 -->◀️ Revert" },
{ message = ".*", group = "<!-- 10 -->💼 Other" },
]
# Regex to select git tags that represent releases.
tag_pattern = "v[0-9]+\\.[0-9]+\\.[0-9]+"
# filter out the commits that are not matched by commit parsers
filter_commits = false
# sort the tags topologically
topo_order = false
# sort the commits inside sections by oldest/newest order
sort_commits = "newest"

View File

@@ -1,41 +1,268 @@
name: VGA_Test
topmodule: VGA_Test_Top
name:
topmodule:
target_device: xc3s1200e-4-fg320
xilinx_path: /opt/Xilinx/14.7/ISE_DS/ISE
constraints:
sources:
vhdl:
- path: src/vga/*.vhd
- path: src/*.vhd
library: work
- path: src/common/*.vhd
library: work
- path: src/VGA_test_top.vhd
library: work
verilog:
- path: src/old_modules/*.v
library: work
dependencies:
- name: AsyncFIFO
git: "https://github.com/0xMax32/Asynchronous-FIFO-AXI-Handshake.git"
rev: "main"
library: asyncfifo
- name: GrayCounter
git: "https://github.com/0xMax32/Gray-Counter.git"
rev: "v1.0.0"
library: graycounter
testbenches:
vhdl:
- path: src/tests/*.vhd
- path: tests/*.vhd
library: work
verilog: []
constraints: constraints/VGA_Test.ucf
dependencies:
# - git: "https://git.0xmax42.io/maxp/Asynchronous-FIFO-AXI-Handshake.git"
# rev: "hdlbuild"
build:
build_dir: working
report_dir: reports
copy_target_dir: output
# Tool Optionen
tool_options:
common:
- "-intstyle"
- "xflow"
ngdbuild: []
map:
- "-detail"
- "-timing"
- "-ol"
- "high"
par: []
bitgen:
- "-g"
- "StartupClk:JtagClk"
trace:
- "-v"
- "3"
- "-n"
- "3"
fuse:
- "-incremental"
isim:
- "-gui"
xst:
# Optimization goal: prioritize speed or area.
# Values: Speed | Area
- "-opt_mode Speed"
# Optimization level: more aggressive optimizations at level 2.
# Values: 1 | 2
- "-opt_level 2"
# Use the new XST parser (recommended for modern designs).
# Values: yes | no
- "-use_new_parser yes"
# Preserve design hierarchy or allow flattening for optimization.
# Values: Yes | No | Soft
- "-keep_hierarchy No"
# Determines how hierarchy is preserved in the netlist.
# Values: As_Optimized | Rebuilt
- "-netlist_hierarchy As_Optimized"
# Global optimization strategy for nets.
# Values: AllClockNets | Offset_In_Before | Offset_Out_After | Inpad_To_Outpad | Max_Delay
- "-glob_opt AllClockNets"
## Misc ##
# Enable reading of IP cores.
# Values: YES | NO
- "-read_cores YES"
# Do not write timing constraints into synthesis report.
# Values: YES | NO
- "-write_timing_constraints NO"
# Analyze paths across different clock domains.
# Values: YES | NO
- "-cross_clock_analysis NO"
# Character used to separate hierarchy levels in instance names.
# Default: /
- "-hierarchy_separator /"
# Delimiters used for bus signals.
# Values: <> | [] | () | {}
- "-bus_delimiter <>"
# Maintain original case of identifiers.
# Values: Maintain | Upper | Lower
- "-case Maintain"
# Target maximum utilization ratio for slices.
# Values: 1–100
- "-slice_utilization_ratio 100"
# Target maximum utilization ratio for BRAMs.
# Values: 1–100
- "-bram_utilization_ratio 100"
# Use Verilog 2001 syntax features.
# Values: YES | NO
- "-verilog2001 YES"
#### HDL Options ####
## FSM ##
# Extract FSMs (Finite State Machines) from HDL code.
# Values: YES | NO
- "-fsm_extract YES"
# Encoding strategy for FSMs.
# Values: Auto | Gray | One-Hot | Johnson | Compact | Sequential | Speed1 | User
- "-fsm_encoding Auto"
# Add safe logic for undefined FSM states.
# Values: Yes | No
- "-safe_implementation No"
# Structure used to implement FSMs.
# Values: LUT | BRAM
- "-fsm_style LUT"
## RAM/ROM ##
# Extract RAM inference from HDL.
# Values: Yes | No
- "-ram_extract Yes"
# Style used to implement RAM.
# Values: Auto | Block | Distributed
- "-ram_style Auto"
# Extract ROM inference from HDL.
# Values: Yes | No
- "-rom_extract Yes"
# Style used for implementing ROM.
# Values: Auto | Distributed | Block
- "-rom_style Auto"
# Enable or disable automatic BRAM packing.
# Values: YES | NO
- "-auto_bram_packing NO"
## MUX/Decoder/Shift Register ##
# Extract multiplexers where possible.
# Values: Yes | No | Force
- "-mux_extract Yes"
# Style used for implementing MUX logic.
# Values: Auto | MUXCY | MUXF
- "-mux_style Auto"
# Extract decoder logic from behavioral code.
# Values: YES | NO
- "-decoder_extract YES"
# Extract and optimize priority encoder structures.
# Values: Yes | No | Force
- "-priority_extract Yes"
# Extract shift register logic.
# Values: YES | NO
- "-shreg_extract YES"
# Extract simple shift operations into dedicated hardware.
# Values: YES | NO
- "-shift_extract YES"
## Multiplier ##
# Style for implementing multipliers.
# Values: Auto | LUT | Pipe_LUT | Pipe_Block | Block
- "-mult_style Auto"
## Misc ##
# Collapse XOR trees where beneficial.
# Values: YES | NO
- "-xor_collapse YES"
# Share resources like adders or multipliers between logic blocks.
# Values: YES | NO | Force
- "-resource_sharing YES"
# Convert asynchronous resets to synchronous where possible.
# Values: YES | NO
- "-async_to_sync NO"
#### Xilinx Specific Options ####
## Optimization ##
# Enable removal of logically equivalent registers.
# Values: YES | NO
- "-equivalent_register_removal YES"
# Duplicate registers to reduce fanout or improve timing.
# Values: YES | NO
- "-register_duplication YES"
# Move registers across logic to balance timing.
# Values: Yes | No | Forward | Backward
- "-register_balancing No"
# Use clock enable signals where possible.
# Values: Auto | Yes | No
- "-use_clock_enable Yes"
# Use synchronous set (preset) signals when available.
# Values: Auto | Yes | No
- "-use_sync_set Yes"
# Use synchronous reset signals where possible.
# Values: Auto | Yes | No
- "-use_sync_reset Yes"
## I/O ##
# Insert IO buffers for top-level ports.
# Values: YES | NO
- "-iobuf YES"
# Placement strategy for IOB registers (Auto = let tools decide).
# Values: Auto | YES | NO
- "-iob Auto"
## Misc ##
# Maximum allowed fanout for a net.
# Values: integer (e.g., 500)
- "-max_fanout 500"
# Maximum number of BUFGs (global buffers) to use.
# Values: 0–32 (device-dependent)
- "-bufg 24"
# Enable logic packing into slices.
# Values: YES | NO
- "-slice_packing YES"
# Try to reduce the number of primitive instances used.
# Values: YES | NO
- "-optimize_primitives NO"
# Margin in percent beyond the target slice utilization.
# Values: 0–100
- "-slice_utilization_ratio_maxmargin 5"

View File

@@ -1,13 +1,12 @@
[tool.poetry]
name = "hdlbuild"
version = "0.1.0"
version = "0.6.1"
description = "Flexible FPGA Build System"
authors = ["0xMax42 <Mail@0xMax42.io>"]
license = "MIT"
readme = "README.md"
packages = [
{ include = "hdlbuild", from = "src" }
]
packages = [{ include = "hdlbuild", from = "src" }]
include = ["src/hdlbuild/templates/*"]
[tool.poetry.scripts]
hdlbuild = "hdlbuild.cli:main"
@@ -15,11 +14,13 @@ hdlbuild = "hdlbuild.cli:main"
[tool.poetry.dependencies]
python = "^3.10"
pyyaml = "^6.0.2"
doit = "^0.36.0"
pydantic = "^2.11.3"
rich = "^14.0.0"
gitpython = "^3.1.44"
typer = "^0.16.0"
[tool.poetry.group.dev.dependencies]
twine = "^6.1.0"
[build-system]
requires = ["poetry-core"]

View File

@@ -1,81 +1,32 @@
import argparse
import sys
import typer
from importlib.metadata import version, PackageNotFoundError
from hdlbuild.dependencies.resolver import DependencyResolver
from hdlbuild.tools.xilinx_ise.main import xilinx_ise_all, xilinx_ise_synth
from hdlbuild.utils.console_utils import ConsoleUtils
from hdlbuild.utils.directory_manager import clear_build_directories, clear_directories, ensure_directories_exist
from hdlbuild.utils.project_loader import load_project_config
from hdlbuild.commands.build import cli as build_cli
from hdlbuild.commands.clean import cli as clean_cli
from hdlbuild.commands.dep import cli as dep_cli
from hdlbuild.commands.test import cli as test_cli
from hdlbuild.commands.init import cli as init_cli
project = load_project_config()
console_utils = ConsoleUtils("hdlbuild")
def get_version() -> str:
try:
return version("hdlbuild")
except PackageNotFoundError:
return "unknown"
def clear(args):
"""Clears the build artifacts."""
if args.target == "all":
console_utils.print("Starting clear all process...")
clear_directories()
console_utils.print("All cleared.")
else:
console_utils.print("Clearing build artifacts...")
clear_build_directories()
console_utils.print("Build artifacts cleared.")
def build(args):
"""Starts the build process."""
console_utils.print("Starting build process...")
ensure_directories_exist(True)
xilinx_ise_all(project)
def synth(args):
"""Starts the build process."""
console_utils.print("Starting build process...")
ensure_directories_exist()
xilinx_ise_synth(project)
def dep(args):
"""Starts the dependencies process."""
console_utils.print("Starting dependencies process...")
DependencyResolver(project).resolve_all()
app = typer.Typer(
rich_help_panel="ℹ️ HDLBuild – FPGA‑Build‑Tool",
help=f"hdlbuild v{get_version()} – Build‑Management for FPGA projects"
)
# Unter‑Kommandos registrieren (entspricht add_subparsers)
app.add_typer(build_cli, name="build", help="Build the project")
app.add_typer(clean_cli, name="clean", help="Clean build artifacts")
app.add_typer(dep_cli, name="dep", help="Resolve dependencies")
app.add_typer(test_cli, name="test", help="Run simulations/testbenches")
app.add_typer(init_cli, name="init", help="Initialize project")
def main():
parser = argparse.ArgumentParser(
description="hdlbuild - Build management tool for FPGA projects",
formatter_class=argparse.RawTextHelpFormatter
)
subparsers = parser.add_subparsers(
title="Commands",
description="Available commands",
dest="command",
required=True
)
# Clear command
parser_clear = subparsers.add_parser("clear", help="Clear build artifacts")
parser_clear.add_argument(
"target",
nargs="?",
choices=["all"],
help="Specify 'all' to clear everything (optional)"
)
parser_clear.set_defaults(func=clear)
# Build command
parser_build = subparsers.add_parser("build", help="Start the build process")
parser_build.set_defaults(func=build)
# Synth command
parser_build = subparsers.add_parser("synth", help="Start the synth process")
parser_build.set_defaults(func=synth)
# Dependencies command
parser_build = subparsers.add_parser("dep", help="Start the dependencies process")
parser_build.set_defaults(func=dep)
args = parser.parse_args()
args.func(args)
app()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,34 @@
import typer
from hdlbuild.tools.xilinx_ise.main import xilinx_ise_all, xilinx_ise_synth
from hdlbuild.utils.console_utils import ConsoleUtils
from hdlbuild.utils.directory_manager import ensure_directories_exist
from hdlbuild.utils.project_loader import load_project_config
cli = typer.Typer(rich_help_panel="🔨 Build Commands")
@cli.callback(invoke_without_command=True)
def build(
target: str = typer.Argument(
None,
help="Optional: 'synth' to run synthesis only",
show_default=False,
rich_help_panel="🔨 Build Commands",
)
) -> None:
"""
Run the full build flow or synthesis only.
* `hdlbuild build` → full flow
* `hdlbuild build synth` → synthesis only
"""
console = ConsoleUtils("hdlbuild")
project = load_project_config()
ensure_directories_exist(True)
if target == "synth":
console.print("Starting synthesis …")
xilinx_ise_synth(project)
else:
console.print("Starting full build …")
xilinx_ise_all(project)

View File

@@ -0,0 +1,35 @@
import typer
from hdlbuild.utils.console_utils import ConsoleUtils
from hdlbuild.utils.directory_manager import clear_build_directories, clear_directories
cli = typer.Typer(rich_help_panel="🧹 Clean Commands")
@cli.callback(invoke_without_command=True)
def clean(
target: str = typer.Argument(
None,
help="Optional: 'all' → wipe *all* artefacts, otherwise only the build directory",
show_default=False,
)
) -> None:
"""
Remove build artefacts (`build/*`) or *everything* (`all`).
Examples
--------
```bash
hdlbuild clean # build/* and temporary files only
hdlbuild clean all # also caches, logs, etc.
```
"""
console = ConsoleUtils("hdlbuild")
if target == "all":
console.print("Starting clean‑all …")
clear_directories()
console.print("All artefacts removed.")
else:
console.print("Removing build artefacts …")
clear_build_directories()
console.print("Build artefacts removed.")

View File

@@ -0,0 +1,23 @@
import typer
from hdlbuild.dependencies.resolver import DependencyResolver
from hdlbuild.utils.console_utils import ConsoleUtils
from hdlbuild.utils.project_loader import load_project_config
cli = typer.Typer(rich_help_panel="🔗 Dependency Commands")
@cli.callback(invoke_without_command=True)
def dep() -> None:
"""
Resolve all project dependencies.
```bash
hdlbuild dep
```
"""
console = ConsoleUtils("hdlbuild")
project = load_project_config()
console.print("Resolving dependencies …")
DependencyResolver(project).resolve_all()
console.print("Dependencies resolved.")

View File

@@ -0,0 +1,35 @@
import shutil
from pathlib import Path
import typer
from hdlbuild.utils.console_utils import ConsoleUtils
cli = typer.Typer(rich_help_panel="🆕 Init Commands")
@cli.callback(invoke_without_command=True)
def init() -> None:
"""
Initialise a new HDLBuild project in the current directory.
Copies `.gitignore` and `project.yml` from the template folder.
"""
console = ConsoleUtils("hdlbuild")
project_dir = Path.cwd()
script_dir = Path(__file__).parent.resolve()
template_dir = (script_dir / ".." / "templates").resolve()
files = [
("gitignore.template", ".gitignore"),
("project.yml.template", "project.yml"),
]
for template_name, target_name in files:
template_path = template_dir / template_name
target_path = project_dir / target_name
if not target_path.exists():
shutil.copy(template_path, target_path)
console.print(f"Created {target_name}")
else:
console.print(f"{target_name} already exists – skipping.")

View File

@@ -0,0 +1,31 @@
import typer
from hdlbuild.tools.xilinx_ise.isim import build_testbench, run_testbench
from hdlbuild.utils.console_utils import ConsoleUtils
from hdlbuild.utils.project_loader import load_project_config
cli = typer.Typer(rich_help_panel="🧪 Test Commands")
@cli.callback(invoke_without_command=True)
def test(
target: str = typer.Argument(
None,
help="Name of the test target (leave empty to run all)",
show_default=False,
)
) -> None:
"""
Build and run testbenches.
```bash
hdlbuild test # run all TBs
hdlbuild test alu # run TB 'alu' only
```
"""
console = ConsoleUtils("hdlbuild")
project = load_project_config()
console.print("Starting test flow …")
build_testbench(project, target)
run_testbench(project, target)
console.print("Tests finished.")

View File

@@ -14,6 +14,7 @@ class ToolOptions(BaseModel):
bitgen: List[str] = Field(default_factory=list)
trace: List[str] = Field(default_factory=list)
fuse: List[str] = Field(default_factory=list)
isim: List[str] = Field(default_factory=list)
class Dependency(BaseModel):
name: Optional[str] = None # Name ist jetzt optional

View File

@@ -0,0 +1,6 @@
.hdlbuild_deps/
.working/
reports/
output/
.locale/
vhdl_ls.toml

View File

@@ -0,0 +1,268 @@
name:
topmodule:
target_device: xc3s1200e-4-fg320
xilinx_path: /opt/Xilinx/14.7/ISE_DS/ISE
constraints:
sources:
vhdl:
- path: src/*.vhd
library: work
testbenches:
vhdl:
- path: tests/*.vhd
library: work
dependencies:
# - git: "https://git.0xmax42.io/maxp/Asynchronous-FIFO-AXI-Handshake.git"
# rev: "hdlbuild"
build:
build_dir: working
report_dir: reports
copy_target_dir: output
# Tool Optionen
tool_options:
common:
- "-intstyle"
- "xflow"
ngdbuild: []
map:
- "-detail"
- "-timing"
- "-ol"
- "high"
par: []
bitgen:
- "-g"
- "StartupClk:JtagClk"
trace:
- "-v"
- "3"
- "-n"
- "3"
fuse:
- "-incremental"
isim:
- "-gui"
xst:
# Optimization goal: prioritize speed or area.
# Values: Speed | Area
- "-opt_mode Speed"
# Optimization level: more aggressive optimizations at level 2.
# Values: 1 | 2
- "-opt_level 2"
# Use the new XST parser (recommended for modern designs).
# Values: yes | no
- "-use_new_parser yes"
# Preserve design hierarchy or allow flattening for optimization.
# Values: Yes | No | Soft
- "-keep_hierarchy No"
# Determines how hierarchy is preserved in the netlist.
# Values: As_Optimized | Rebuilt
- "-netlist_hierarchy As_Optimized"
# Global optimization strategy for nets.
# Values: AllClockNets | Offset_In_Before | Offset_Out_After | Inpad_To_Outpad | Max_Delay
- "-glob_opt AllClockNets"
## Misc ##
# Enable reading of IP cores.
# Values: YES | NO
- "-read_cores YES"
# Do not write timing constraints into synthesis report.
# Values: YES | NO
- "-write_timing_constraints NO"
# Analyze paths across different clock domains.
# Values: YES | NO
- "-cross_clock_analysis NO"
# Character used to separate hierarchy levels in instance names.
# Default: /
- "-hierarchy_separator /"
# Delimiters used for bus signals.
# Values: <> | [] | () | {}
- "-bus_delimiter <>"
# Maintain original case of identifiers.
# Values: Maintain | Upper | Lower
- "-case Maintain"
# Target maximum utilization ratio for slices.
# Values: 1–100
- "-slice_utilization_ratio 100"
# Target maximum utilization ratio for BRAMs.
# Values: 1–100
- "-bram_utilization_ratio 100"
# Use Verilog 2001 syntax features.
# Values: YES | NO
- "-verilog2001 YES"
#### HDL Options ####
## FSM ##
# Extract FSMs (Finite State Machines) from HDL code.
# Values: YES | NO
- "-fsm_extract YES"
# Encoding strategy for FSMs.
# Values: Auto | Gray | One-Hot | Johnson | Compact | Sequential | Speed1 | User
- "-fsm_encoding Auto"
# Add safe logic for undefined FSM states.
# Values: Yes | No
- "-safe_implementation No"
# Structure used to implement FSMs.
# Values: LUT | BRAM
- "-fsm_style LUT"
## RAM/ROM ##
# Extract RAM inference from HDL.
# Values: Yes | No
- "-ram_extract Yes"
# Style used to implement RAM.
# Values: Auto | Block | Distributed
- "-ram_style Auto"
# Extract ROM inference from HDL.
# Values: Yes | No
- "-rom_extract Yes"
# Style used for implementing ROM.
# Values: Auto | Distributed | Block
- "-rom_style Auto"
# Enable or disable automatic BRAM packing.
# Values: YES | NO
- "-auto_bram_packing NO"
## MUX/Decoder/Shift Register ##
# Extract multiplexers where possible.
# Values: Yes | No | Force
- "-mux_extract Yes"
# Style used for implementing MUX logic.
# Values: Auto | MUXCY | MUXF
- "-mux_style Auto"
# Extract decoder logic from behavioral code.
# Values: YES | NO
- "-decoder_extract YES"
# Extract and optimize priority encoder structures.
# Values: Yes | No | Force
- "-priority_extract Yes"
# Extract shift register logic.
# Values: YES | NO
- "-shreg_extract YES"
# Extract simple shift operations into dedicated hardware.
# Values: YES | NO
- "-shift_extract YES"
## Multiplier ##
# Style for implementing multipliers.
# Values: Auto | LUT | Pipe_LUT | Pipe_Block | Block
- "-mult_style Auto"
## Misc ##
# Collapse XOR trees where beneficial.
# Values: YES | NO
- "-xor_collapse YES"
# Share resources like adders or multipliers between logic blocks.
# Values: YES | NO | Force
- "-resource_sharing YES"
# Convert asynchronous resets to synchronous where possible.
# Values: YES | NO
- "-async_to_sync NO"
#### Xilinx Specific Options ####
## Optimization ##
# Enable removal of logically equivalent registers.
# Values: YES | NO
- "-equivalent_register_removal YES"
# Duplicate registers to reduce fanout or improve timing.
# Values: YES | NO
- "-register_duplication YES"
# Move registers across logic to balance timing.
# Values: Yes | No | Forward | Backward
- "-register_balancing No"
# Use clock enable signals where possible.
# Values: Auto | Yes | No
- "-use_clock_enable Yes"
# Use synchronous set (preset) signals when available.
# Values: Auto | Yes | No
- "-use_sync_set Yes"
# Use synchronous reset signals where possible.
# Values: Auto | Yes | No
- "-use_sync_reset Yes"
## I/O ##
# Insert IO buffers for top-level ports.
# Values: YES | NO
- "-iobuf YES"
# Placement strategy for IOB registers (Auto = let tools decide).
# Values: Auto | YES | NO
- "-iob Auto"
## Misc ##
# Maximum allowed fanout for a net.
# Values: integer (e.g., 500)
- "-max_fanout 500"
# Maximum number of BUFGs (global buffers) to use.
# Values: 0–32 (device-dependent)
- "-bufg 24"
# Enable logic packing into slices.
# Values: YES | NO
- "-slice_packing YES"
# Try to reduce the number of primitive instances used.
# Values: YES | NO
- "-optimize_primitives NO"
# Margin in percent beyond the target slice utilization.
# Values: 0–100
- "-slice_utilization_ratio_maxmargin 5"

View File

@@ -23,7 +23,8 @@ def copy_bitstream_file(project: ProjectConfig):
copy_file(
project=project,
source_filename=f"{project.name}.bit",
destination_filename=f"{project.name}.Bitstream",
destination_filename=f"{project.name}.bit",
destination_dir=DIRECTORIES.copy_target,
description="Bitstream File",
step_number=10, total_steps=12
)

View File

@@ -46,6 +46,7 @@ def copy_file(
source_filename: str,
destination_filename: str,
description: str = "Report",
destination_dir: str = DIRECTORIES.report,
step_number: Optional[int] = None,
total_steps: Optional[int] = None
):
@@ -59,12 +60,12 @@ def copy_file(
description (str): Optionale Beschreibung für die Ausgabe (z.B. "Synthesis Report")
"""
src_path = os.path.join(DIRECTORIES.build, source_filename)
dst_path = os.path.join(DIRECTORIES.report, destination_filename)
dst_path = os.path.join(destination_dir, destination_filename)
if not os.path.exists(src_path):
raise FileNotFoundError(f"{description} nicht gefunden: {src_path}")
os.makedirs(DIRECTORIES.report, exist_ok=True)
os.makedirs(destination_dir, exist_ok=True)
shutil.copyfile(src_path, dst_path)

View File

@@ -0,0 +1,140 @@
import os
from typing import List
from hdlbuild.models.project import ProjectConfig
from hdlbuild.models.config import DIRECTORIES
from hdlbuild.dependencies.resolver import DependencyResolver
from hdlbuild.tools.xilinx_ise.common import run_tool
from hdlbuild.utils.console_utils import ConsoleTask
from hdlbuild.utils.source_resolver import expand_all_sources, expand_testbenches
def generate_simulation_project_file(project: ProjectConfig, output_path: str, testbench_name: str):
"""
Generiert die ISim Simulationsprojektdatei (.prj).
Args:
project (ProjectConfig): Das Hauptprojekt.
output_path (str): Zielpfad für die .prj Datei.
testbench_name (str): Name der Testbench-Datei (z.B. "VGATimingGenerator_test_tb").
"""
resolver = DependencyResolver(project, offline_mode=True)
resolver.resolve_all()
vhdl_sources, verilog_sources = expand_all_sources(project, resolver.resolved)
with open(output_path, "w") as f:
# Normale VHDL-Sources
for lib, file in vhdl_sources:
f.write(f"vhdl {lib} \"{DIRECTORIES.get_relative_prefix()}{file}\"\n")
# Normale Verilog-Sources
for lib, file in verilog_sources:
f.write(f"verilog {lib} \"{DIRECTORIES.get_relative_prefix()}{file}\"\n")
# Testbench-Datei suchen und einfügen
testbench_file = find_testbench_file(project, testbench_name)
normalized_tb = os.path.normpath(testbench_file)
f.write(f"vhdl work \"{DIRECTORIES.get_relative_prefix()}{normalized_tb}\"\n")
# glbl.v immer zuletzt
f.write(f"verilog work /opt/Xilinx/14.7/ISE_DS/ISE/verilog/src/glbl.v\n")
def find_testbench_file(project: ProjectConfig, testbench_name: str) -> str:
"""
Findet eine Testbench-Datei im Projekt anhand ihres Namens (ohne Endung, Case-Insensitive).
Args:
project (ProjectConfig): Projektdefinition.
testbench_name (str): Gesuchter Dateiname (z.B. "VGATimingGenerator_test_tb").
Returns:
str: Vollständiger Pfad zur Testbench-Datei.
Raises:
FileNotFoundError: Wenn die Datei nicht gefunden wurde.
"""
candidates = expand_testbenches(project)
# Vergleichswerte vorbereiten (Name ohne Endung, in Kleinbuchstaben)
search_name = os.path.splitext(testbench_name)[0].lower()
for _, filepath in candidates:
filename = os.path.basename(filepath)
filename_no_ext = os.path.splitext(filename)[0].lower()
if filename_no_ext == search_name:
return filepath
raise FileNotFoundError(f"Testbench '{testbench_name}' wurde nicht gefunden.")
def build_testbench(project: ProjectConfig, testbench_name: str):
"""
Baut eine einzelne Testbench mit FUSE.
Args:
project (ProjectConfig): Hauptprojekt-Konfiguration
testbench_name (str): Name der Testbench-Datei, z.B. "VGATimingGenerator_test_tb.vhd"
"""
# Pfade
isim_exe_name = f"isim_{testbench_name.replace('.vhd', '').replace('.v', '')}"
isim_exe_path = os.path.join(DIRECTORIES.build, isim_exe_name)
# 1. Simulation-Projektdatei generieren
generate_simulation_project_file(
project=project,
output_path=os.path.join(DIRECTORIES.build, f"{project.name}_sim.prj"),
testbench_name=testbench_name
)
# 2. FUSE-Befehl ausführen mit `run_tool`
mandatory_arguments = [
"-prj", f"{project.name}_sim.prj",
"-o", isim_exe_name,
f"work.{testbench_name.replace('.vhd', '').replace('.v', '')}",
"work.glbl"
]
run_tool(
project=project,
tool_executable_name="fuse",
tool_option_attr="fuse",
mandatory_arguments=mandatory_arguments,
working_dir=DIRECTORIES.build,
silent=False
)
def run_testbench(project: ProjectConfig, testbench_name: str):
"""
Führt eine gebaute Testbench-Executable aus (ISim Simulation).
Args:
testbench_name (str): Name der Testbench-Datei (z.B. "VGATimingGenerator_test_tb.vhd")
"""
# Pfade
isim_exe_name = f"isim_{testbench_name.replace('.vhd', '').replace('.v', '')}"
isim_cmd_file = os.path.join(DIRECTORIES.build, f"{isim_exe_name}.cmd")
# 1. TCL-Skript für ISim erzeugen (einfache Simulation)
with open(isim_cmd_file, "w") as f:
f.write("")
cmd = [f"./{isim_exe_name}"]
tool_opts = getattr(project.tool_options, "isim", [])
if tool_opts:
cmd.extend(tool_opts)
# 2. Kommando bauen
cmd.extend([
"-tclbatch",
f"{isim_exe_name}.cmd"
])
# 3. Ausführen
task = ConsoleTask(prefix="hdlbuild", title=f"RUN {testbench_name}")
result = task.run_command(cmd, cwd=DIRECTORIES.build)
if result != 0:
raise RuntimeError(f"Testbench {testbench_name} ist während der Simulation fehlgeschlagen!")

View File

@@ -66,3 +66,35 @@ def expand_all_sources(root_project: ProjectConfig, resolved_dependencies: List[
all_verilog_sources.extend(verilog_dep)
return all_vhdl_sources, all_verilog_sources
def expand_testbenches(project: ProjectConfig) -> List[Tuple[str, str]]:
"""
Expandiert nur die Testbenches (vhdl und verilog) aus dem Hauptprojekt.
Args:
project (ProjectConfig): Das Hauptprojekt.
Returns:
List of (library, filepath) Tupel
"""
expanded = []
if project.testbenches:
# VHDL Testbenches
for source in project.testbenches.vhdl:
full_pattern = os.path.join(".", source.path)
matched_files = glob.glob(full_pattern, recursive=True)
for file in matched_files:
normalized = os.path.normpath(file)
expanded.append((source.library, normalized))
# Verilog Testbenches (optional)
for source in project.testbenches.verilog:
full_pattern = os.path.join(".", source.path)
matched_files = glob.glob(full_pattern, recursive=True)
for file in matched_files:
normalized = os.path.normpath(file)
expanded.append((source.library, normalized))
return expanded