opencos-eda 0.3.9__py3-none-any.whl → 0.3.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opencos/commands/deps_help.py +89 -120
- opencos/commands/export.py +7 -2
- opencos/commands/multi.py +3 -3
- opencos/commands/sim.py +14 -16
- opencos/commands/synth.py +1 -2
- opencos/commands/upload.py +192 -4
- opencos/commands/waves.py +8 -8
- opencos/deps/deps_commands.py +6 -6
- opencos/deps/deps_file.py +82 -79
- opencos/deps/deps_processor.py +129 -50
- opencos/docs/Architecture.md +45 -0
- opencos/docs/ConnectingApps.md +29 -0
- opencos/docs/DEPS.md +199 -0
- opencos/docs/Debug.md +77 -0
- opencos/docs/DirectoryStructure.md +22 -0
- opencos/docs/Installation.md +117 -0
- opencos/docs/OcVivadoTcl.md +63 -0
- opencos/docs/OpenQuestions.md +7 -0
- opencos/docs/README.md +13 -0
- opencos/docs/RtlCodingStyle.md +54 -0
- opencos/docs/eda.md +147 -0
- opencos/docs/oc_cli.md +135 -0
- opencos/eda.py +175 -41
- opencos/eda_base.py +180 -50
- opencos/eda_config.py +62 -16
- opencos/eda_config_defaults.yml +21 -4
- opencos/eda_deps_bash_completion.bash +37 -15
- opencos/files.py +26 -1
- opencos/tools/cocotb.py +5 -5
- opencos/tools/invio.py +2 -2
- opencos/tools/invio_yosys.py +2 -1
- opencos/tools/iverilog.py +3 -3
- opencos/tools/quartus.py +113 -115
- opencos/tools/questa_common.py +3 -4
- opencos/tools/riviera.py +3 -3
- opencos/tools/slang.py +11 -7
- opencos/tools/slang_yosys.py +1 -0
- opencos/tools/surelog.py +4 -3
- opencos/tools/verilator.py +5 -4
- opencos/tools/vivado.py +307 -176
- opencos/tools/yosys.py +4 -4
- opencos/util.py +6 -3
- opencos/utils/dict_helpers.py +31 -0
- opencos/utils/markup_helpers.py +2 -2
- opencos/utils/str_helpers.py +7 -0
- opencos/utils/subprocess_helpers.py +3 -3
- opencos/utils/vscode_helper.py +2 -2
- opencos/utils/vsim_helper.py +58 -22
- {opencos_eda-0.3.9.dist-info → opencos_eda-0.3.11.dist-info}/METADATA +1 -1
- opencos_eda-0.3.11.dist-info/RECORD +94 -0
- {opencos_eda-0.3.9.dist-info → opencos_eda-0.3.11.dist-info}/entry_points.txt +1 -0
- opencos/tests/__init__.py +0 -0
- opencos/tests/custom_config.yml +0 -13
- opencos/tests/deps_files/command_order/DEPS.yml +0 -44
- opencos/tests/deps_files/error_msgs/DEPS.yml +0 -55
- opencos/tests/deps_files/iverilog_test/DEPS.yml +0 -4
- opencos/tests/deps_files/no_deps_here/DEPS.yml +0 -0
- opencos/tests/deps_files/non_sv_reqs/DEPS.yml +0 -50
- opencos/tests/deps_files/tags_with_tools/DEPS.yml +0 -54
- opencos/tests/deps_files/test_err_fatal/DEPS.yml +0 -4
- opencos/tests/helpers.py +0 -354
- opencos/tests/test_build.py +0 -12
- opencos/tests/test_deps_helpers.py +0 -207
- opencos/tests/test_deps_schema.py +0 -30
- opencos/tests/test_eda.py +0 -921
- opencos/tests/test_eda_elab.py +0 -110
- opencos/tests/test_eda_synth.py +0 -150
- opencos/tests/test_oc_cli.py +0 -25
- opencos/tests/test_tools.py +0 -404
- opencos_eda-0.3.9.dist-info/RECORD +0 -99
- {opencos_eda-0.3.9.dist-info → opencos_eda-0.3.11.dist-info}/WHEEL +0 -0
- {opencos_eda-0.3.9.dist-info → opencos_eda-0.3.11.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.3.9.dist-info → opencos_eda-0.3.11.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.3.9.dist-info → opencos_eda-0.3.11.dist-info}/top_level.txt +0 -0
|
@@ -1,14 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
|
|
3
3
|
# How to use?
|
|
4
|
-
# 1)
|
|
4
|
+
# 1) run:
|
|
5
|
+
# eda_show_autocomplete
|
|
6
|
+
# for instructions, will likely show you the location of this file,
|
|
7
|
+
# eda_deps_bash_completion.bash, for you to source. Does not require uv.
|
|
8
|
+
#
|
|
9
|
+
# 2) Given the result from (1), and if you use uv, add the following to your
|
|
10
|
+
# ~/.bashrc:
|
|
11
|
+
# # Make sure 'eda' is a valid executable when not in a venv:
|
|
12
|
+
# if ! type -P "eda" &>/dev/null; then
|
|
13
|
+
# uv tool install --python 3.14 opencos-eda >/dev/null 2>&1
|
|
14
|
+
# echo "uv tool installed opencos-eda"
|
|
15
|
+
# fi
|
|
16
|
+
# if [ -f PATH-FROM-STEP-1 ]; then
|
|
17
|
+
# . PATH-FROM-STEP-1
|
|
18
|
+
# fi
|
|
19
|
+
#
|
|
20
|
+
# 3) copy this script locally and source it.
|
|
5
21
|
# For example:
|
|
6
22
|
# > source ~/sh/eda_deps_bash_completion.bash
|
|
7
|
-
# You can put this in your .bashrc.
|
|
8
|
-
#
|
|
9
|
-
#
|
|
23
|
+
# You can put this in your .bashrc. Note you will need a venv active or
|
|
24
|
+
# "eda" isn't in your path yet.
|
|
25
|
+
#
|
|
26
|
+
# 4) Have it sourced when you start your venv. Note this doesn't play as nicely
|
|
27
|
+
# with "uv" due to having a less stable .venv, but you can add this to your
|
|
28
|
+
# VENV_NAME/bin/activate script:
|
|
29
|
+
# (bottom of activate script, assuming python3.XX):
|
|
10
30
|
# script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
|
11
|
-
# . $script_dir/../lib/python3.
|
|
31
|
+
# . $script_dir/../lib/python3.XX/site-packages/opencos/eda_deps_bash_completion.bash
|
|
12
32
|
|
|
13
33
|
|
|
14
34
|
# scripts via pyproject.toml:
|
|
@@ -17,6 +37,15 @@ SCRIPT_NAME="eda"
|
|
|
17
37
|
# how we get the completion targets:
|
|
18
38
|
EXTRACTION_SCRIPT_NAME="eda_targets"
|
|
19
39
|
|
|
40
|
+
EDA_WORDS="sim lint elab synth flist proj multi tools-multi sweep build \
|
|
41
|
+
waves upload open export shell targets lec \
|
|
42
|
+
+define+ +incdirs+ \
|
|
43
|
+
--help --quiet --verbose --debug \
|
|
44
|
+
--tool --seed --top --keep --force --fake --lint --work-dir \
|
|
45
|
+
--stop-before-compile --stop-after-compile --stop-before-elaborate \
|
|
46
|
+
--export --export-run --export-json \
|
|
47
|
+
"
|
|
48
|
+
|
|
20
49
|
_eda_script_completion() {
|
|
21
50
|
|
|
22
51
|
# Set up for additional completions
|
|
@@ -28,22 +57,15 @@ _eda_script_completion() {
|
|
|
28
57
|
if [[ $(type -P "$EXTRACTION_SCRIPT_NAME") ]]; then
|
|
29
58
|
keys=$("$EXTRACTION_SCRIPT_NAME" "$cur")
|
|
30
59
|
if [[ -n "$keys" ]]; then
|
|
31
|
-
completions=($(compgen -W "$keys" -- "$cur"))
|
|
60
|
+
completions=($(compgen -W "$keys $EDA_WORDS" -- "$cur"))
|
|
32
61
|
fi
|
|
33
62
|
fi
|
|
34
63
|
|
|
35
64
|
if [ -z "${completions}" ]; then
|
|
36
65
|
# If we didn't find anything in a DEPS.[yml|yaml|toml|json], then use:
|
|
37
|
-
#
|
|
38
|
-
eda_words="multi sim elab flist build synth waves proj waves targets \
|
|
39
|
-
+define+ +incdirs+ \
|
|
40
|
-
--help --quiet --verbose --debug \
|
|
41
|
-
--tool --seed --top --keep --force --fake --lint --work-dir \
|
|
42
|
-
--stop-before-compile --stop-after-compile --stop-before-elaborate \
|
|
43
|
-
--export --export-run --export-json \
|
|
44
|
-
"
|
|
66
|
+
# -- a bunch of known eda words or args.
|
|
45
67
|
# 2. a glob the current word to mimic normal bash:
|
|
46
|
-
completions=($(compgen -W "$
|
|
68
|
+
completions=($(compgen -W "$EDA_WORDS" -G "${cur}*" -- "$cur"))
|
|
47
69
|
fi
|
|
48
70
|
|
|
49
71
|
COMPREPLY=("${completions[@]}")
|
opencos/files.py
CHANGED
|
@@ -13,6 +13,7 @@ as part of a verilog $readmemh, etc)
|
|
|
13
13
|
'''
|
|
14
14
|
|
|
15
15
|
import os
|
|
16
|
+
import shutil
|
|
16
17
|
|
|
17
18
|
# Ways to force files not ending in .sv to be systemverilog (for tools
|
|
18
19
|
# that require -sv vs Verilog-2001'''
|
|
@@ -31,8 +32,12 @@ FORCE_PREFIX_DICT = {
|
|
|
31
32
|
|
|
32
33
|
ALL_FORCED_PREFIXES = set(list(FORCE_PREFIX_DICT.keys()))
|
|
33
34
|
|
|
34
|
-
def get_source_file(target:str) -> (bool, str, str):
|
|
35
|
+
def get_source_file(target: str) -> (bool, str, str):
|
|
35
36
|
'''Returns tuple: bool if file exists, filepath str, and optional forced file type str'''
|
|
37
|
+
|
|
38
|
+
if '$' in target:
|
|
39
|
+
target = os.path.expandvars(target)
|
|
40
|
+
|
|
36
41
|
if os.path.isfile(target):
|
|
37
42
|
# target exists as a file, return True w/ original target:
|
|
38
43
|
return True, target, ''
|
|
@@ -47,3 +52,23 @@ def get_source_file(target:str) -> (bool, str, str):
|
|
|
47
52
|
|
|
48
53
|
# target or fpath didn't exist, return False with the original target:
|
|
49
54
|
return False, target, ''
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def safe_shutil_which(path: str) -> str:
|
|
58
|
+
'''Windows/WSL compatible Wrapper for shutil.which that checks for 'path':
|
|
59
|
+
|
|
60
|
+
- path
|
|
61
|
+
- path.exe
|
|
62
|
+
- path.bat
|
|
63
|
+
|
|
64
|
+
Returns full path str returned by shutil.which
|
|
65
|
+
'''
|
|
66
|
+
for ext in ('', '.exe', 'bat'):
|
|
67
|
+
if found := shutil.which(f'{path}{ext}'):
|
|
68
|
+
return found
|
|
69
|
+
return ''
|
|
70
|
+
|
|
71
|
+
# Note that in Windows, 'python' will return the venv or uv version, 'python3' will
|
|
72
|
+
# return the installed version (which may not be what you want), so we'll prefer
|
|
73
|
+
# 'python':
|
|
74
|
+
PY_EXE = safe_shutil_which('python') or safe_shutil_which('python3')
|
opencos/tools/cocotb.py
CHANGED
|
@@ -4,12 +4,12 @@ Contains classes for ToolCocotb, CommandSimCocotb.
|
|
|
4
4
|
'''
|
|
5
5
|
|
|
6
6
|
import os
|
|
7
|
-
import shutil
|
|
8
7
|
import subprocess
|
|
9
8
|
|
|
10
9
|
from opencos import util
|
|
11
|
-
from opencos.eda_base import Tool
|
|
12
10
|
from opencos.commands import CommandSim
|
|
11
|
+
from opencos.eda_base import Tool
|
|
12
|
+
from opencos.files import safe_shutil_which, PY_EXE
|
|
13
13
|
from opencos.utils import status_constants
|
|
14
14
|
from opencos.utils.str_helpers import sanitize_defines_for_sh
|
|
15
15
|
from opencos.tools import verilator # For default waivers.
|
|
@@ -30,7 +30,7 @@ class ToolCocotb(Tool):
|
|
|
30
30
|
return self._VERSION
|
|
31
31
|
|
|
32
32
|
# Check if python is available
|
|
33
|
-
python_path =
|
|
33
|
+
python_path = PY_EXE
|
|
34
34
|
if not python_path:
|
|
35
35
|
self.error('"python" or "python3" not in path, required for cocotb')
|
|
36
36
|
else:
|
|
@@ -128,7 +128,7 @@ class CommandSimCocotb(CommandSim, ToolCocotb):
|
|
|
128
128
|
util.warning('--cocotb-simulator is not set, a simulation cannot be run with'
|
|
129
129
|
'this arg value')
|
|
130
130
|
return
|
|
131
|
-
exe =
|
|
131
|
+
exe = safe_shutil_which(simulator)
|
|
132
132
|
if not exe:
|
|
133
133
|
util.warning(f'--cocotb-simulator={simulator}, {simulator} is not present in PATH',
|
|
134
134
|
'a simulation cannot be run with this arg value')
|
|
@@ -330,7 +330,7 @@ class CommandSimCocotb(CommandSim, ToolCocotb):
|
|
|
330
330
|
def _generate_runner_script_content(self, test_module: str, hdl_sources: list) -> str:
|
|
331
331
|
'''Generate the content for the Python runner script'''
|
|
332
332
|
|
|
333
|
-
if
|
|
333
|
+
if safe_shutil_which('verilator'):
|
|
334
334
|
# TODO(drew): this shortcuts if verilator is truly usable,
|
|
335
335
|
# consider using eda_tool_helper to get "tools_loaded", which
|
|
336
336
|
# is not set in self.config['tools_loaded'] when --tool=cocotb.
|
opencos/tools/invio.py
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
import importlib.util
|
|
6
6
|
|
|
7
7
|
from opencos import util
|
|
8
|
-
from opencos.tools import invio_helpers
|
|
9
|
-
from opencos.eda_base import Tool
|
|
10
8
|
from opencos.commands import CommandElab
|
|
9
|
+
from opencos.eda_base import Tool
|
|
10
|
+
from opencos.tools import invio_helpers
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
opencos/tools/invio_yosys.py
CHANGED
|
@@ -8,6 +8,7 @@ import os
|
|
|
8
8
|
import importlib.util
|
|
9
9
|
|
|
10
10
|
from opencos import util
|
|
11
|
+
from opencos.files import PY_EXE
|
|
11
12
|
from opencos.tools import invio_helpers
|
|
12
13
|
from opencos.tools.yosys import ToolYosys, CommonSynthYosys
|
|
13
14
|
|
|
@@ -100,7 +101,7 @@ class CommandSynthInvioYosys(CommonSynthYosys, ToolInvioYosys):
|
|
|
100
101
|
|
|
101
102
|
|
|
102
103
|
invio_command_list = util.ShellCommandList(
|
|
103
|
-
[
|
|
104
|
+
[PY_EXE, invio_dict['full_py_filename']], tee_fpath=invio_dict['full_py_filename']
|
|
104
105
|
)
|
|
105
106
|
|
|
106
107
|
# Optinally create and run a sta.f:
|
opencos/tools/iverilog.py
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
Contains classes for ToolIverilog CommandSimIverilog, CommandElabIverilog.
|
|
4
4
|
'''
|
|
5
5
|
|
|
6
|
-
import shutil
|
|
7
6
|
import subprocess
|
|
8
7
|
|
|
9
8
|
from opencos import util
|
|
10
|
-
from opencos.eda_base import Tool
|
|
11
9
|
from opencos.commands import CommandSim
|
|
10
|
+
from opencos.eda_base import Tool
|
|
11
|
+
from opencos.files import safe_shutil_which
|
|
12
12
|
from opencos.utils.str_helpers import sanitize_defines_for_sh
|
|
13
13
|
|
|
14
14
|
|
|
@@ -28,7 +28,7 @@ class ToolIverilog(Tool):
|
|
|
28
28
|
if self._VERSION:
|
|
29
29
|
return self._VERSION
|
|
30
30
|
|
|
31
|
-
iverilog_path =
|
|
31
|
+
iverilog_path = safe_shutil_which(self._EXE)
|
|
32
32
|
if iverilog_path is None:
|
|
33
33
|
self.error(f'"{self._EXE}" not in path, need to get it ({self._URL})')
|
|
34
34
|
else:
|
opencos/tools/quartus.py
CHANGED
|
@@ -9,16 +9,14 @@ Used for Intel FPGA synthesis, place & route, and bitstream generation.
|
|
|
9
9
|
import os
|
|
10
10
|
import re
|
|
11
11
|
import shlex
|
|
12
|
-
import shutil
|
|
13
12
|
import subprocess
|
|
14
|
-
|
|
15
13
|
from pathlib import Path
|
|
16
14
|
|
|
17
|
-
from opencos import util
|
|
18
|
-
from opencos.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
from opencos import util
|
|
16
|
+
from opencos.commands import CommandSynth, CommandBuild, CommandFList, CommandProj, \
|
|
17
|
+
CommandUpload, CommandOpen
|
|
18
|
+
from opencos.eda_base import Tool, get_eda_exec
|
|
19
|
+
from opencos.files import safe_shutil_which
|
|
22
20
|
from opencos.utils.str_helpers import sanitize_defines_for_sh, strip_outer_quotes
|
|
23
21
|
|
|
24
22
|
class ToolQuartus(Tool):
|
|
@@ -48,16 +46,17 @@ class ToolQuartus(Tool):
|
|
|
48
46
|
if self._VERSION:
|
|
49
47
|
return self._VERSION
|
|
50
48
|
|
|
51
|
-
path =
|
|
49
|
+
path = safe_shutil_which(self._EXE)
|
|
52
50
|
if not path:
|
|
53
51
|
self.error("Quartus not in path, need to install or add to $PATH",
|
|
54
52
|
f"(looked for '{self._EXE}')")
|
|
55
53
|
else:
|
|
56
54
|
self.quartus_exe = path
|
|
57
55
|
self.quartus_base_path, _ = os.path.split(path)
|
|
58
|
-
self.quartus_gui_exe = shutil.which('quartus') # vs quartus_sh
|
|
59
|
-
|
|
60
56
|
|
|
57
|
+
self.quartus_gui_exe = safe_shutil_which(
|
|
58
|
+
os.path.join(self.quartus_base_path, 'quartus') # vs quartus_sh
|
|
59
|
+
)
|
|
61
60
|
|
|
62
61
|
# Get version based on install path name or by running quartus_sh --version
|
|
63
62
|
util.debug(f"quartus path = {self.quartus_exe}")
|
|
@@ -151,7 +150,6 @@ class CommandSynthQuartus(CommandSynth, ToolQuartus):
|
|
|
151
150
|
self.write_tcl_file(tcl_file=tcl_file)
|
|
152
151
|
|
|
153
152
|
# execute Quartus synthesis
|
|
154
|
-
command_list_gui = [self.quartus_gui_exe, '-t', tcl_file]
|
|
155
153
|
command_list = [
|
|
156
154
|
self.quartus_exe, '-t', tcl_file
|
|
157
155
|
]
|
|
@@ -172,11 +170,7 @@ class CommandSynthQuartus(CommandSynth, ToolQuartus):
|
|
|
172
170
|
typ='text', description='Quartus Synthesis Report'
|
|
173
171
|
)
|
|
174
172
|
|
|
175
|
-
|
|
176
|
-
self.exec(self.args['work-dir'], command_list_gui)
|
|
177
|
-
else:
|
|
178
|
-
self.exec(self.args['work-dir'], command_list)
|
|
179
|
-
|
|
173
|
+
self.exec(self.args['work-dir'], command_list)
|
|
180
174
|
|
|
181
175
|
saved_qpf_filename = self.args["top"] + '.qpf'
|
|
182
176
|
if not os.path.isfile(os.path.join(self.args['work-dir'], saved_qpf_filename)):
|
|
@@ -185,8 +179,13 @@ class CommandSynthQuartus(CommandSynth, ToolQuartus):
|
|
|
185
179
|
|
|
186
180
|
util.info(f"Synthesis done, results are in: {self.args['work-dir']}")
|
|
187
181
|
|
|
188
|
-
# Note: in GUI mode, if
|
|
189
|
-
#
|
|
182
|
+
# Note: in GUI mode, if we were to run:
|
|
183
|
+
# ran: quaruts -t build.tcl
|
|
184
|
+
# it treats the tcl script as running "headless" as a pre-script, and won't open the
|
|
185
|
+
# GUI anyway, and will exit on completion,
|
|
186
|
+
# Instead we:
|
|
187
|
+
# 1. always run with quartus_sh, so text goes to stdout
|
|
188
|
+
# 2. we'll re-open the project in GUI mode, here:
|
|
190
189
|
if self.args['gui'] and self.quartus_gui_exe:
|
|
191
190
|
self.exec(
|
|
192
191
|
work_dir=self.args['work-dir'],
|
|
@@ -212,19 +211,22 @@ class CommandSynthQuartus(CommandSynth, ToolQuartus):
|
|
|
212
211
|
# Add source files (convert to relative paths and use forward slashes)
|
|
213
212
|
# Note that default of self.args['all-sv'] is False so we should have added
|
|
214
213
|
# all files to self.files_sv instead of files_v:
|
|
214
|
+
# Note that tcl uses POSIX paths, so \\ -> /
|
|
215
215
|
for f in self.files_v:
|
|
216
|
-
rel_path = os.path.relpath(f, self.args['work-dir']).
|
|
216
|
+
rel_path = Path(os.path.relpath(f, self.args['work-dir'])).as_posix()
|
|
217
217
|
tcl_lines.append(f"set_global_assignment -name VERILOG_FILE \"{rel_path}\"")
|
|
218
218
|
for f in self.files_sv:
|
|
219
|
-
rel_path = os.path.relpath(f, self.args['work-dir']).
|
|
219
|
+
rel_path = Path(os.path.relpath(f, self.args['work-dir'])).as_posix()
|
|
220
220
|
tcl_lines.append(f"set_global_assignment -name SYSTEMVERILOG_FILE \"{rel_path}\"")
|
|
221
221
|
for f in self.files_vhd:
|
|
222
|
-
rel_path = os.path.relpath(f, self.args['work-dir']).
|
|
222
|
+
rel_path = Path(os.path.relpath(f, self.args['work-dir'])).as_posix()
|
|
223
223
|
tcl_lines.append(f"set_global_assignment -name VHDL_FILE \"{rel_path}\"")
|
|
224
224
|
|
|
225
225
|
# Add include directories - Quartus needs the base directory where "lib/" can be found
|
|
226
226
|
for incdir in self.incdirs:
|
|
227
|
-
tcl_lines.append(
|
|
227
|
+
tcl_lines.append(
|
|
228
|
+
f"set_global_assignment -name SEARCH_PATH \"{Path(incdir).as_posix()}\""
|
|
229
|
+
)
|
|
228
230
|
|
|
229
231
|
# Parameters --> set_parameter -name <Parameter_Name> <Value>
|
|
230
232
|
for k,v in self.parameters.items():
|
|
@@ -242,7 +244,7 @@ class CommandSynthQuartus(CommandSynth, ToolQuartus):
|
|
|
242
244
|
for incdir in self.incdirs:
|
|
243
245
|
if os.path.exists(incdir):
|
|
244
246
|
tcl_lines.append(
|
|
245
|
-
f"set_global_assignment -name USER_LIBRARIES \"{incdir}\""
|
|
247
|
+
f"set_global_assignment -name USER_LIBRARIES \"{Path(incdir).as_posix()}\""
|
|
246
248
|
)
|
|
247
249
|
|
|
248
250
|
# Add defines
|
|
@@ -268,7 +270,7 @@ class CommandSynthQuartus(CommandSynth, ToolQuartus):
|
|
|
268
270
|
for f in sdc_files:
|
|
269
271
|
for attr in ('SDC_FILE', 'SYN_SDC_FILE', 'RTL_SDC_FILE'):
|
|
270
272
|
tcl_lines.extend([
|
|
271
|
-
f"set_global_assignment -name {attr} \"{f}\""
|
|
273
|
+
f"set_global_assignment -name {attr} \"{Path(f).as_posix()}\""
|
|
272
274
|
])
|
|
273
275
|
tcl_lines.append("set_global_assignment -name SYNTH_TIMING_DRIVEN_SYNTHESIS ON")
|
|
274
276
|
|
|
@@ -331,7 +333,7 @@ class CommandBuildQuartus(CommandBuild, ToolQuartus):
|
|
|
331
333
|
f"design={self.args['design']}")
|
|
332
334
|
|
|
333
335
|
command_list = [
|
|
334
|
-
|
|
336
|
+
get_eda_exec('flist'), 'flist',
|
|
335
337
|
'--no-default-log',
|
|
336
338
|
'--tool=' + self.args['tool'],
|
|
337
339
|
'--force',
|
|
@@ -411,7 +413,7 @@ class CommandBuildQuartus(CommandBuild, ToolQuartus):
|
|
|
411
413
|
fname_abs = os.path.abspath(fname)
|
|
412
414
|
if not os.path.isfile(fname_abs):
|
|
413
415
|
self.error(f'add-tcl-files: "{fname_abs}"; does not exist')
|
|
414
|
-
build_tcl_lines.append(f'source {fname_abs}')
|
|
416
|
+
build_tcl_lines.append(f'source {Path(fname_abs).as_posix()}')
|
|
415
417
|
build_tcl_lines.append('')
|
|
416
418
|
|
|
417
419
|
# If we don't have any args for --flow-tcl-files, then use a default flow:
|
|
@@ -428,7 +430,7 @@ class CommandBuildQuartus(CommandBuild, ToolQuartus):
|
|
|
428
430
|
fname_abs = os.path.abspath(fname)
|
|
429
431
|
if not os.path.isfile(fname_abs):
|
|
430
432
|
self.error(f'flow-tcl-files: "{fname_abs}"; does not exist')
|
|
431
|
-
build_tcl_lines.append(f'source {fname_abs}')
|
|
433
|
+
build_tcl_lines.append(f'source {Path(fname_abs).as_posix()}')
|
|
432
434
|
build_tcl_lines.append('')
|
|
433
435
|
|
|
434
436
|
with open(build_tcl_file, 'w', encoding='utf-8') as ftcl:
|
|
@@ -552,20 +554,22 @@ class CommandProjQuartus(CommandProj, ToolQuartus):
|
|
|
552
554
|
f"set_global_assignment -name TOP_LEVEL_ENTITY {top}",
|
|
553
555
|
]
|
|
554
556
|
|
|
555
|
-
# Add source files
|
|
557
|
+
# Add source files, tcl prefers POSIX paths even in Windows Powershell.
|
|
556
558
|
for f in self.files_v:
|
|
557
|
-
rel_path = os.path.relpath(f, self.args['work-dir']).
|
|
559
|
+
rel_path = Path(os.path.relpath(f, self.args['work-dir'])).as_posix()
|
|
558
560
|
tcl_lines.append(f"set_global_assignment -name VERILOG_FILE \"{rel_path}\"")
|
|
559
561
|
for f in self.files_sv:
|
|
560
|
-
rel_path = os.path.relpath(f, self.args['work-dir']).
|
|
562
|
+
rel_path = Path(os.path.relpath(f, self.args['work-dir'])).as_posix()
|
|
561
563
|
tcl_lines.append(f"set_global_assignment -name SYSTEMVERILOG_FILE \"{rel_path}\"")
|
|
562
564
|
for f in self.files_vhd:
|
|
563
|
-
rel_path = os.path.relpath(f, self.args['work-dir']).
|
|
565
|
+
rel_path = Path(os.path.relpath(f, self.args['work-dir'])).as_posix()
|
|
564
566
|
tcl_lines.append(f"set_global_assignment -name VHDL_FILE \"{rel_path}\"")
|
|
565
567
|
|
|
566
568
|
# Add include directories
|
|
567
569
|
for incdir in self.incdirs:
|
|
568
|
-
tcl_lines.append(
|
|
570
|
+
tcl_lines.append(
|
|
571
|
+
f"set_global_assignment -name SEARCH_PATH \"{Path(incdir).as_posix()}\""
|
|
572
|
+
)
|
|
569
573
|
|
|
570
574
|
# Add defines
|
|
571
575
|
for key, value in self.defines.items():
|
|
@@ -575,9 +579,10 @@ class CommandProjQuartus(CommandProj, ToolQuartus):
|
|
|
575
579
|
tcl_lines.append(f"set_global_assignment -name VERILOG_MACRO \"{key}={value}\"")
|
|
576
580
|
|
|
577
581
|
# Add constraints if available
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
582
|
+
for sdc_file in self.files_sdc:
|
|
583
|
+
tcl_lines.append(
|
|
584
|
+
f"set_global_assignment -name SDC_FILE \"{Path(sdc_file).as_posix()}\""
|
|
585
|
+
)
|
|
581
586
|
|
|
582
587
|
tcl_lines += [
|
|
583
588
|
"project_close",
|
|
@@ -601,116 +606,109 @@ class CommandProjQuartus(CommandProj, ToolQuartus):
|
|
|
601
606
|
class CommandUploadQuartus(CommandUpload, ToolQuartus):
|
|
602
607
|
'''CommandUploadQuartus is a command handler for: eda upload --tool=quartus'''
|
|
603
608
|
|
|
609
|
+
SUPPORTED_BIT_EXT = ['.sof']
|
|
610
|
+
|
|
604
611
|
def __init__(self, config: dict):
|
|
605
612
|
CommandUpload.__init__(self, config)
|
|
606
613
|
ToolQuartus.__init__(self, config=self.config)
|
|
607
614
|
# add args specific to this tool
|
|
608
615
|
self.args.update({
|
|
609
|
-
'sof-file': "",
|
|
610
616
|
'cable': "1",
|
|
611
617
|
'device': "1",
|
|
612
618
|
'list-cables': False,
|
|
613
619
|
'list-devices': False,
|
|
614
|
-
'list-sof-files': False,
|
|
615
|
-
'tcl-file': "upload.tcl",
|
|
616
|
-
'log-file': "upload.log",
|
|
617
620
|
})
|
|
618
621
|
self.args_help.update({
|
|
619
|
-
'sof-file': 'SOF file to upload (auto-detected if not specified)',
|
|
620
622
|
'cable': 'Cable number to use for programming',
|
|
621
623
|
'device': 'Device number on the cable',
|
|
622
624
|
'list-cables': 'List available programming cables',
|
|
623
625
|
'list-devices': 'List available devices on cable',
|
|
624
|
-
'list-sof-files': 'List available SOF files',
|
|
625
|
-
'tcl-file': 'name of TCL file to be created for upload',
|
|
626
|
-
'log-file': 'log file for upload operation',
|
|
627
626
|
})
|
|
628
627
|
|
|
628
|
+
# Support mulitple arg keys for bitfile and list-bitfiles, so
|
|
629
|
+
# --sof-file and --list-sof-files work the same.
|
|
630
|
+
self.args_args.update({
|
|
631
|
+
'bitfile': ['sof-file'],
|
|
632
|
+
'list-bitfiles': ['list-sof-files'],
|
|
633
|
+
})
|
|
634
|
+
self.args_help.update({
|
|
635
|
+
'bitfile': 'SOF file to upload (auto-detected if not specified)',
|
|
636
|
+
'list-bitfiles': 'List available SOF files',
|
|
637
|
+
})
|
|
638
|
+
|
|
639
|
+
|
|
629
640
|
def do_it(self): # pylint: disable=too-many-branches,too-many-statements,too-many-locals
|
|
641
|
+
'''
|
|
642
|
+
Note this is called directly by opencos.commands.CommandUpload, based
|
|
643
|
+
on which bitfile(s) were found, or if --tool=quartus was set
|
|
644
|
+
|
|
645
|
+
We do not need to handle --list-bitfiles, was handled by CommandUpload.
|
|
646
|
+
'''
|
|
647
|
+
|
|
630
648
|
# add defines for this job
|
|
631
649
|
self.set_tool_defines()
|
|
632
650
|
self.write_eda_config_and_args()
|
|
633
651
|
|
|
652
|
+
# Find quartus_pgm executable, we'll want the one from the same path
|
|
653
|
+
# that was used for our self._EXE (ToolQuartus).
|
|
654
|
+
quartus_pgm = safe_shutil_which(os.path.join(self.quartus_base_path, 'quartus_pgm'))
|
|
655
|
+
if not quartus_pgm:
|
|
656
|
+
self.error("quartus_pgm not found in PATH")
|
|
657
|
+
return
|
|
658
|
+
|
|
659
|
+
# Handle --list-cables
|
|
660
|
+
if self.args['list-cables']:
|
|
661
|
+
util.info("Listing available cables...")
|
|
662
|
+
command_list = [quartus_pgm, '--auto']
|
|
663
|
+
_, stdout, _ = self.exec(self.args['work-dir'], command_list)
|
|
664
|
+
util.info("Available cables listed above")
|
|
665
|
+
return
|
|
666
|
+
|
|
634
667
|
sof_file = None
|
|
635
|
-
if self.args['
|
|
636
|
-
if os.path.isfile(self.args['
|
|
637
|
-
sof_file = self.args['
|
|
638
|
-
|
|
639
|
-
|
|
668
|
+
if self.args['bitfile']:
|
|
669
|
+
if os.path.isfile(self.args['bitfile']):
|
|
670
|
+
sof_file = self.args['bitfile']
|
|
671
|
+
|
|
672
|
+
# self.bitfiles was already set by CommandUpload.process_tokens()
|
|
673
|
+
if len(self.bitfiles) == 1:
|
|
674
|
+
sof_file = self.bitfiles[0]
|
|
640
675
|
|
|
641
676
|
# Auto-discover SOF file if not specified
|
|
642
|
-
if not sof_file
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
elif len(sof_files) > 1:
|
|
655
|
-
if self.args['list-sof-files']:
|
|
656
|
-
util.info("Multiple SOF files found:")
|
|
657
|
-
for sf in sof_files:
|
|
658
|
-
util.info(f" {sf}")
|
|
659
|
-
return
|
|
660
|
-
self.error("Multiple SOF files found, please specify --sof-file")
|
|
661
|
-
elif not sof_files:
|
|
662
|
-
if self.args['list-sof-files']:
|
|
663
|
-
util.info("No SOF files found")
|
|
664
|
-
return
|
|
665
|
-
self.error("No SOF files found")
|
|
666
|
-
|
|
667
|
-
# Generate TCL script
|
|
668
|
-
script_file = Path(self.args['tcl-file'])
|
|
669
|
-
|
|
670
|
-
try:
|
|
671
|
-
with script_file.open("w", encoding="utf-8") as fout:
|
|
672
|
-
fout.write('load_package quartus_pgm\n')
|
|
673
|
-
|
|
674
|
-
if self.args['list-cables']:
|
|
675
|
-
fout.write('foreach cable [get_hardware_names] {\n')
|
|
676
|
-
fout.write(' puts "Cable: $cable"\n')
|
|
677
|
-
fout.write('}\n')
|
|
678
|
-
|
|
679
|
-
if self.args['list-devices']:
|
|
680
|
-
cable_idx = int(self.args["cable"]) - 1
|
|
681
|
-
fout.write(f'set cable [lindex [get_hardware_names] {cable_idx}]\n')
|
|
682
|
-
fout.write('foreach device [get_device_names -hardware_name $cable] {\n')
|
|
683
|
-
fout.write(' puts "Device: $device"\n')
|
|
684
|
-
fout.write('}\n')
|
|
685
|
-
|
|
686
|
-
if sof_file:
|
|
687
|
-
cable_idx2 = int(self.args["cable"]) - 1
|
|
688
|
-
device_idx = int(self.args["device"]) - 1
|
|
689
|
-
fout.write(f'set cable [lindex [get_hardware_names] {cable_idx2}]\n')
|
|
690
|
-
device_cmd = (
|
|
691
|
-
f'set device [lindex [get_device_names -hardware_name $cable] {device_idx}]'
|
|
692
|
-
)
|
|
693
|
-
fout.write(device_cmd)
|
|
694
|
-
fout.write('set_global_assignment -name USE_CONFIGURATION_DEVICE OFF\n')
|
|
695
|
-
fout.write('execute_flow -compile\n')
|
|
696
|
-
fout.write(f'quartus_pgm -c $cable -m jtag -o "p;{sof_file}@$device"\n')
|
|
697
|
-
|
|
698
|
-
except Exception as exc:
|
|
699
|
-
self.error(f"Cannot create {script_file}: {exc}")
|
|
700
|
-
|
|
701
|
-
if sof_file:
|
|
702
|
-
util.info(f"Programming with SOF file: {sof_file}")
|
|
703
|
-
else:
|
|
704
|
-
util.info("Listing cables/devices only")
|
|
677
|
+
if not sof_file:
|
|
678
|
+
# CommandUpload already displayed them, and exited on --list-bitfiles.
|
|
679
|
+
if len(self.bitfiles) > 1:
|
|
680
|
+
self.error("Multiple SOF files found, please specify --sof-file or --bitfile",
|
|
681
|
+
"or use a different search pattern")
|
|
682
|
+
return
|
|
683
|
+
|
|
684
|
+
self.error("No SOF files found")
|
|
685
|
+
return
|
|
686
|
+
|
|
687
|
+
util.info(f"Programming with SOF file: {sof_file}")
|
|
688
|
+
|
|
705
689
|
|
|
706
690
|
# Execute Quartus programmer
|
|
691
|
+
# Format: quartus_pgm -c <cable> -m jtag -o "p;<sof_file>@<device>"
|
|
692
|
+
cable = self.args['cable']
|
|
693
|
+
device = self.args['device']
|
|
694
|
+
operation = f"p;{sof_file}@{device}"
|
|
695
|
+
|
|
707
696
|
command_list = [
|
|
708
|
-
|
|
697
|
+
quartus_pgm, '-c', cable, '-m', 'jtag', '-o', operation
|
|
709
698
|
]
|
|
710
|
-
if not util.args['verbose']:
|
|
711
|
-
command_list.append('-q')
|
|
712
699
|
|
|
713
|
-
self.exec(self.args['work-dir'], command_list)
|
|
700
|
+
_, stdout, _ = self.exec(self.args['work-dir'], command_list)
|
|
701
|
+
|
|
702
|
+
# Do some log scraping
|
|
703
|
+
for line in stdout.split('\n'):
|
|
704
|
+
if any(x in line for x in ('Warning', 'WARNING')):
|
|
705
|
+
self.tool_warning_count += 1
|
|
706
|
+
elif any(x in line for x in ('Error', 'ERROR')):
|
|
707
|
+
self.tool_error_count += 1
|
|
708
|
+
|
|
709
|
+
self.report_tool_warn_error_counts()
|
|
710
|
+
self.report_pass_fail()
|
|
711
|
+
|
|
714
712
|
util.info("Upload operation completed")
|
|
715
713
|
|
|
716
714
|
|
opencos/tools/questa_common.py
CHANGED
|
@@ -11,11 +11,11 @@ Contains classes for ToolQuesta, and CommonSimQuesta.
|
|
|
11
11
|
|
|
12
12
|
import os
|
|
13
13
|
import re
|
|
14
|
-
import shutil
|
|
15
14
|
|
|
16
15
|
from opencos import util
|
|
17
16
|
from opencos.commands import sim, CommandSim, CommandFList
|
|
18
17
|
from opencos.eda_base import Tool
|
|
18
|
+
from opencos.files import safe_shutil_which
|
|
19
19
|
from opencos.utils.str_helpers import sanitize_defines_for_sh
|
|
20
20
|
|
|
21
21
|
class ToolQuesta(Tool):
|
|
@@ -25,7 +25,7 @@ class ToolQuesta(Tool):
|
|
|
25
25
|
_EXE = 'vsim'
|
|
26
26
|
|
|
27
27
|
starter_edition = False
|
|
28
|
-
use_vopt =
|
|
28
|
+
use_vopt = False # set manually or by get_versions() (after __init__ has set self._EXE)
|
|
29
29
|
sim_exe = '' # vsim or qrun
|
|
30
30
|
sim_exe_base_path = ''
|
|
31
31
|
questa_major = None
|
|
@@ -33,12 +33,11 @@ class ToolQuesta(Tool):
|
|
|
33
33
|
|
|
34
34
|
def __init__(self, config: dict):
|
|
35
35
|
super().__init__(config=config)
|
|
36
|
-
self.args['part'] = 'xcu200-fsgd2104-2-e'
|
|
37
36
|
|
|
38
37
|
def get_versions(self) -> str:
|
|
39
38
|
if self._VERSION:
|
|
40
39
|
return self._VERSION
|
|
41
|
-
path =
|
|
40
|
+
path = safe_shutil_which(self._EXE)
|
|
42
41
|
if not path:
|
|
43
42
|
self.error(f"{self._EXE} not in path, need to setup",
|
|
44
43
|
"(i.e. source /opt/intelFPGA_pro/23.4/settings64.sh")
|
opencos/tools/riviera.py
CHANGED
|
@@ -7,12 +7,12 @@ Contains classes for ToolRiviera, CommandSimRiviera, CommandElabRiviera.
|
|
|
7
7
|
# pylint: disable=R0801 # (duplicate code in derived classes, such as if-condition return.)
|
|
8
8
|
|
|
9
9
|
import os
|
|
10
|
-
import shutil
|
|
11
10
|
import subprocess
|
|
12
11
|
|
|
13
12
|
from opencos import util
|
|
14
|
-
from opencos.tools.questa_common import ToolQuesta, CommonSimQuesta
|
|
15
13
|
from opencos.commands import CommandFList
|
|
14
|
+
from opencos.files import safe_shutil_which
|
|
15
|
+
from opencos.tools.questa_common import ToolQuesta, CommonSimQuesta
|
|
16
16
|
from opencos.utils.str_helpers import sanitize_defines_for_sh
|
|
17
17
|
from opencos.utils import status_constants
|
|
18
18
|
|
|
@@ -27,7 +27,7 @@ class ToolRiviera(ToolQuesta):
|
|
|
27
27
|
def get_versions(self) -> str:
|
|
28
28
|
if self._VERSION:
|
|
29
29
|
return self._VERSION
|
|
30
|
-
path =
|
|
30
|
+
path = safe_shutil_which(self._EXE)
|
|
31
31
|
if not path:
|
|
32
32
|
self.error(f"{self._EXE} not in path, need to setup or add to PATH")
|
|
33
33
|
util.debug(f"{path=}")
|