Compare commits
6 Commits
feature/tr
...
dodo
Author | SHA1 | Date | |
---|---|---|---|
2fce367686 | |||
5d4cd3e315 | |||
54949f43c0 | |||
9e1255568e | |||
63140660a3 | |||
![]() |
c661c3f453 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,2 +1,5 @@
|
|||||||
working/
|
working/
|
||||||
.locale/
|
reports/
|
||||||
|
*__pycache__*
|
||||||
|
*.venv
|
||||||
|
.doit.db
|
||||||
|
49
Makefile
49
Makefile
@@ -12,7 +12,7 @@
|
|||||||
# Version
|
# Version
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
Makefile_Version := 1.2.0-alpha
|
Makefile_Version := 1.1.5
|
||||||
$(info ISE Makefile Version: $(Makefile_Version))
|
$(info ISE Makefile Version: $(Makefile_Version))
|
||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
@@ -40,7 +40,7 @@ endif
|
|||||||
TOPLEVEL ?= $(PROJECT)
|
TOPLEVEL ?= $(PROJECT)
|
||||||
CONSTRAINTS ?= $(PROJECT).ucf
|
CONSTRAINTS ?= $(PROJECT).ucf
|
||||||
BUILD_DIR ?= working
|
BUILD_DIR ?= working
|
||||||
TRANSPILING_DIR ?= transpiling
|
REPORT_DIR ?= reports
|
||||||
BITFILE ?= $(BUILD_DIR)/$(PROJECT).bit
|
BITFILE ?= $(BUILD_DIR)/$(PROJECT).bit
|
||||||
|
|
||||||
COMMON_OPTS ?= -intstyle xflow
|
COMMON_OPTS ?= -intstyle xflow
|
||||||
@@ -53,7 +53,7 @@ TRACE_OPTS ?= -v 3 -n 3
|
|||||||
FUSE_OPTS ?= -incremental
|
FUSE_OPTS ?= -incremental
|
||||||
|
|
||||||
ISIM_OPTS ?= -gui
|
ISIM_OPTS ?= -gui
|
||||||
ISIM_CMD ?= vcd dumpfile $@.vcd\nvcd dumpvars -m /UUT\nrun all\nvcd dumpflush\nquit
|
ISIM_CMD ?=
|
||||||
|
|
||||||
PROGRAMMER ?= none
|
PROGRAMMER ?= none
|
||||||
PROGRAMMER_PRE ?=
|
PROGRAMMER_PRE ?=
|
||||||
@@ -144,10 +144,12 @@ default: $(BITFILE)
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BUILD_DIR)
|
rm -rf $(BUILD_DIR)
|
||||||
|
rm -rf $(REPORT_DIR)
|
||||||
|
|
||||||
$(BUILD_DIR)/$(PROJECT).prj: ../project.cfg
|
$(BUILD_DIR)/$(PROJECT).prj: ../project.cfg
|
||||||
@echo "Updating $@"
|
@echo "Updating $@"
|
||||||
@mkdir -p $(BUILD_DIR)
|
@mkdir -p $(BUILD_DIR)
|
||||||
|
@mkdir -p $(REPORT_DIR)
|
||||||
@rm -f $@
|
@rm -f $@
|
||||||
@$(foreach idx,$(shell seq 1 $(words $(V_PATHS))),echo "verilog $(word $(idx),$(V_LIBS)) \"../$(word $(idx),$(V_PATHS))\"" >> $@;)
|
@$(foreach idx,$(shell seq 1 $(words $(V_PATHS))),echo "verilog $(word $(idx),$(V_LIBS)) \"../$(word $(idx),$(V_PATHS))\"" >> $@;)
|
||||||
@$(foreach idx,$(shell seq 1 $(words $(VHD_PATHS))),echo "vhdl $(word $(idx),$(VHD_LIBS)) \"../$(word $(idx),$(VHD_PATHS))\"" >> $@;)
|
@$(foreach idx,$(shell seq 1 $(words $(VHD_PATHS))),echo "vhdl $(word $(idx),$(VHD_LIBS)) \"../$(word $(idx),$(VHD_PATHS))\"" >> $@;)
|
||||||
@@ -173,51 +175,39 @@ $(BUILD_DIR)/$(PROJECT).scr: ../project.cfg
|
|||||||
"-p $(TARGET_PART)" \
|
"-p $(TARGET_PART)" \
|
||||||
> $(BUILD_DIR)/$(PROJECT).scr
|
> $(BUILD_DIR)/$(PROJECT).scr
|
||||||
|
|
||||||
$(BITFILE): ../project.cfg $(V_PATHS) $(VHD_PATHS) ../$(CONSTRAINTS) $(BUILD_DIR)/$(PROJECT).prj transpile $(BUILD_DIR)/$(PROJECT).scr
|
$(BITFILE): ../project.cfg $(V_PATHS) $(VHD_PATHS) ../$(CONSTRAINTS) $(BUILD_DIR)/$(PROJECT).prj $(BUILD_DIR)/$(PROJECT).scr
|
||||||
@mkdir -p $(BUILD_DIR)
|
@mkdir -p $(BUILD_DIR)
|
||||||
|
@mkdir -p $(REPORT_DIR)
|
||||||
$(call RUN,xst) $(COMMON_OPTS) \
|
$(call RUN,xst) $(COMMON_OPTS) \
|
||||||
-ifn $(PROJECT).scr
|
-ifn $(PROJECT).scr
|
||||||
|
@cp ./$(BUILD_DIR)/$(PROJECT).srp $(REPORT_DIR)/$(PROJECT).SynthesisReport
|
||||||
$(call RUN,ngdbuild) $(COMMON_OPTS) $(NGDBUILD_OPTS) \
|
$(call RUN,ngdbuild) $(COMMON_OPTS) $(NGDBUILD_OPTS) \
|
||||||
-p $(TARGET_PART) -uc ../../$(CONSTRAINTS) \
|
-p $(TARGET_PART) -uc ../../$(CONSTRAINTS) \
|
||||||
$(PROJECT).ngc $(PROJECT).ngd
|
$(PROJECT).ngc $(PROJECT).ngd
|
||||||
$(call RUN,map) $(COMMON_OPTS) $(MAP_OPTS) \
|
$(call RUN,map) $(COMMON_OPTS) $(MAP_OPTS) \
|
||||||
-p $(TARGET_PART) \
|
-p $(TARGET_PART) \
|
||||||
-w $(PROJECT).ngd -o $(PROJECT).map.ncd $(PROJECT).pcf
|
-w $(PROJECT).ngd -o $(PROJECT).map.ncd $(PROJECT).pcf
|
||||||
|
@cp ./$(BUILD_DIR)/$(PROJECT).map.mrp $(REPORT_DIR)/$(PROJECT).MapReport
|
||||||
$(call RUN,par) $(COMMON_OPTS) $(PAR_OPTS) \
|
$(call RUN,par) $(COMMON_OPTS) $(PAR_OPTS) \
|
||||||
-w $(PROJECT).map.ncd $(PROJECT).ncd $(PROJECT).pcf
|
-w $(PROJECT).map.ncd $(PROJECT).ncd $(PROJECT).pcf
|
||||||
|
@cp ./$(BUILD_DIR)/$(PROJECT).par $(REPORT_DIR)/$(PROJECT).PlaceRouteReport
|
||||||
$(call RUN,bitgen) $(COMMON_OPTS) $(BITGEN_OPTS) \
|
$(call RUN,bitgen) $(COMMON_OPTS) $(BITGEN_OPTS) \
|
||||||
-w $(PROJECT).ncd $(PROJECT).bit
|
-w $(PROJECT).ncd $(PROJECT).bit
|
||||||
@echo "\e[1;32m============ OK ============\e[m\n\n"
|
@echo "\e[1;32m============ OK ============\e[m\n\n"
|
||||||
@echo "\e[1;33m============ Reports.. ===========\e[m\n"
|
@echo "\e[1;33m============ Reports.. ===========\e[m\n"
|
||||||
@echo "\e[1;97m==== Synthesis Summary Report ====\e[m"
|
@echo "\e[1;97m==== Synthesis Summary Report ====\e[m"
|
||||||
@echo "\e[1;35m ./$(BUILD_DIR)/$(PROJECT).srp\e[m\n"
|
@echo "\e[1;35m ./$(REPORT_DIR)/$(PROJECT).SynthesisReport\e[m\n"
|
||||||
@echo "\e[1;97m======= Map Summary Report =======\e[m"
|
@echo "\e[1;97m======= Map Summary Report =======\e[m"
|
||||||
@echo "\e[1;35m ./$(BUILD_DIR)/$(PROJECT).map.mrp\e[m\n"
|
@echo "\e[1;35m ./$(REPORT_DIR)/$(PROJECT).MapReport\e[m\n"
|
||||||
@echo "\e[1;97m======= PAR Summary Report =======\e[m"
|
@echo "\e[1;97m======= PAR Summary Report =======\e[m"
|
||||||
@echo "\e[1;35m ./$(BUILD_DIR)/$(PROJECT).par\e[m\n"
|
@echo "\e[1;35m ./$(REPORT_DIR)/$(PROJECT).PlaceRouteReport\e[m\n"
|
||||||
@echo "\e[1;97m===== Pinout Summary Report ======\e[m"
|
@echo "\e[1;97m===== Pinout Summary Report ======\e[m"
|
||||||
@echo "\e[1;35m ./$(BUILD_DIR)/$(PROJECT)_pad.txt\e[m\n"
|
@cp ./$(BUILD_DIR)/$(PROJECT)_pad.txt $(REPORT_DIR)/$(PROJECT).PinoutReport
|
||||||
|
@echo "\e[1;35m ./$(REPORT_DIR)/$(PROJECT).PinoutReport\e[m\n"
|
||||||
###########################################################################
|
|
||||||
# Transpiling (work in progress)
|
|
||||||
###########################################################################
|
|
||||||
|
|
||||||
transpile:
|
|
||||||
ifeq ($(TRANSPILING),true)
|
|
||||||
@echo "Transpiling is enabled"
|
|
||||||
@mkdir -p ./$(BUILD_DIR)/$(TRANSPILING_DIR)/dist
|
|
||||||
@for idx in $(shell seq 1 $(words $(VHD_PATHS))); do \
|
|
||||||
vhdl_path=$$(echo $(VHD_PATHS) | cut -d ' ' -f $$idx); \
|
|
||||||
vhdl_lib=$$(echo $(VHD_LIBS) | cut -d ' ' -f $$idx); \
|
|
||||||
echo "Analyzing $$vhdl_path for library $$vhdl_lib..."; \
|
|
||||||
ghdl -a --std=08 --workdir=./$(BUILD_DIR)/$(TRANSPILING_DIR)/dist "./$$vhdl_path"; \
|
|
||||||
done
|
|
||||||
ghdl --synth --std=08 --workdir=./$(BUILD_DIR)/$(TRANSPILING_DIR)/dist --out=raw-vhdl $(TOPLEVEL) > ./$(BUILD_DIR)/$(TRANSPILING_DIR)/$(TOPLEVEL).vhd
|
|
||||||
@echo "vhdl work \"$(TRANSPILING_DIR)/$(TOPLEVEL).vhd\"" > $(BUILD_DIR)/$(PROJECT).prj
|
|
||||||
else
|
|
||||||
@echo "Transpiling is disabled"
|
|
||||||
endif
|
|
||||||
|
|
||||||
|
copy: $(BITFILE)
|
||||||
|
@cp $(BITFILE) $(COPY_TARGET_DIR)/$(PROJECT).bit
|
||||||
|
@echo "\n\e[1;32m= Copy bitfile successful =\e[m\n"
|
||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
# Testing (work in progress)
|
# Testing (work in progress)
|
||||||
@@ -228,7 +218,8 @@ trace: ../project.cfg $(BITFILE)
|
|||||||
$(PROJECT).ncd $(PROJECT).pcf
|
$(PROJECT).ncd $(PROJECT).pcf
|
||||||
@echo "\n\e[1;33m============ Reports.. ===========\e[m\n"
|
@echo "\n\e[1;33m============ Reports.. ===========\e[m\n"
|
||||||
@echo "\e[1;97m===== Timing Summary Report ======\e[m"
|
@echo "\e[1;97m===== Timing Summary Report ======\e[m"
|
||||||
@echo "\e[1;35m ./$(BUILD_DIR)/$(PROJECT).twr\e[m\n"
|
@cp ./$(BUILD_DIR)/$(PROJECT).twr $(REPORT_DIR)/$(PROJECT).TimingReport
|
||||||
|
@echo "\e[1;35m ./$(REPORT_DIR)/$(PROJECT).TimingReport\e[m\n"
|
||||||
|
|
||||||
test: buildtest runtest
|
test: buildtest runtest
|
||||||
|
|
||||||
|
226
README.md
226
README.md
@@ -1,225 +1 @@
|
|||||||
# Xilinx ISE Makefile
|
**Todo**
|
||||||
|
|
||||||
Tired of clicking around in Xilinx ISE? Run your builds from the command line!
|
|
||||||
|
|
||||||
## Forked from..
|
|
||||||
|
|
||||||
The original project is located at [Xilinx-ISE-Makefile](https://github.com/duskwuff/Xilinx-ISE-Makefile) and was created by [duskwuff](github.com/duskwuff/).
|
|
||||||
|
|
||||||
Many thanks for the good work!
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Xilinx ISE, ideally 14.7 (the final version)
|
|
||||||
|
|
||||||
Works great on Linux. Windows Subsystem for Linux is tested and works well.
|
|
||||||
|
|
||||||
- GNU (or compatible?) Make
|
|
||||||
|
|
||||||
Install this through Cygwin on Windows.
|
|
||||||
|
|
||||||
## Creating a project
|
|
||||||
|
|
||||||
To start building a project, you will need to create a file `project.cfg` in
|
|
||||||
the top level of your project. This file is a text file sourced by Make, so
|
|
||||||
it consists of `KEY = value` pairs. It must define at least the following keys:
|
|
||||||
|
|
||||||
- `PROJECT`
|
|
||||||
|
|
||||||
The name of the project, used as a name for certain intermediate files, and
|
|
||||||
as the default name for the top-level module and constraints file.
|
|
||||||
|
|
||||||
- `TARGET_PART`
|
|
||||||
|
|
||||||
The full part-speed-package identifier for the Xilinx part to be targeted,
|
|
||||||
e.g. `xc6slx9-2-tqg144`.
|
|
||||||
|
|
||||||
- `XILINX`
|
|
||||||
|
|
||||||
The path to the appropriate binaries directory of the target Xilinx ISE
|
|
||||||
install, e.g.
|
|
||||||
`/cygdrive/c/Xilinx/14.7/ISE_DS/ISE`
|
|
||||||
or
|
|
||||||
`/opt/Xilinx/14.7/ISE_DS/ISE`
|
|
||||||
for typical installs.
|
|
||||||
|
|
||||||
- `VSOURCE` and/or `VHDSOURCE`
|
|
||||||
|
|
||||||
The space-separated names of all Verilog and/or VHDL source files to be
|
|
||||||
used in the project.
|
|
||||||
|
|
||||||
You can define these on multiple lines using `+=`, e.g.
|
|
||||||
|
|
||||||
VSOURCE += foo.v
|
|
||||||
VSOURCE += bar.v
|
|
||||||
|
|
||||||
You can also add a library name to the source file, e.g.
|
|
||||||
|
|
||||||
VSOURCE += my_lib:foo.v
|
|
||||||
VSOURCE += my_lib:bar.v
|
|
||||||
|
|
||||||
The default library name is `work`.
|
|
||||||
|
|
||||||
A simple `project.cfg` may thus resemble:
|
|
||||||
|
|
||||||
PROJECT = example
|
|
||||||
TARGET_PART = xc6slx9-2-cpg196
|
|
||||||
|
|
||||||
XILINX = /cygdrive/c/Xilinx/14.7/ISE_DS/ISE/bin/nt64
|
|
||||||
|
|
||||||
VSOURCE = example.v
|
|
||||||
|
|
||||||
A number of other keys can be set in the project configuration, including:
|
|
||||||
|
|
||||||
- `XILINX_PLATFORM`
|
|
||||||
|
|
||||||
The Xilinx name for the platform to build for, e.g. `nt64` or `lin`.
|
|
||||||
`nt64` is used by default for Windows systems, and `lin64` for Linux
|
|
||||||
systems, so you only need to set this if you explicitly need to use the
|
|
||||||
32-bit version of the tools for some reason.
|
|
||||||
|
|
||||||
- `TOPLEVEL`
|
|
||||||
|
|
||||||
The name of the top-level module to be used in the project.
|
|
||||||
(Defaults to `$PROJECT`.)
|
|
||||||
|
|
||||||
- `CONSTRAINTS`
|
|
||||||
|
|
||||||
The name of the constraints file (`.ucf`) to be used for the project.
|
|
||||||
(Defaults to `$PROJECT.ucf`.)
|
|
||||||
|
|
||||||
- `COMMON_OPTS`
|
|
||||||
|
|
||||||
Extra command-line options to be passed to all ISE executables. Defaults to
|
|
||||||
`-intstyle xflow`.
|
|
||||||
|
|
||||||
- `XST_OPTS`, `NGDBUILD_OPTS`, `MAP_OPTS`, `PAR_OPTS`, `BITGEN_OPTS`,
|
|
||||||
`TRACE_OPTS`, `FUSE_OPTS`
|
|
||||||
|
|
||||||
Extra command-line options to be passed to the corresponding ISE tools.
|
|
||||||
|
|
||||||
Defaults is:
|
|
||||||
|
|
||||||
```
|
|
||||||
XST_OPTS ?=
|
|
||||||
NGDBUILD_OPTS ?=
|
|
||||||
MAP_OPTS ?= -detail
|
|
||||||
PAR_OPTS ?=
|
|
||||||
BITGEN_OPTS ?=
|
|
||||||
TRACE_OPTS ?= -v 3 -n 3
|
|
||||||
FUSE_OPTS ?= -incremental
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that `XST_OPTS` will not appear on the command line during
|
|
||||||
compilation, as the XST options are embedded in a script file.
|
|
||||||
|
|
||||||
`MAP_OPTS` and `PAR_OPTS` can be set to `-mt 2` to use multithreading,
|
|
||||||
which may speed up compilation of large designs.
|
|
||||||
|
|
||||||
`BITGEN_OPTS` can be set to `-g Compress` to apply bitstream compression.
|
|
||||||
|
|
||||||
- `PROGRAMMER`
|
|
||||||
|
|
||||||
The name of the programmer to be used for `make prog`. Currently supported
|
|
||||||
values are:
|
|
||||||
|
|
||||||
- `impact`
|
|
||||||
|
|
||||||
Uses Xilinx iMPACT for programming, using a batch file named
|
|
||||||
`impact.cmd` by default. The iMPACT command line may be overridden by
|
|
||||||
setting `IMPACT_OPTS`.
|
|
||||||
|
|
||||||
A typical batch file may resemble:
|
|
||||||
|
|
||||||
setMode -bscan
|
|
||||||
setCable -p auto
|
|
||||||
addDevice -p 1 -file build/projectname.bit
|
|
||||||
program -p 1
|
|
||||||
quit
|
|
||||||
|
|
||||||
- `digilent`
|
|
||||||
|
|
||||||
Uses the Digilent JTAG utility for programming, which must be installed
|
|
||||||
separately. The name of the board must be set as `DJTG_DEVICE`; the
|
|
||||||
path to the djtgcfg executable can be set as `DJTG_EXE`, and the index
|
|
||||||
of the device can be set as `DJTG_INDEX`. You can set the flash index
|
|
||||||
with `DJTG_FLASH_INDEX`.
|
|
||||||
|
|
||||||
- `xc3sprog`
|
|
||||||
|
|
||||||
Uses the xc3sprog utility for programming, which must also be installed
|
|
||||||
separately. The cable name must be set as `XC3SPROG_CABLE`; additional
|
|
||||||
options can be set as `XC3SPROG_OPTS`.
|
|
||||||
|
|
||||||
- `PROGRAMMER_PRE`
|
|
||||||
|
|
||||||
A command to be run before programming. This can be used to use `sudo` or
|
|
||||||
`yes` to confirm programming.
|
|
||||||
|
|
||||||
## Targets
|
|
||||||
|
|
||||||
The Xilinx ISE Makefile implements the following targets:
|
|
||||||
|
|
||||||
- `make default` (or just `make`)
|
|
||||||
|
|
||||||
Builds the bitstream.
|
|
||||||
|
|
||||||
- `make clean`
|
|
||||||
|
|
||||||
Removes the build directory.
|
|
||||||
|
|
||||||
- `make prog`
|
|
||||||
|
|
||||||
Writes the bitstream to a target device. Requires some additional
|
|
||||||
configuration; see below for details.
|
|
||||||
|
|
||||||
- `make flash`
|
|
||||||
|
|
||||||
Writes the bitstream to a flash device.
|
|
||||||
**This is currently only for digilent implemented.**
|
|
||||||
|
|
||||||
## Console output
|
|
||||||
|
|
||||||
After a successful build, you will find the paths to the generated **reports** on the console. E.g.:
|
|
||||||
|
|
||||||
```
|
|
||||||
============ Reports.. ===========
|
|
||||||
|
|
||||||
==== Synthesis Summary Report ====
|
|
||||||
./build/Example.srp
|
|
||||||
|
|
||||||
======= Map Summary Report =======
|
|
||||||
./build/Example.map.mrp
|
|
||||||
|
|
||||||
======= PAR Summary Report =======
|
|
||||||
./build/Example.par
|
|
||||||
|
|
||||||
===== Pinout Summary Report ======
|
|
||||||
./build/Example_pad.txt
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## Unimplemented features
|
|
||||||
|
|
||||||
The following features are not currently implemented. (Pull requests are
|
|
||||||
encouraged!)
|
|
||||||
|
|
||||||
- Generation of SPI or other unusual programming files
|
|
||||||
|
|
||||||
- CPLD synthesis
|
|
||||||
|
|
||||||
- Synthesis tools other than XST
|
|
||||||
|
|
||||||
- Display and/or handling of warnings and errors from `build/_xmsgs`
|
|
||||||
|
|
||||||
- Running unit tests
|
|
||||||
|
|
||||||
- Anything else (open an issue?)
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
To the extent possible under law, the author(s) have dedicated all copyright
|
|
||||||
and related and neighboring rights to this software to the public domain
|
|
||||||
worldwide. This software is distributed without any warranty.
|
|
||||||
|
|
||||||
See LICENSE.md for details.
|
|
78
dodo.py
Normal file
78
dodo.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
from tasks.generate_prj import generate_prj
|
||||||
|
from tasks.generate_scr import generate_scr
|
||||||
|
from tools.config import parse_project_cfg
|
||||||
|
from tools.defaults import with_defaults
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
HERE = Path(__file__).parent.resolve() # Verzeichnis, in dem dodo.py liegt
|
||||||
|
CFG_PATH = HERE.parent / "project.cfg"
|
||||||
|
TOML_PATH = HERE.parent / "vhdl_ls.toml"
|
||||||
|
|
||||||
|
|
||||||
|
def _get_project_name(cfg_path: Path) -> str:
|
||||||
|
return with_defaults(parse_project_cfg(cfg_path))["PROJECT"]
|
||||||
|
|
||||||
|
|
||||||
|
def task_generate_prj():
|
||||||
|
cfg_path = Path(CFG_PATH)
|
||||||
|
toml_path = Path(TOML_PATH)
|
||||||
|
project = _get_project_name(cfg_path)
|
||||||
|
prj_path = Path("working") / f"{project}.prj"
|
||||||
|
|
||||||
|
return {
|
||||||
|
"actions": [(generate_prj, [prj_path, cfg_path, toml_path])],
|
||||||
|
"file_dep": [cfg_path, toml_path],
|
||||||
|
"targets": [prj_path],
|
||||||
|
"verbosity": 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def task_generate_scr():
|
||||||
|
cfg_path = Path(CFG_PATH)
|
||||||
|
project = _get_project_name(cfg_path)
|
||||||
|
scr_path = Path("working") / f"{project}.scr"
|
||||||
|
|
||||||
|
return {
|
||||||
|
"actions": [(generate_scr, [scr_path, cfg_path])],
|
||||||
|
"file_dep": [cfg_path],
|
||||||
|
"targets": [scr_path],
|
||||||
|
"verbosity": 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def task_xst():
|
||||||
|
"""Run XST synthesizer"""
|
||||||
|
cfg_path = Path(CFG_PATH)
|
||||||
|
cfg = with_defaults(parse_project_cfg(cfg_path))
|
||||||
|
|
||||||
|
build_dir = Path(cfg["BUILD_DIR"])
|
||||||
|
project = cfg["PROJECT"]
|
||||||
|
scr_file = f"{project}.scr"
|
||||||
|
scr_path = build_dir / scr_file
|
||||||
|
prj_path = build_dir / f"{project}.prj"
|
||||||
|
xilinx_path = Path(cfg["XILINX"])
|
||||||
|
xilinx_platform = "lin64" # TODO: dynamisch ermitteln, falls nötig
|
||||||
|
|
||||||
|
xst_exe = xilinx_path / "bin" / xilinx_platform / "xst"
|
||||||
|
common_opts = cfg.get("COMMON_OPTS", "")
|
||||||
|
|
||||||
|
def action():
|
||||||
|
print(f"============ Running XST ============")
|
||||||
|
print(f"> {xst_exe} {common_opts} -ifn {scr_file}")
|
||||||
|
subprocess.run(
|
||||||
|
[str(xst_exe), "-ifn", scr_file],
|
||||||
|
cwd=build_dir,
|
||||||
|
check=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
"actions": [action],
|
||||||
|
"file_dep": [scr_path, prj_path], # <== beides!
|
||||||
|
"targets": [Path(cfg["REPORT_DIR"]) / f"{project}.SynthesisReport"],
|
||||||
|
"verbosity": 2,
|
||||||
|
"task_dep": ["generate_scr", "generate_prj"],
|
||||||
|
}
|
110
project.cfg.sample
Normal file
110
project.cfg.sample
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
## Main settings.. ##
|
||||||
|
|
||||||
|
# Project name
|
||||||
|
# @remark The name of the project is used as default name for the top module and the ucf file
|
||||||
|
PROJECT =
|
||||||
|
|
||||||
|
# Target device
|
||||||
|
# @example xc3s1200e-4-fg320 | xc5vlx50t-1-ff1136
|
||||||
|
TARGET_PART =
|
||||||
|
|
||||||
|
# Path to the Xilinx ISE installation
|
||||||
|
XILINX = /opt/Xilinx/14.7/ISE_DS/ISE
|
||||||
|
|
||||||
|
# Optional the name of the top module (default is the project name)
|
||||||
|
# TOPLEVEL =
|
||||||
|
|
||||||
|
# Optional the path/name of the ucf file (default is the project name)
|
||||||
|
# CONSTRAINTS =
|
||||||
|
|
||||||
|
# Optional a target to copy the bit file to (make copy)
|
||||||
|
# COPY_TARGET_DIR =
|
||||||
|
|
||||||
|
## ## ## ## ## ## ## ##
|
||||||
|
# ---------------------
|
||||||
|
|
||||||
|
## Source files settings.. ##
|
||||||
|
# The source files to be compiled
|
||||||
|
# @example `VSOURCE += src/main.v` (add a single Verilog file per line)
|
||||||
|
# @example `VHDSOURCE += src/main.vhd` (add a single VHDL file per line)
|
||||||
|
|
||||||
|
|
||||||
|
## Test files settings.. ##
|
||||||
|
# The testbench files to be compiled
|
||||||
|
# @example `VTEST += tests/main_tb.v` (add a single Verilog testbench file per line)
|
||||||
|
# @example `VHDTEST += tests/main_tb.vhd` (add a single VHDL testbench file per line)
|
||||||
|
|
||||||
|
|
||||||
|
## ## ## ## ## ## ## ##
|
||||||
|
# ---------------------
|
||||||
|
|
||||||
|
## ISE executable settings.. ##
|
||||||
|
|
||||||
|
# General command line options to be passed to all ISE executables (default is `-intstyle xflow`)
|
||||||
|
# COMMON_OPTS =
|
||||||
|
|
||||||
|
# Options for the XST synthesizer
|
||||||
|
# @example -register_balancing (yes|no)
|
||||||
|
# @example -opt_mode (speed|area)
|
||||||
|
# @example -opt_level (1|2)
|
||||||
|
XST_OPTS =
|
||||||
|
|
||||||
|
# Options for the NGDBuild tool
|
||||||
|
# NGDBUILD_OPTS =
|
||||||
|
|
||||||
|
# Options for the MAP tool
|
||||||
|
# @example -mt 2 (multi-threading with 2 threads)
|
||||||
|
# @example -cm speed (speed optimization)
|
||||||
|
# @example -ol high
|
||||||
|
# @example -detail
|
||||||
|
# @example -timing
|
||||||
|
MAP_OPTS =
|
||||||
|
|
||||||
|
# Options for the PAR tool
|
||||||
|
# @example -mt 2 (multi-threading with 2 threads)
|
||||||
|
# @example -ol high
|
||||||
|
PAR_OPTS =
|
||||||
|
|
||||||
|
# Options for the BitGen tool
|
||||||
|
# @example -g Compress (compress bitstream)
|
||||||
|
# @example -g StartupClk:Cclk (specify the startup clock to onboard clock)
|
||||||
|
# @example -g StartupClk:JtagClk (specify the startup clock to JTAG clock)
|
||||||
|
BITGEN_OPTS = -g StartupClk:JtagClk
|
||||||
|
|
||||||
|
# Options for the Trace tool
|
||||||
|
# TRACE_OPTS =
|
||||||
|
|
||||||
|
# Options for the Fuse tool
|
||||||
|
# FUSE_OPTS =
|
||||||
|
|
||||||
|
# Options for the ISim simulator
|
||||||
|
# @example -gui (start the simulator in GUI mode)
|
||||||
|
# ISIM_OPTS =
|
||||||
|
|
||||||
|
# Options for the ISim batch file
|
||||||
|
# @example vcd dumpfile $@.vcd \n vcd dumpvars -m /UUT \n run all \n vcd dumpflush \n quit
|
||||||
|
# ISIM_CMD =
|
||||||
|
|
||||||
|
## ## ## ## ## ## ## ##
|
||||||
|
# ---------------------
|
||||||
|
|
||||||
|
## Programmer settings.. ##
|
||||||
|
|
||||||
|
# The programmer to use
|
||||||
|
# @example impact | digilent | xc3sprog
|
||||||
|
# @remark impact is the default Xilinx programmer and you must create a impact.cmd file in the root directory..
|
||||||
|
PROGRAMMER =
|
||||||
|
|
||||||
|
## Digilent JTAG cable settings
|
||||||
|
|
||||||
|
# @remark Use the `djtgcfg enum` command to list all available devices
|
||||||
|
# DJTG_DEVICE = DOnbUsb
|
||||||
|
|
||||||
|
# The index of the JTAG device for the `prog` target
|
||||||
|
# DJTG_INDEX = 0
|
||||||
|
|
||||||
|
# The index of the flash device for the `flash` target
|
||||||
|
# DJTG_FLASH_INDEX = 1
|
||||||
|
|
||||||
|
## ## ## ## ## ## ## ##
|
||||||
|
# ---------------------
|
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
toml
|
||||||
|
pytest
|
||||||
|
doit
|
14
tasks/generate_prj.py
Normal file
14
tasks/generate_prj.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import os
|
||||||
|
from tools.config import get_vhdl_sources_and_tests
|
||||||
|
from tools.paths import REL_FROM_WORKING_TO_ROOT as REL_ROOT, ROOT, WORKING
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def generate_prj(prj_path: Path, *_):
|
||||||
|
sources, _ = get_vhdl_sources_and_tests()
|
||||||
|
prj_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
with prj_path.open("w", encoding="utf-8") as f:
|
||||||
|
for libname, files in sources.items():
|
||||||
|
for file in files:
|
||||||
|
rel_path = os.path.relpath(ROOT / file, WORKING)
|
||||||
|
f.write(f"vhdl {libname} {rel_path}\n")
|
33
tasks/generate_scr.py
Normal file
33
tasks/generate_scr.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
from tools.config import parse_project_cfg
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def generate_scr(scr_path: Path, cfg_path: Path):
|
||||||
|
cfg = parse_project_cfg(cfg_path)
|
||||||
|
|
||||||
|
project = cfg["PROJECT"]
|
||||||
|
top = cfg.get("TOPLEVEL", project)
|
||||||
|
target_part = cfg["TARGET_PART"]
|
||||||
|
xst_opts = cfg.get("XST_OPTS", "")
|
||||||
|
|
||||||
|
# SCR-Datei vollständig in einer Zeile, wie im Makefile
|
||||||
|
scr_parts = [
|
||||||
|
"run",
|
||||||
|
f"-ifn {project}.prj",
|
||||||
|
f"-ofn {project}.ngc",
|
||||||
|
"-ifmt mixed",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Optional: Optionen aus XST_OPTS hinzufügen
|
||||||
|
if xst_opts.strip():
|
||||||
|
scr_parts += xst_opts.strip().split()
|
||||||
|
|
||||||
|
scr_parts += [
|
||||||
|
f"-top {top}",
|
||||||
|
"-ofmt NGC",
|
||||||
|
f"-p {target_part}",
|
||||||
|
]
|
||||||
|
|
||||||
|
scr_line = " ".join(scr_parts).replace("\n", " ").replace("\r", " ")
|
||||||
|
scr_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
scr_path.write_text(scr_line, encoding="utf-8")
|
32
tasks/run_xst.py
Normal file
32
tasks/run_xst.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import subprocess
|
||||||
|
from tools.paths import WORKING, REPORTS, PROJECT_CFG
|
||||||
|
from tools.config import parse_project_cfg
|
||||||
|
from tools.defaults import with_defaults
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def run_xst():
|
||||||
|
cfg = with_defaults(parse_project_cfg(PROJECT_CFG))
|
||||||
|
|
||||||
|
project = cfg["PROJECT"]
|
||||||
|
scr_file = f"{project}.scr"
|
||||||
|
scr_path = WORKING / scr_file
|
||||||
|
xilinx_path = Path(cfg["XILINX"])
|
||||||
|
xilinx_platform = "lin64" # TODO: Optional dynamisch machen
|
||||||
|
xst_exe = xilinx_path / "bin" / xilinx_platform / "xst"
|
||||||
|
common_opts = cfg.get("COMMON_OPTS", "")
|
||||||
|
|
||||||
|
if not xst_exe.exists():
|
||||||
|
raise FileNotFoundError(f"xst executable not found at {xst_exe}")
|
||||||
|
|
||||||
|
if not scr_path.exists():
|
||||||
|
raise FileNotFoundError(f"SCR file not found: {scr_path}")
|
||||||
|
|
||||||
|
print(f"\n============ Running XST ============\n")
|
||||||
|
print(f"> {xst_exe} {common_opts} -ifn {scr_file}\n")
|
||||||
|
|
||||||
|
subprocess.run(
|
||||||
|
[str(xst_exe), *common_opts.split(), "-ifn", scr_file],
|
||||||
|
cwd=WORKING,
|
||||||
|
check=True
|
||||||
|
)
|
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
72
tests/test_config.py
Normal file
72
tests/test_config.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
from tools.config import parse_vhdl_ls_toml
|
||||||
|
from pathlib import Path
|
||||||
|
import tempfile
|
||||||
|
import textwrap
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
def test_parsing_skips_third_party_libs():
|
||||||
|
content = textwrap.dedent("""
|
||||||
|
[libraries.lib]
|
||||||
|
files = ["src/main.vhd"]
|
||||||
|
|
||||||
|
[libraries.XILINX]
|
||||||
|
files = ["/opt/Xilinx/abc.vhd"]
|
||||||
|
is_third_party = true
|
||||||
|
""")
|
||||||
|
with tempfile.NamedTemporaryFile("w+", suffix=".toml", delete=False) as f:
|
||||||
|
f.write(content)
|
||||||
|
f.flush()
|
||||||
|
result = parse_vhdl_ls_toml(Path(f.name))
|
||||||
|
|
||||||
|
assert "work" in result
|
||||||
|
assert "XILINX" not in result
|
||||||
|
assert result["work"] == [Path("src/main.vhd")]
|
||||||
|
|
||||||
|
|
||||||
|
def test_parsing_multiple_libraries():
|
||||||
|
content = textwrap.dedent("""
|
||||||
|
[libraries.lib]
|
||||||
|
files = ["src/foo.vhd", "src/bar.vhd"]
|
||||||
|
|
||||||
|
[libraries.myip]
|
||||||
|
files = ["ipcore/top.vhd"]
|
||||||
|
""")
|
||||||
|
with tempfile.NamedTemporaryFile("w+", suffix=".toml", delete=False) as f:
|
||||||
|
f.write(content)
|
||||||
|
f.flush()
|
||||||
|
result = parse_vhdl_ls_toml(Path(f.name))
|
||||||
|
|
||||||
|
assert "work" in result
|
||||||
|
assert "myip" in result
|
||||||
|
assert result["work"] == [Path("src/foo.vhd"), Path("src/bar.vhd")]
|
||||||
|
assert result["myip"] == [Path("ipcore/top.vhd")]
|
||||||
|
|
||||||
|
|
||||||
|
def test_empty_toml_file():
|
||||||
|
with tempfile.NamedTemporaryFile("w+", suffix=".toml", delete=False) as f:
|
||||||
|
f.write("") # Empty file
|
||||||
|
f.flush()
|
||||||
|
result = parse_vhdl_ls_toml(Path(f.name))
|
||||||
|
|
||||||
|
assert result == {}
|
||||||
|
|
||||||
|
|
||||||
|
def test_missing_file_raises_error():
|
||||||
|
with pytest.raises(FileNotFoundError):
|
||||||
|
parse_vhdl_ls_toml(Path("nonexistent.toml"))
|
||||||
|
|
||||||
|
|
||||||
|
def test_libname_translation_to_work():
|
||||||
|
content = textwrap.dedent("""
|
||||||
|
[libraries.lib]
|
||||||
|
files = ["src/xyz.vhd"]
|
||||||
|
""")
|
||||||
|
with tempfile.NamedTemporaryFile("w+", suffix=".toml", delete=False) as f:
|
||||||
|
f.write(content)
|
||||||
|
f.flush()
|
||||||
|
result = parse_vhdl_ls_toml(Path(f.name))
|
||||||
|
|
||||||
|
assert "work" in result
|
||||||
|
assert "lib" not in result
|
||||||
|
assert result["work"] == [Path("src/xyz.vhd")]
|
47
tests/test_generate_prj.py
Normal file
47
tests/test_generate_prj.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
from tasks.generate_prj import generate_prj
|
||||||
|
|
||||||
|
|
||||||
|
def write_file(path: Path, content: str):
|
||||||
|
path.write_text(content.strip(), encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def test_generate_prj_single_lib(tmp_path):
|
||||||
|
cfg_path = tmp_path / "project.cfg"
|
||||||
|
toml_path = tmp_path / "vhdl_ls.toml"
|
||||||
|
prj_path = tmp_path / "working" / "MyProject.prj"
|
||||||
|
|
||||||
|
write_file(cfg_path, "PROJECT = MyProject\nTARGET_PART = dummy\nXILINX = /some/path")
|
||||||
|
write_file(toml_path, """
|
||||||
|
[libraries.lib]
|
||||||
|
files = ["src/main.vhd"]
|
||||||
|
""")
|
||||||
|
|
||||||
|
generate_prj(prj_path, cfg_path, toml_path)
|
||||||
|
|
||||||
|
assert prj_path.exists()
|
||||||
|
content = prj_path.read_text()
|
||||||
|
assert "vhdl work ../src/main.vhd" in content
|
||||||
|
|
||||||
|
|
||||||
|
def test_generate_prj_multiple_libs(tmp_path):
|
||||||
|
cfg_path = tmp_path / "project.cfg"
|
||||||
|
toml_path = tmp_path / "vhdl_ls.toml"
|
||||||
|
prj_path = tmp_path / "working" / "MyProject.prj"
|
||||||
|
|
||||||
|
write_file(cfg_path, "PROJECT = MyProject\nTARGET_PART = dummy\nXILINX = /some/path")
|
||||||
|
write_file(toml_path, """
|
||||||
|
[libraries.lib]
|
||||||
|
files = ["src/A.vhd", "src/B.vhd"]
|
||||||
|
|
||||||
|
[libraries.otherlib]
|
||||||
|
files = ["src/C.vhd"]
|
||||||
|
""")
|
||||||
|
|
||||||
|
generate_prj(prj_path, cfg_path, toml_path)
|
||||||
|
|
||||||
|
content = prj_path.read_text()
|
||||||
|
assert "vhdl work ../src/A.vhd" in content
|
||||||
|
assert "vhdl work ../src/B.vhd" in content
|
||||||
|
assert "vhdl otherlib ../src/C.vhd" in content
|
44
tests/test_generate_scr.py
Normal file
44
tests/test_generate_scr.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
from tasks.generate_scr import generate_scr
|
||||||
|
|
||||||
|
|
||||||
|
def write_file(path: Path, content: str):
|
||||||
|
path.write_text(content.strip(), encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def test_generate_scr_basic(tmp_path):
|
||||||
|
cfg_path = tmp_path / "project.cfg"
|
||||||
|
scr_path = tmp_path / "working" / "MyProject.scr"
|
||||||
|
|
||||||
|
write_file(cfg_path, """
|
||||||
|
PROJECT = MyProject
|
||||||
|
TARGET_PART = xc3s50-4-pq208
|
||||||
|
XILINX = /some/path
|
||||||
|
""")
|
||||||
|
|
||||||
|
generate_scr(scr_path, cfg_path)
|
||||||
|
|
||||||
|
content = scr_path.read_text()
|
||||||
|
assert "-ifn MyProject.prj" in content
|
||||||
|
assert "-ofn MyProject.ngc" in content
|
||||||
|
assert "-top MyProject" in content
|
||||||
|
assert "-p xc3s50-4-pq208" in content
|
||||||
|
|
||||||
|
|
||||||
|
def test_generate_scr_with_top_and_opts(tmp_path):
|
||||||
|
cfg_path = tmp_path / "project.cfg"
|
||||||
|
scr_path = tmp_path / "working" / "MyProject.scr"
|
||||||
|
|
||||||
|
write_file(cfg_path, """
|
||||||
|
PROJECT = MyProject
|
||||||
|
TARGET_PART = xc3s200-5-ft256
|
||||||
|
TOPLEVEL = TopModule
|
||||||
|
XST_OPTS = -opt_mode Speed -opt_level 2
|
||||||
|
XILINX = /some/path
|
||||||
|
""")
|
||||||
|
|
||||||
|
generate_scr(scr_path, cfg_path)
|
||||||
|
|
||||||
|
content = scr_path.read_text()
|
||||||
|
assert "-top TopModule" in content
|
||||||
|
assert "-opt_mode Speed -opt_level 2" in content
|
51
tests/test_project_cfg.py
Normal file
51
tests/test_project_cfg.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
from build.tools.config import parse_project_cfg
|
||||||
|
from pathlib import Path
|
||||||
|
import tempfile
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_complex_project_cfg_correctly():
|
||||||
|
cfg = textwrap.dedent("""
|
||||||
|
## Main settings.. ##
|
||||||
|
PROJECT = SpriteChannel
|
||||||
|
TARGET_PART = xc3s1200e-4-fg320
|
||||||
|
XILINX = /opt/Xilinx/14.7/ISE_DS/ISE
|
||||||
|
TOPLEVEL = SpriteChannel
|
||||||
|
CONSTRAINTS = src/SpriteChannel.ucf
|
||||||
|
|
||||||
|
# Sources (should be ignored)
|
||||||
|
VHDSOURCE += src/GenericCounter.vhd
|
||||||
|
VSOURCE += src/something.v
|
||||||
|
VHDTEST += test/SpriteChannel_tb.vhd
|
||||||
|
|
||||||
|
# Options
|
||||||
|
XST_OPTS += -opt_mode Speed
|
||||||
|
XST_OPTS += -opt_level 2
|
||||||
|
MAP_OPTS = -detail -timing -ol high
|
||||||
|
PAR_OPTS = -ol high
|
||||||
|
BITGEN_OPTS = -g StartupClk:JtagClk
|
||||||
|
|
||||||
|
# Programmer
|
||||||
|
PROGRAMMER = digilent
|
||||||
|
""")
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile("w+", suffix=".cfg", delete=False) as f:
|
||||||
|
f.write(cfg)
|
||||||
|
f.flush()
|
||||||
|
result = parse_project_cfg(Path(f.name))
|
||||||
|
|
||||||
|
assert result["PROJECT"] == "SpriteChannel"
|
||||||
|
assert result["TARGET_PART"] == "xc3s1200e-4-fg320"
|
||||||
|
assert result["XILINX"] == "/opt/Xilinx/14.7/ISE_DS/ISE"
|
||||||
|
assert result["TOPLEVEL"] == "SpriteChannel"
|
||||||
|
assert result["CONSTRAINTS"] == "src/SpriteChannel.ucf"
|
||||||
|
assert result["XST_OPTS"] == "-opt_mode Speed -opt_level 2"
|
||||||
|
assert result["MAP_OPTS"] == "-detail -timing -ol high"
|
||||||
|
assert result["PAR_OPTS"] == "-ol high"
|
||||||
|
assert result["BITGEN_OPTS"] == "-g StartupClk:JtagClk"
|
||||||
|
assert result["PROGRAMMER"] == "digilent"
|
||||||
|
|
||||||
|
# Sicherstellen, dass SOURCE-Felder ignoriert wurden
|
||||||
|
assert "VHDSOURCE" not in result
|
||||||
|
assert "VSOURCE" not in result
|
||||||
|
assert "VHDTEST" not in result
|
0
tools/__init__.py
Normal file
0
tools/__init__.py
Normal file
88
tools/config.py
Normal file
88
tools/config.py
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
from tools.paths import ROOT, PROJECT_CFG, VHDL_LS_TOML
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
import toml
|
||||||
|
import re
|
||||||
|
from typing import Tuple
|
||||||
|
from tools.defaults import with_defaults
|
||||||
|
|
||||||
|
def parse_vhdl_ls_toml(toml_path: Optional[Path] = None) -> Dict[str, List[Path]]:
|
||||||
|
if toml_path is None:
|
||||||
|
toml_path = VHDL_LS_TOML
|
||||||
|
|
||||||
|
if not toml_path.exists():
|
||||||
|
raise FileNotFoundError(f"{toml_path} not found.")
|
||||||
|
|
||||||
|
with open(toml_path, "r", encoding="utf-8") as f:
|
||||||
|
raw = toml.load(f)
|
||||||
|
|
||||||
|
libraries = raw.get("libraries", {})
|
||||||
|
parsed: Dict[str, List[Path]] = {}
|
||||||
|
|
||||||
|
for libname, content in libraries.items():
|
||||||
|
if content.get("is_third_party", False):
|
||||||
|
continue
|
||||||
|
files = content.get("files", [])
|
||||||
|
actual_libname = "work" if libname == "lib" else libname
|
||||||
|
parsed[actual_libname] = [Path(f) for f in files]
|
||||||
|
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
|
||||||
|
def parse_project_cfg(cfg_path: Optional[Path] = None) -> Dict[str, str]:
|
||||||
|
cfg_path = cfg_path or PROJECT_CFG
|
||||||
|
|
||||||
|
if not cfg_path.exists():
|
||||||
|
raise FileNotFoundError(f"{cfg_path} not found.")
|
||||||
|
|
||||||
|
result: Dict[str, str] = {}
|
||||||
|
ignored_keys = {"VHDSOURCE", "VSOURCE", "VHDTEST", "VTEST"}
|
||||||
|
|
||||||
|
with cfg_path.open("r", encoding="utf-8") as f:
|
||||||
|
for line in f:
|
||||||
|
line = line.strip()
|
||||||
|
|
||||||
|
# Kommentare und Leerzeilen überspringen
|
||||||
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if "+=" in line:
|
||||||
|
key, value = map(str.strip, line.split("+=", 1))
|
||||||
|
if key in ignored_keys:
|
||||||
|
continue
|
||||||
|
if key in result:
|
||||||
|
result[key] += f" {value}"
|
||||||
|
else:
|
||||||
|
result[key] = value
|
||||||
|
elif "=" in line:
|
||||||
|
key, value = map(str.strip, line.split("=", 1))
|
||||||
|
if key in ignored_keys:
|
||||||
|
continue
|
||||||
|
result[key] = value
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_vhdl_sources_and_tests() -> Tuple[Dict[str, List[Path]], Dict[str, List[Path]]]:
|
||||||
|
cfg = with_defaults(parse_project_cfg())
|
||||||
|
test_filter = cfg.get("TEST_FILTER", r"^tests/|_tb\.vhd$") # Default-Fallback
|
||||||
|
|
||||||
|
try:
|
||||||
|
pattern = re.compile(test_filter)
|
||||||
|
except re.error as e:
|
||||||
|
raise ValueError(f"Invalid TEST_FILTER regex: {e}")
|
||||||
|
|
||||||
|
all_sources = parse_vhdl_ls_toml()
|
||||||
|
|
||||||
|
normal_sources: Dict[str, List[Path]] = {}
|
||||||
|
test_sources: Dict[str, List[Path]] = {}
|
||||||
|
|
||||||
|
for lib, files in all_sources.items():
|
||||||
|
for file in files:
|
||||||
|
# relative Pfade als Strings prüfen
|
||||||
|
rel_path = str(file)
|
||||||
|
if pattern.search(rel_path):
|
||||||
|
test_sources.setdefault(lib, []).append(file)
|
||||||
|
else:
|
||||||
|
normal_sources.setdefault(lib, []).append(file)
|
||||||
|
|
||||||
|
return normal_sources, test_sources
|
43
tools/defaults.py
Normal file
43
tools/defaults.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# tools/defaults.py
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
def get_default_config() -> Dict[str, str]:
|
||||||
|
return {
|
||||||
|
"TOPLEVEL": "", # wird dynamisch ersetzt
|
||||||
|
"CONSTRAINTS": "",
|
||||||
|
"BUILD_DIR": "working",
|
||||||
|
"REPORT_DIR": "reports",
|
||||||
|
"COMMON_OPTS": "-intstyle xflow",
|
||||||
|
"XST_OPTS": "",
|
||||||
|
"NGDBUILD_OPTS": "",
|
||||||
|
"MAP_OPTS": "-detail",
|
||||||
|
"PAR_OPTS": "",
|
||||||
|
"BITGEN_OPTS": "",
|
||||||
|
"TRACE_OPTS": "-v 3 -n 3",
|
||||||
|
"FUSE_OPTS": "-incremental",
|
||||||
|
"ISIM_OPTS": "-gui",
|
||||||
|
"ISIM_CMD": "",
|
||||||
|
"PROGRAMMER": "none",
|
||||||
|
"PROGRAMMER_PRE": "",
|
||||||
|
"IMPACT_OPTS": "-batch impact.cmd",
|
||||||
|
"DJTG_EXE": "djtgcfg",
|
||||||
|
"DJTG_DEVICE": "DJTG_DEVICE-NOT-SET",
|
||||||
|
"DJTG_INDEX": "0",
|
||||||
|
"DJTG_FLASH_INDEX": "1",
|
||||||
|
"XC3SPROG_EXE": "xc3sprog",
|
||||||
|
"XC3SPROG_CABLE": "none",
|
||||||
|
"XC3SPROG_OPTS": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
def with_defaults(project_cfg: Dict[str, str]) -> Dict[str, str]:
|
||||||
|
merged = get_default_config()
|
||||||
|
merged.update(project_cfg)
|
||||||
|
|
||||||
|
# Fallbacks sicherstellen (kein leerer oder None-Wert bleibt erhalten)
|
||||||
|
if not merged.get("TOPLEVEL"):
|
||||||
|
merged["TOPLEVEL"] = merged.get("PROJECT", "")
|
||||||
|
if not merged.get("CONSTRAINTS"):
|
||||||
|
merged["CONSTRAINTS"] = f"{merged.get('PROJECT', '')}.ucf"
|
||||||
|
|
||||||
|
# Alle Felder final auf gültigen string casten (zur Sicherheit)
|
||||||
|
return {k: (v if isinstance(v, str) else str(v)) for k, v in merged.items()}
|
21
tools/paths.py
Normal file
21
tools/paths.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Ermittle absoluten Pfad dieser Datei
|
||||||
|
HERE = Path(__file__).resolve()
|
||||||
|
|
||||||
|
# Gehe zurück von tools/ → build/ → Projekt-Root
|
||||||
|
ROOT = HERE.parent.parent.parent
|
||||||
|
|
||||||
|
# Abgeleitete Pfade
|
||||||
|
BUILD = ROOT / "build"
|
||||||
|
WORKING = BUILD / "working"
|
||||||
|
REPORTS = BUILD / "reports"
|
||||||
|
|
||||||
|
# Relativer Pfad von WORKING zurück zur Projektwurzel
|
||||||
|
REL_FROM_WORKING_TO_ROOT = Path(os.path.relpath(ROOT, WORKING))
|
||||||
|
|
||||||
|
# Standard-Konfigurationsdateien
|
||||||
|
PROJECT_CFG = ROOT / "project.cfg"
|
||||||
|
VHDL_LS_TOML = ROOT / "vhdl_ls.toml"
|
Reference in New Issue
Block a user