opencos-eda 0.2.28__py3-none-any.whl → 0.2.32__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/export.py +2 -1
- opencos/commands/flist.py +49 -12
- opencos/commands/multi.py +101 -130
- opencos/commands/synth.py +0 -1
- opencos/commands/upload.py +7 -7
- opencos/commands/waves.py +50 -19
- opencos/deps_helpers.py +93 -3
- opencos/deps_schema.py +28 -18
- opencos/eda.py +6 -1
- opencos/eda_base.py +29 -10
- opencos/eda_config.py +1 -0
- opencos/eda_config_defaults.yml +4 -1
- opencos/eda_extract_deps_keys.py +27 -10
- opencos/files.py +6 -8
- opencos/names.py +1 -0
- opencos/oc_cli.py +143 -1
- opencos/peakrdl_cleanup.py +0 -1
- opencos/tests/helpers.py +38 -10
- opencos/tests/test_deps_helpers.py +46 -2
- opencos/tests/test_eda.py +65 -41
- opencos/tests/test_tools.py +17 -9
- opencos/tools/iverilog.py +0 -2
- opencos/tools/modelsim_ase.py +11 -5
- opencos/tools/questa.py +14 -19
- opencos/tools/verilator.py +0 -2
- opencos/tools/vivado.py +253 -112
- opencos/tools/yosys.py +1 -6
- opencos/util.py +4 -0
- {opencos_eda-0.2.28.dist-info → opencos_eda-0.2.32.dist-info}/METADATA +1 -1
- {opencos_eda-0.2.28.dist-info → opencos_eda-0.2.32.dist-info}/RECORD +35 -35
- {opencos_eda-0.2.28.dist-info → opencos_eda-0.2.32.dist-info}/WHEEL +1 -1
- {opencos_eda-0.2.28.dist-info → opencos_eda-0.2.32.dist-info}/entry_points.txt +0 -0
- {opencos_eda-0.2.28.dist-info → opencos_eda-0.2.32.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.2.28.dist-info → opencos_eda-0.2.32.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.2.28.dist-info → opencos_eda-0.2.32.dist-info}/top_level.txt +0 -0
opencos/tests/helpers.py
CHANGED
|
@@ -11,6 +11,19 @@ from contextlib import redirect_stdout, redirect_stderr
|
|
|
11
11
|
from opencos import eda, util
|
|
12
12
|
from opencos import deps_schema
|
|
13
13
|
|
|
14
|
+
def can_run_eda_command(*commands, config: dict) -> bool:
|
|
15
|
+
'''Returns True if we have any installed tool that can run: eda <command>'''
|
|
16
|
+
runnable = []
|
|
17
|
+
for command in list(commands):
|
|
18
|
+
handler = config['command_handler'].get(command, None)
|
|
19
|
+
if not handler:
|
|
20
|
+
return False
|
|
21
|
+
if handler and getattr(handler, 'CHECK_REQUIRES', []):
|
|
22
|
+
if not all(isinstance(handler, x) for x in getattr(handler, 'CHECK_REQUIRES', [])):
|
|
23
|
+
return False
|
|
24
|
+
runnable.append(True)
|
|
25
|
+
return runnable and all(runnable)
|
|
26
|
+
|
|
14
27
|
def chdir_remove_work_dir(startpath, relpath):
|
|
15
28
|
'''Changes dir to startpath/relpath, removes the work directories (eda.work, eda.export*)'''
|
|
16
29
|
os.chdir(os.path.join(startpath, relpath))
|
|
@@ -125,6 +138,10 @@ class Helpers:
|
|
|
125
138
|
if logfile is None:
|
|
126
139
|
logfile = self.DEFAULT_LOG
|
|
127
140
|
rc = 50
|
|
141
|
+
|
|
142
|
+
# TODO(drew): There are some issues with log_it redirecting stdout from vivado
|
|
143
|
+
# and modelsim_ase. So this may not work for all tools, you may have to directly
|
|
144
|
+
# look at eda.work/{target}.sim/sim.log or xsim.log.
|
|
128
145
|
with open(logfile, 'w', encoding='utf-8') as f:
|
|
129
146
|
with redirect_stdout(f):
|
|
130
147
|
with redirect_stderr(f):
|
|
@@ -135,41 +152,52 @@ class Helpers:
|
|
|
135
152
|
print(f'Wrote: {os.path.abspath(logfile)=}')
|
|
136
153
|
return rc
|
|
137
154
|
|
|
138
|
-
def is_in_log(self, *want_str, logfile=None):
|
|
155
|
+
def is_in_log(self, *want_str, logfile=None, windows_path_support=False):
|
|
139
156
|
'''Check if any of want_str args are in the logfile, or self.DEFAULT_LOG'''
|
|
140
157
|
if logfile is None:
|
|
141
158
|
logfile = self.DEFAULT_LOG
|
|
142
|
-
|
|
159
|
+
want_str0 = ' '.join(list(want_str))
|
|
160
|
+
want_str1 = want_str0.replace('/', '\\')
|
|
143
161
|
with open(logfile, encoding='utf-8') as f:
|
|
144
162
|
for line in f.readlines():
|
|
145
|
-
if
|
|
163
|
+
if want_str0 in line or \
|
|
164
|
+
(windows_path_support and want_str1 in line):
|
|
146
165
|
return True
|
|
147
166
|
return False
|
|
148
167
|
|
|
149
|
-
def get_log_lines_with(self, *want_str, logfile=None):
|
|
168
|
+
def get_log_lines_with(self, *want_str, logfile=None, windows_path_support=False):
|
|
150
169
|
'''gets all log lines with any of want_str args are in the logfile, or self.DEFAULT_LOG'''
|
|
151
170
|
if logfile is None:
|
|
152
171
|
logfile = self.DEFAULT_LOG
|
|
153
172
|
ret_list = []
|
|
154
|
-
|
|
173
|
+
want_str0 = ' '.join(list(want_str))
|
|
174
|
+
want_str1 = want_str0.replace('/', '\\')
|
|
155
175
|
with open(logfile, encoding='utf-8') as f:
|
|
156
176
|
for line in f.readlines():
|
|
157
|
-
if
|
|
177
|
+
if want_str0 in line:
|
|
178
|
+
ret_list.append(line)
|
|
179
|
+
elif windows_path_support and want_str1 in line:
|
|
158
180
|
ret_list.append(line)
|
|
159
181
|
return ret_list
|
|
160
182
|
|
|
161
|
-
def get_log_words_with(self, *want_str, logfile=None):
|
|
183
|
+
def get_log_words_with(self, *want_str, logfile=None, windows_path_support=False):
|
|
162
184
|
'''gets all log lines with any of *want_str within a single word
|
|
163
185
|
in the logfile or self.DEFAULT_LOG
|
|
164
186
|
'''
|
|
165
187
|
if logfile is None:
|
|
166
188
|
logfile = self.DEFAULT_LOG
|
|
167
189
|
ret_list = []
|
|
168
|
-
|
|
190
|
+
want_str0 = ' '.join(list(want_str))
|
|
191
|
+
want_str1 = want_str0.replace('/', '\\')
|
|
169
192
|
with open(logfile, encoding='utf-8') as f:
|
|
170
193
|
for line in f.readlines():
|
|
171
|
-
if
|
|
194
|
+
if want_str0 in line:
|
|
172
195
|
for word in line.split():
|
|
173
|
-
if
|
|
196
|
+
if want_str0 in word:
|
|
174
197
|
ret_list.append(word)
|
|
198
|
+
elif windows_path_support and want_str1 in line:
|
|
199
|
+
for word in line.split():
|
|
200
|
+
if want_str1 in word:
|
|
201
|
+
ret_list.append(word)
|
|
202
|
+
|
|
175
203
|
return ret_list
|
|
@@ -6,9 +6,53 @@
|
|
|
6
6
|
# TODO(drew): for now, ignore long lines and docstrings
|
|
7
7
|
# pylint: disable=line-too-long,missing-function-docstring
|
|
8
8
|
|
|
9
|
+
from pathlib import Path
|
|
9
10
|
import os
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
13
|
+
from opencos import eda_tool_helper, deps_helpers
|
|
14
|
+
|
|
15
|
+
THISPATH = os.path.dirname(__file__)
|
|
16
|
+
|
|
17
|
+
# Figure out what tools the system has available, without calling eda.main(..)
|
|
18
|
+
config, tools_loaded = eda_tool_helper.get_config_and_tools_loaded()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_get_all_targets():
|
|
22
|
+
'''Makes sure that deps_helpers.get_all_targets(filter_str:str) works'''
|
|
23
|
+
|
|
24
|
+
targets = deps_helpers.get_all_targets(
|
|
25
|
+
dirs=[
|
|
26
|
+
str(Path('../../lib/tests')),
|
|
27
|
+
str(Path('../../lib/rams/tests')),
|
|
28
|
+
],
|
|
29
|
+
base_path=str(Path(THISPATH)),
|
|
30
|
+
filter_str='*test',
|
|
31
|
+
)
|
|
32
|
+
print(f'{targets=}')
|
|
33
|
+
assert str(Path('../../lib/rams/tests/oclib_ram2rw_test')) in targets
|
|
34
|
+
assert str(Path('../../lib/tests/oclib_fifo_test')) in targets
|
|
35
|
+
for t in targets:
|
|
36
|
+
assert t.endswith('test'), f'target {t} filter did not work *test'
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@pytest.mark.skipif(
|
|
40
|
+
not('vivado' in tools_loaded or
|
|
41
|
+
'verilator' in tools_loaded),
|
|
42
|
+
reason="requires vivado or verilator"
|
|
43
|
+
)
|
|
44
|
+
def test_get_all_targets_eda_multi():
|
|
45
|
+
'''Makes sure that deps_helpers.get_all_targets(filter_using_mult:str) works'''
|
|
46
|
+
|
|
47
|
+
targets = deps_helpers.get_all_targets(
|
|
48
|
+
base_path=THISPATH,
|
|
49
|
+
filter_using_multi='sim ../../lib/tests/*test ../../lib/rams/tests/*test',
|
|
50
|
+
)
|
|
51
|
+
print(f'{targets=}')
|
|
52
|
+
assert '../../lib/rams/tests/oclib_ram2rw_test' in targets
|
|
53
|
+
assert '../../lib/tests/oclib_fifo_test' in targets
|
|
54
|
+
for t in targets:
|
|
55
|
+
assert t.endswith('test'), f'target {t} filter did not work *test'
|
|
12
56
|
|
|
13
57
|
|
|
14
58
|
def test_parse_deps_shell_str__no_parse():
|
opencos/tests/test_eda.py
CHANGED
|
@@ -24,15 +24,22 @@ from opencos.tests.helpers import eda_wrap, eda_sim_wrap, eda_elab_wrap, \
|
|
|
24
24
|
Helpers
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
THISPATH = os.path.dirname(__file__)
|
|
28
28
|
|
|
29
29
|
def chdir_remove_work_dir(relpath):
|
|
30
30
|
'''Changes dir to relpath, removes the work directories (eda.work, eda.export*)'''
|
|
31
|
-
return helpers.chdir_remove_work_dir(
|
|
31
|
+
return helpers.chdir_remove_work_dir(THISPATH, relpath)
|
|
32
32
|
|
|
33
33
|
# Figure out what tools the system has available, without calling eda.main(..)
|
|
34
34
|
config, tools_loaded = eda_tool_helper.get_config_and_tools_loaded()
|
|
35
35
|
|
|
36
|
+
def can_run_eda_sim() -> bool:
|
|
37
|
+
'''Returns True if we have any installed tool that can run: eda sim'''
|
|
38
|
+
return helpers.can_run_eda_command('sim', config=config)
|
|
39
|
+
|
|
40
|
+
def can_run_eda_elab() -> bool:
|
|
41
|
+
'''Returns True if we have any installed tool that can run: eda elab'''
|
|
42
|
+
return helpers.can_run_eda_command('elab', config=config)
|
|
36
43
|
|
|
37
44
|
@pytest.mark.skipif(
|
|
38
45
|
'verilator' not in tools_loaded and 'vivado' not in tools_loaded,
|
|
@@ -112,7 +119,7 @@ class TestsRequiresVerilator( # pylint: disable=too-many-public-methods
|
|
|
112
119
|
print(f'{rc=}')
|
|
113
120
|
assert rc == 0
|
|
114
121
|
|
|
115
|
-
os.chdir(os.path.join(
|
|
122
|
+
os.chdir(os.path.join(THISPATH, '../../lib/tests/eda.work/oclib_fifo_test.sim'))
|
|
116
123
|
res = subprocess.run(
|
|
117
124
|
[ './lint_only.sh' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
|
118
125
|
check=True
|
|
@@ -391,18 +398,19 @@ class TestsRequiresVerilator( # pylint: disable=too-many-public-methods
|
|
|
391
398
|
|
|
392
399
|
|
|
393
400
|
class TestMissingDepsFileErrorMessages(Helpers):
|
|
394
|
-
'''Test for missing DEPS.yml file'''
|
|
395
|
-
DEFAULT_DIR = os.path.join(
|
|
401
|
+
'''Test for missing DEPS.yml file, using 'eda export' to avoid tools.'''
|
|
402
|
+
DEFAULT_DIR = os.path.join(THISPATH, 'deps_files', 'no_deps_here', 'empty')
|
|
396
403
|
DEFAULT_LOG = 'eda.log'
|
|
397
404
|
|
|
398
405
|
def test_bad0(self):
|
|
399
406
|
'''Looks for target_bad0, but there is no DEPS file in .'''
|
|
400
407
|
self.chdir()
|
|
401
|
-
rc = self.log_it(command_str='
|
|
408
|
+
rc = self.log_it(command_str='export target_bad0')
|
|
402
409
|
assert rc != 0
|
|
403
410
|
assert self.is_in_log(
|
|
404
411
|
'Trying to resolve command-line target=./target_bad0: was not found',
|
|
405
|
-
'in deps_file=None, possible targets in deps file = []'
|
|
412
|
+
'in deps_file=None, possible targets in deps file = []',
|
|
413
|
+
windows_path_support=True
|
|
406
414
|
)
|
|
407
415
|
|
|
408
416
|
|
|
@@ -413,35 +421,35 @@ class TestDepsResolveErrorMessages(Helpers):
|
|
|
413
421
|
or other less helpful information to the user. This confirms that file/target/
|
|
414
422
|
linenumber information is printed when available.'''
|
|
415
423
|
|
|
416
|
-
DEFAULT_DIR = os.path.join(
|
|
424
|
+
DEFAULT_DIR = os.path.join(THISPATH, 'deps_files', 'error_msgs')
|
|
417
425
|
DEFAULT_LOG = 'eda.log'
|
|
418
426
|
|
|
419
427
|
# files foo.sv, foo2.sv, target_bad0.sv, and target_bad1.sv exist.
|
|
420
428
|
# files missing*.sv and targets missing* do not exist.
|
|
421
|
-
# These all
|
|
429
|
+
# These all "export" targets, to avoid requiring an installed tool (for example, to elab)
|
|
422
430
|
|
|
423
431
|
def test_good0(self):
|
|
424
432
|
'''Simple test with good target (foo)'''
|
|
425
433
|
self.chdir()
|
|
426
|
-
rc = self.log_it('
|
|
434
|
+
rc = self.log_it('export foo')
|
|
427
435
|
assert rc == 0
|
|
428
436
|
|
|
429
437
|
def test_good1(self):
|
|
430
438
|
'''Simple test with good target (foo2)'''
|
|
431
439
|
self.chdir()
|
|
432
|
-
rc = self.log_it('
|
|
440
|
+
rc = self.log_it('export foo2')
|
|
433
441
|
assert rc == 0
|
|
434
442
|
|
|
435
443
|
def test_good2(self):
|
|
436
444
|
'''Simple test with good target (foo + top=foo using deps str)'''
|
|
437
445
|
self.chdir()
|
|
438
|
-
rc = self.log_it('
|
|
446
|
+
rc = self.log_it('export target_good2')
|
|
439
447
|
assert rc == 0
|
|
440
448
|
|
|
441
449
|
def test_good3(self):
|
|
442
450
|
'''Simple test with good target (foo2 + top=foo2 using deps list)'''
|
|
443
451
|
self.chdir()
|
|
444
|
-
rc = self.log_it('
|
|
452
|
+
rc = self.log_it('export target_good3')
|
|
445
453
|
assert rc == 0
|
|
446
454
|
|
|
447
455
|
# Bit of a change-detector-test here, but I want to make sure the
|
|
@@ -449,133 +457,146 @@ class TestDepsResolveErrorMessages(Helpers):
|
|
|
449
457
|
def test_bad0(self):
|
|
450
458
|
'''Tests missing file in DEPS target using implicit deps str style'''
|
|
451
459
|
self.chdir()
|
|
452
|
-
rc = self.log_it(command_str='
|
|
460
|
+
rc = self.log_it(command_str='export target_bad0')
|
|
453
461
|
assert rc != 0
|
|
454
462
|
assert self.is_in_log(
|
|
455
463
|
"target=./missing0.sv (file?): called from ./DEPS.yml::target_bad0::line=20,",
|
|
456
|
-
"File=missing0.sv not found in directory=."
|
|
464
|
+
"File=missing0.sv not found in directory=.",
|
|
465
|
+
windows_path_support=True
|
|
457
466
|
)
|
|
458
467
|
|
|
459
468
|
def test_bad1(self):
|
|
460
469
|
'''Tests missing file in DEPS target using implicit deps list style'''
|
|
461
470
|
self.chdir()
|
|
462
|
-
rc = self.log_it(command_str='
|
|
471
|
+
rc = self.log_it(command_str='export target_bad1')
|
|
463
472
|
assert rc != 0
|
|
464
473
|
assert self.is_in_log(
|
|
465
474
|
"target=./missing1.sv (file?): called from ./DEPS.yml::target_bad1::line=24,",
|
|
466
|
-
"File=missing1.sv not found in directory=."
|
|
475
|
+
"File=missing1.sv not found in directory=.",
|
|
476
|
+
windows_path_support=True
|
|
467
477
|
)
|
|
468
478
|
|
|
469
479
|
def test_bad2(self):
|
|
470
480
|
'''Tests missing file in DEPS target using deps as str style'''
|
|
471
481
|
self.chdir()
|
|
472
|
-
rc = self.log_it(command_str='
|
|
482
|
+
rc = self.log_it(command_str='export target_bad2')
|
|
473
483
|
assert rc != 0
|
|
474
484
|
assert self.is_in_log(
|
|
475
485
|
"target=./missing2.sv (file?): called from ./DEPS.yml::target_bad2::line=28,",
|
|
476
|
-
"File=missing2.sv not found in directory=."
|
|
486
|
+
"File=missing2.sv not found in directory=.",
|
|
487
|
+
windows_path_support=True
|
|
477
488
|
)
|
|
478
489
|
|
|
479
490
|
def test_bad3(self):
|
|
480
491
|
'''Tests missing file in DEPS target using deps as list style'''
|
|
481
492
|
self.chdir()
|
|
482
|
-
rc = self.log_it(command_str='
|
|
493
|
+
rc = self.log_it(command_str='export target_bad3')
|
|
483
494
|
assert rc != 0
|
|
484
495
|
assert self.is_in_log(
|
|
485
496
|
"target=./missing3.sv (file?): called from ./DEPS.yml::target_bad3::line=33,",
|
|
486
|
-
"File=missing3.sv not found in directory=."
|
|
497
|
+
"File=missing3.sv not found in directory=.",
|
|
498
|
+
windows_path_support=True
|
|
487
499
|
)
|
|
488
500
|
|
|
489
501
|
def test_bad4(self):
|
|
490
502
|
'''EDA on a bad target (bad target within deps of 'target_bad4'), explicit deps str'''
|
|
491
503
|
self.chdir()
|
|
492
|
-
rc = self.log_it(command_str='
|
|
504
|
+
rc = self.log_it(command_str='export target_bad4')
|
|
493
505
|
assert rc != 0
|
|
494
506
|
assert self.is_in_log(
|
|
495
507
|
"target=./missing_target4: called from ./DEPS.yml::target_bad4::line=39,",
|
|
496
|
-
"Target not found in deps_file=./DEPS.yml"
|
|
508
|
+
"Target not found in deps_file=./DEPS.yml",
|
|
509
|
+
windows_path_support=True
|
|
497
510
|
)
|
|
498
511
|
|
|
499
512
|
def test_bad5(self):
|
|
500
513
|
'''EDA on a bad target (bad target within deps of 'target_bad4'), explicit deps list'''
|
|
501
514
|
self.chdir()
|
|
502
|
-
rc = self.log_it(command_str='
|
|
515
|
+
rc = self.log_it(command_str='export target_bad5')
|
|
503
516
|
assert rc != 0
|
|
504
517
|
assert self.is_in_log(
|
|
505
518
|
"target=./missing_target5: called from ./DEPS.yml::target_bad5::line=43,",
|
|
506
|
-
"Target not found in deps_file=./DEPS.yml"
|
|
519
|
+
"Target not found in deps_file=./DEPS.yml",
|
|
520
|
+
windows_path_support=True
|
|
507
521
|
)
|
|
508
522
|
|
|
509
523
|
def test_bad6(self):
|
|
510
524
|
'''EDA on a bad target (bad target within deps of 'target_bad4'), deps str'''
|
|
511
525
|
self.chdir()
|
|
512
|
-
rc = self.log_it(command_str='
|
|
526
|
+
rc = self.log_it(command_str='export target_bad6')
|
|
513
527
|
assert rc != 0
|
|
514
528
|
assert self.is_in_log(
|
|
515
529
|
"target=./missing_target6: called from ./DEPS.yml::target_bad6::line=47,",
|
|
516
|
-
"Target not found in deps_file=./DEPS.yml"
|
|
530
|
+
"Target not found in deps_file=./DEPS.yml",
|
|
531
|
+
windows_path_support=True
|
|
532
|
+
|
|
517
533
|
)
|
|
518
534
|
|
|
519
535
|
def test_bad7(self):
|
|
520
536
|
'''EDA on a bad target (bad target within deps of 'target_bad4'), deps list'''
|
|
521
537
|
self.chdir()
|
|
522
|
-
rc = self.log_it(command_str='
|
|
538
|
+
rc = self.log_it(command_str='export target_bad7')
|
|
523
539
|
assert rc != 0
|
|
524
540
|
assert self.is_in_log(
|
|
525
541
|
"target=./missing_target7: called from ./DEPS.yml::target_bad7::line=52,",
|
|
526
|
-
"Target not found in deps_file=./DEPS.yml"
|
|
542
|
+
"Target not found in deps_file=./DEPS.yml",
|
|
543
|
+
windows_path_support=True
|
|
527
544
|
)
|
|
528
545
|
|
|
529
546
|
def test_cmd_line_good0(self):
|
|
530
547
|
'''EDA w/ out DEPS, on file'''
|
|
531
548
|
self.chdir()
|
|
532
|
-
rc = self.log_it(command_str='
|
|
549
|
+
rc = self.log_it(command_str='export foo.sv')
|
|
533
550
|
assert rc == 0
|
|
534
551
|
|
|
535
552
|
def test_cmd_line_good1(self):
|
|
536
553
|
'''EDA w/ out DEPS, on two files'''
|
|
537
554
|
self.chdir()
|
|
538
|
-
rc = self.log_it(command_str='
|
|
555
|
+
rc = self.log_it(command_str='export foo.sv foo2.sv')
|
|
539
556
|
assert rc == 0
|
|
540
557
|
|
|
541
558
|
def test_cmd_line_bad0(self):
|
|
542
559
|
'''EDA calling a non-existent target in DEPS file'''
|
|
543
560
|
self.chdir()
|
|
544
|
-
rc = self.log_it(command_str='
|
|
561
|
+
rc = self.log_it(command_str='export nope_target0')
|
|
545
562
|
assert rc != 0
|
|
546
563
|
assert self.is_in_log(
|
|
547
564
|
"Trying to resolve command-line target=./nope_target0: was not",
|
|
548
|
-
"found in deps_file=./DEPS.yml, possible targets in deps file = ['foo'"
|
|
565
|
+
"found in deps_file=./DEPS.yml, possible targets in deps file = ['foo'",
|
|
566
|
+
windows_path_support=True
|
|
549
567
|
)
|
|
550
568
|
|
|
551
569
|
def test_cmd_line_bad1(self):
|
|
552
570
|
'''EDA calling a non-existent target in DEPS file, with file that exists.'''
|
|
553
571
|
self.chdir()
|
|
554
|
-
rc = self.log_it(command_str='
|
|
572
|
+
rc = self.log_it(command_str='export foo.sv nope_target1')
|
|
555
573
|
assert rc != 0
|
|
556
574
|
assert self.is_in_log(
|
|
557
575
|
"Trying to resolve command-line target=./nope_target1: was not",
|
|
558
|
-
"found in deps_file=./DEPS.yml, possible targets in deps file = ['foo'"
|
|
576
|
+
"found in deps_file=./DEPS.yml, possible targets in deps file = ['foo'",
|
|
577
|
+
windows_path_support=True
|
|
559
578
|
)
|
|
560
579
|
|
|
561
580
|
def test_cmd_line_bad2(self):
|
|
562
581
|
'''EDA calling a non-existent file w/out DEPS'''
|
|
563
582
|
self.chdir()
|
|
564
|
-
rc = self.log_it(command_str='
|
|
583
|
+
rc = self.log_it(command_str='export nope_file0.sv')
|
|
565
584
|
assert rc != 0
|
|
566
585
|
assert self.is_in_log(
|
|
567
586
|
"Trying to resolve command-line target=./nope_file0.sv",
|
|
568
|
-
"(file?): File=nope_file0.sv not found in directory=."
|
|
587
|
+
"(file?): File=nope_file0.sv not found in directory=.",
|
|
588
|
+
windows_path_support=True
|
|
569
589
|
)
|
|
570
590
|
|
|
571
591
|
def test_cmd_line_bad3(self):
|
|
572
592
|
'''EDA calling a non-existent file w/out DEPS, and a file that does exist.'''
|
|
573
593
|
self.chdir()
|
|
574
|
-
rc = self.log_it(command_str='
|
|
594
|
+
rc = self.log_it(command_str='export foo2.sv nope_file1.sv')
|
|
575
595
|
assert rc != 0
|
|
576
596
|
assert self.is_in_log(
|
|
577
597
|
"Trying to resolve command-line target=./nope_file1.sv",
|
|
578
|
-
"(file?): File=nope_file1.sv not found in directory=."
|
|
598
|
+
"(file?): File=nope_file1.sv not found in directory=.",
|
|
599
|
+
windows_path_support=True
|
|
579
600
|
)
|
|
580
601
|
|
|
581
602
|
|
|
@@ -599,6 +620,8 @@ class TestsRequiresIVerilog(Helpers):
|
|
|
599
620
|
print(f'{rc=}')
|
|
600
621
|
assert rc == 0
|
|
601
622
|
|
|
623
|
+
|
|
624
|
+
@pytest.mark.skipif(not can_run_eda_sim(), reason='no tool found to handle command: sim')
|
|
602
625
|
class TestDepsReqs:
|
|
603
626
|
'''Tests for 'reqs' in the DEPS files. 'reqs' are requirements, like a .pcap or file
|
|
604
627
|
used by SV $readmemh. They do not fit into normal compilable files (.sv, .v, .vhd[l])
|
|
@@ -647,6 +670,7 @@ class TestDepsReqs:
|
|
|
647
670
|
assert rc != 0
|
|
648
671
|
|
|
649
672
|
|
|
673
|
+
@pytest.mark.skipif(not can_run_eda_sim(), reason='no tool found to handle command: sim')
|
|
650
674
|
def test_deps_command_order():
|
|
651
675
|
'''Test for various "commands" within a DEPS target. This test checks that command
|
|
652
676
|
order is preserved in the top-to-bottom deps order, meaning that eda.py has to collect
|
|
@@ -684,7 +708,7 @@ def test_deps_command_order():
|
|
|
684
708
|
# Added check, we redirected to create eda.log earlier to confirm the targets worked,
|
|
685
709
|
# but as a general eda.py check, all shell commands should create their own
|
|
686
710
|
# {target}__shell_0.log file:
|
|
687
|
-
work_dir = os.path.join(
|
|
711
|
+
work_dir = os.path.join(THISPATH, 'deps_files/command_order', 'eda.work', 'target_test.sim')
|
|
688
712
|
with open(os.path.join(work_dir, 'target_echo_hi__shell_0.log'), encoding='utf-8') as f:
|
|
689
713
|
text = ''.join(f.readlines()).strip()
|
|
690
714
|
assert text == 'hi'
|
|
@@ -776,7 +800,7 @@ class TestDepsNoFilesTargets(Helpers):
|
|
|
776
800
|
|
|
777
801
|
class TestDepsTags(Helpers):
|
|
778
802
|
'''Series of tests for DEPS - target - tags, in ./deps_files/tags_with_tools'''
|
|
779
|
-
DEFAULT_DIR = os.path.join(
|
|
803
|
+
DEFAULT_DIR = os.path.join(THISPATH, 'deps_files', 'tags_with_tools')
|
|
780
804
|
DEFAULT_LOG = 'eda.log'
|
|
781
805
|
|
|
782
806
|
@pytest.mark.skipif('verilator' not in tools_loaded, reason="requires verilator")
|
opencos/tests/test_tools.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# pylint: disable=R0801 # (similar lines in 2+ files)
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
+
import sys
|
|
6
7
|
import pytest
|
|
7
8
|
|
|
8
9
|
from opencos import eda, util, eda_tool_helper, eda_base
|
|
@@ -29,7 +30,15 @@ def test_tools_loaded():
|
|
|
29
30
|
'''
|
|
30
31
|
assert config
|
|
31
32
|
assert len(config.keys()) > 0
|
|
32
|
-
|
|
33
|
+
|
|
34
|
+
# It's possible we're running in some container or install that has no tools, for example,
|
|
35
|
+
# Windows.
|
|
36
|
+
if sys.platform.startswith('win') and \
|
|
37
|
+
not helpers.can_run_eda_command('elab', 'sim', config=config):
|
|
38
|
+
# Windows, not handlers for elab or sim:
|
|
39
|
+
pass
|
|
40
|
+
else:
|
|
41
|
+
assert len(tools_loaded) > 0
|
|
33
42
|
|
|
34
43
|
def version_checker(
|
|
35
44
|
obj: eda_base.Tool, chk_str: str
|
|
@@ -95,7 +104,7 @@ def test_err_fatal(command, tool, target, sim_expect_pass):
|
|
|
95
104
|
|
|
96
105
|
|
|
97
106
|
@pytest.mark.skipif('vivado' not in tools_loaded, reason="requires vivado")
|
|
98
|
-
def
|
|
107
|
+
def test_vivado_tool_defines():
|
|
99
108
|
'''This test attempts to confirm that the following class inheritance works:
|
|
100
109
|
|
|
101
110
|
Command <- CommandDesign <- CommandSim <- CommandSimVivado <- CommandElabVivado
|
|
@@ -104,11 +113,15 @@ def test_vivado_xilinx_arg():
|
|
|
104
113
|
correct ToolVivado.set_tool_defines() method, and that no other parent Command
|
|
105
114
|
class has overriden it to defeat the defines that should be set.
|
|
106
115
|
|
|
107
|
-
We also run with
|
|
116
|
+
We also run with an added dependency (lib_ultrascale_plus_defines) to check that
|
|
117
|
+
defines are set as expected.
|
|
108
118
|
'''
|
|
109
119
|
|
|
110
120
|
chdir_remove_work_dir('../../lib')
|
|
111
|
-
rc = eda_wrap(
|
|
121
|
+
rc = eda_wrap(
|
|
122
|
+
'elab', '--tool', 'vivado', 'third_party/vendors/xilinx/lib_ultrascale_plus_defines',
|
|
123
|
+
'oclib_fifo'
|
|
124
|
+
)
|
|
112
125
|
assert rc == 0
|
|
113
126
|
|
|
114
127
|
# Confirm that args and defines we expected to be set are set.
|
|
@@ -122,13 +135,8 @@ def test_vivado_xilinx_arg():
|
|
|
122
135
|
assert 'config' in data
|
|
123
136
|
assert 'eda_original_args' in data['config']
|
|
124
137
|
assert 'oclib_fifo' in data['config']['eda_original_args']
|
|
125
|
-
assert '--xilinx' in data['config']['eda_original_args']
|
|
126
138
|
assert data.get('target', '') == 'oclib_fifo'
|
|
127
139
|
|
|
128
|
-
assert 'xilinx' in data['args']
|
|
129
|
-
assert 'xilinx' in data['modified_args']
|
|
130
|
-
assert data['args']['xilinx'] is True
|
|
131
|
-
assert data['modified_args']['xilinx'] is True
|
|
132
140
|
|
|
133
141
|
# This checks opencos.tools.vivado.ToolVivado.set_tool_defines():
|
|
134
142
|
# We ran with --xilinx, so we expect certain defines to be set, others not to be set.
|
opencos/tools/iverilog.py
CHANGED
|
@@ -78,8 +78,6 @@ class CommandSimIverilog(CommandSim, ToolIverilog):
|
|
|
78
78
|
# We do not override CommandSim.do_it()
|
|
79
79
|
def prepare_compile(self):
|
|
80
80
|
self.set_tool_defines()
|
|
81
|
-
if self.args['xilinx']:
|
|
82
|
-
self.error('Error: --xilinx with Iverilog is not yet supported', do_exit=False)
|
|
83
81
|
|
|
84
82
|
self.iverilog_command_lists = self.get_compile_command_lists()
|
|
85
83
|
self.iverilog_exec_command_lists = self.get_simulate_command_lists()
|
opencos/tools/modelsim_ase.py
CHANGED
|
@@ -53,8 +53,6 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
|
|
|
53
53
|
self.set_tool_defines()
|
|
54
54
|
self.write_vlog_dot_f()
|
|
55
55
|
self.write_vsim_dot_do(dot_do_to_write='all')
|
|
56
|
-
if self.args['xilinx']:
|
|
57
|
-
self.error('Error: --xilinx with Modelsim ASE is not yet supported', do_exit=False)
|
|
58
56
|
|
|
59
57
|
vsim_command_lists = self.get_compile_command_lists()
|
|
60
58
|
util.write_shell_command_file(
|
|
@@ -161,9 +159,17 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
|
|
|
161
159
|
if v is None:
|
|
162
160
|
vlog_dot_f_lines += [ f'+define+{k}' ]
|
|
163
161
|
else:
|
|
164
|
-
|
|
165
|
-
#
|
|
166
|
-
|
|
162
|
+
|
|
163
|
+
# if the value v is a double-quoted string, such as v='"hi"', the
|
|
164
|
+
# entire +define+NAME="hi" needs to wrapped in double quotes with the
|
|
165
|
+
# value v double-quotes escaped: "+define+NAME=\"hi\""
|
|
166
|
+
if isinstance(v, str) and v.startswith('"') and v.endswith('"'):
|
|
167
|
+
str_v = v.replace('"', '\\"')
|
|
168
|
+
vlog_dot_f_lines += [ f'"+define+{k}={str_v}"' ]
|
|
169
|
+
else:
|
|
170
|
+
# Generally we should only support int and str python types passed as
|
|
171
|
+
# +define+{k}={v}, but also for SystemVerilog plusargs
|
|
172
|
+
vlog_dot_f_lines += [ f'+define+{k}={sanitize_defines_for_sh(v)}' ]
|
|
167
173
|
|
|
168
174
|
|
|
169
175
|
vlog_dot_f_lines += self.args['compile-args']
|
opencos/tools/questa.py
CHANGED
|
@@ -30,7 +30,6 @@ class ToolQuesta(Tool):
|
|
|
30
30
|
|
|
31
31
|
def __init__(self, config: dict):
|
|
32
32
|
super().__init__(config=config)
|
|
33
|
-
self.args['xilinx'] = False
|
|
34
33
|
self.args['part'] = 'xcu200-fsgd2104-2-e'
|
|
35
34
|
|
|
36
35
|
def get_versions(self) -> str:
|
|
@@ -65,12 +64,6 @@ class ToolQuesta(Tool):
|
|
|
65
64
|
# i.e. has self.defines
|
|
66
65
|
self.defines['OC_TOOL_QUESTA'] = None
|
|
67
66
|
self.defines[f'OC_TOOL_QUESTA_{self.questa_major:d}_{self.questa_minor:d}'] = None
|
|
68
|
-
if self.args['xilinx']:
|
|
69
|
-
self.defines['OC_LIBRARY_ULTRASCALE_PLUS'] = None
|
|
70
|
-
self.defines['OC_LIBRARY'] = "1"
|
|
71
|
-
else:
|
|
72
|
-
self.defines['OC_LIBRARY_BEHAVIORAL'] = None
|
|
73
|
-
self.defines['OC_LIBRARY'] = "0"
|
|
74
67
|
|
|
75
68
|
class CommandSimQuesta(CommandSim, ToolQuesta):
|
|
76
69
|
'''Command handler for: eda sim --tool=questa.'''
|
|
@@ -115,12 +108,14 @@ class CommandSimQuesta(CommandSim, ToolQuesta):
|
|
|
115
108
|
for f in self.files_sv:
|
|
116
109
|
command_list += [ f ]
|
|
117
110
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
111
|
+
# TODO(drew): We don't natively support Xilinx Ultrascale+ glbl.v in questa yet.
|
|
112
|
+
# To do so, would need to do something along the lines of:
|
|
113
|
+
# if using_queta_and_xilinx_libs:
|
|
114
|
+
# vivado_base_path = getattr(self, 'vivado_base_path', '')
|
|
115
|
+
# glbl_v = vivado_base_path.replace('bin', 'data/verilog/src/glbl.v')
|
|
116
|
+
# if not os.path.exists(glbl_v):
|
|
117
|
+
# self.error(f"Vivado is not setup, could not find file {glbl_v=}")
|
|
118
|
+
# command_list.append(glbl_v)
|
|
124
119
|
|
|
125
120
|
# misc options
|
|
126
121
|
command_list += [ '-top', self.args['top'], '-timescale', '1ns/1ps', '-work', 'work.lib']
|
|
@@ -148,12 +143,12 @@ class CommandSimQuesta(CommandSim, ToolQuesta):
|
|
|
148
143
|
|
|
149
144
|
if util.args['verbose']:
|
|
150
145
|
command_list += ['-verbose']
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
146
|
+
|
|
147
|
+
# TODO(drew): We don't natively support Xilinx Ultrascale+ libraries in --tool=questa
|
|
148
|
+
# with an easy hook (like --xilinx) but we could?
|
|
149
|
+
# To get this to work, need to add this, among other items:
|
|
150
|
+
# --> command_list += '''-L xil_defaultlib -L unisims_ver -L unimacro_ver
|
|
151
|
+
# -L xpm -L secureip -L xilinx_vip'''.split(" ")
|
|
157
152
|
|
|
158
153
|
# check if we're bailing out early
|
|
159
154
|
if self.args['stop-after-elaborate']:
|
opencos/tools/verilator.py
CHANGED
|
@@ -107,8 +107,6 @@ class VerilatorSim(CommandSim, ToolVerilator):
|
|
|
107
107
|
# We do not override CommandSim.do_it()
|
|
108
108
|
def prepare_compile(self):
|
|
109
109
|
self.set_tool_defines()
|
|
110
|
-
if self.args['xilinx']:
|
|
111
|
-
self.error('Error: --xilinx with Verilator is not yet supported', do_exit=False)
|
|
112
110
|
|
|
113
111
|
# If there are C++ files here, then we will run Verilator in --cc mode:
|
|
114
112
|
if self.files_cpp:
|