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.
Files changed (35) hide show
  1. opencos/commands/export.py +2 -1
  2. opencos/commands/flist.py +49 -12
  3. opencos/commands/multi.py +101 -130
  4. opencos/commands/synth.py +0 -1
  5. opencos/commands/upload.py +7 -7
  6. opencos/commands/waves.py +50 -19
  7. opencos/deps_helpers.py +93 -3
  8. opencos/deps_schema.py +28 -18
  9. opencos/eda.py +6 -1
  10. opencos/eda_base.py +29 -10
  11. opencos/eda_config.py +1 -0
  12. opencos/eda_config_defaults.yml +4 -1
  13. opencos/eda_extract_deps_keys.py +27 -10
  14. opencos/files.py +6 -8
  15. opencos/names.py +1 -0
  16. opencos/oc_cli.py +143 -1
  17. opencos/peakrdl_cleanup.py +0 -1
  18. opencos/tests/helpers.py +38 -10
  19. opencos/tests/test_deps_helpers.py +46 -2
  20. opencos/tests/test_eda.py +65 -41
  21. opencos/tests/test_tools.py +17 -9
  22. opencos/tools/iverilog.py +0 -2
  23. opencos/tools/modelsim_ase.py +11 -5
  24. opencos/tools/questa.py +14 -19
  25. opencos/tools/verilator.py +0 -2
  26. opencos/tools/vivado.py +253 -112
  27. opencos/tools/yosys.py +1 -6
  28. opencos/util.py +4 -0
  29. {opencos_eda-0.2.28.dist-info → opencos_eda-0.2.32.dist-info}/METADATA +1 -1
  30. {opencos_eda-0.2.28.dist-info → opencos_eda-0.2.32.dist-info}/RECORD +35 -35
  31. {opencos_eda-0.2.28.dist-info → opencos_eda-0.2.32.dist-info}/WHEEL +1 -1
  32. {opencos_eda-0.2.28.dist-info → opencos_eda-0.2.32.dist-info}/entry_points.txt +0 -0
  33. {opencos_eda-0.2.28.dist-info → opencos_eda-0.2.32.dist-info}/licenses/LICENSE +0 -0
  34. {opencos_eda-0.2.28.dist-info → opencos_eda-0.2.32.dist-info}/licenses/LICENSE.spdx +0 -0
  35. {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
- want_str = ' '.join(list(want_str))
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 want_str in line:
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
- want_str = ' '.join(list(want_str))
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 want_str in line:
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
- want_str = ' '.join(list(want_str))
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 want_str in line:
194
+ if want_str0 in line:
172
195
  for word in line.split():
173
- if want_str in word:
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
- from opencos import deps_helpers
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
- thispath = os.path.dirname(__file__)
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(thispath, relpath)
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(thispath, '../../lib/tests/eda.work/oclib_fifo_test.sim'))
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(thispath, 'deps_files', 'no_deps_here', 'empty')
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='elab target_bad0')
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(thispath, 'deps_files', 'error_msgs')
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 run elab targets.
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('elab foo')
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('elab foo2')
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('elab target_good2')
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('elab target_good3')
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='elab target_bad0')
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='elab target_bad1')
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='elab target_bad2')
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='elab target_bad3')
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='elab target_bad4')
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='elab target_bad5')
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='elab target_bad6')
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='elab target_bad7')
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='elab foo.sv')
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='elab foo.sv foo2.sv')
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='elab nope_target0')
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='elab foo.sv nope_target1')
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='elab nope_file0.sv')
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='elab foo2.sv nope_file1.sv')
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(thispath, 'deps_files/command_order', 'eda.work', 'target_test.sim')
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(thispath, 'deps_files', 'tags_with_tools')
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")
@@ -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
- assert len(tools_loaded) > 0
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 test_vivado_xilinx_arg():
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 arg --xilinx to check that defines are set as expected.
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('elab', '--tool', 'vivado', '--xilinx', 'oclib_fifo')
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()
@@ -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
- # Generally we should only support int and str python types passed as
165
- # +define+{k}={v}, but also for SystemVerilog plusargs
166
- vlog_dot_f_lines += [ f'+define+{k}={sanitize_defines_for_sh(v)}' ]
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
- if self.args['xilinx']:
119
- vivado_base_path = getattr(self, 'vivado_base_path', '')
120
- glbl_v = vivado_base_path.replace('bin', 'data/verilog/src/glbl.v')
121
- if not os.path.exists(glbl_v):
122
- self.error(f"Vivado is not setup, could not find file {glbl_v=}")
123
- command_list.append(glbl_v)
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
- if self.args['xilinx']:
152
- # this will need some work
153
- self.error("NOT YET SUPPORTED, arg --xilinx set with Questa", do_exit=False)
154
- # TODO(drew): to get this to work, need to add this, among other items:
155
- # --> command_list += '''-L xil_defaultlib -L unisims_ver -L unimacro_ver
156
- # -L xpm -L secureip -L xilinx_vip'''.split(" ")
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']:
@@ -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: