opencos-eda 0.3.3__py3-none-any.whl → 0.3.6__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/eda_config.py CHANGED
@@ -15,10 +15,14 @@ import shutil
15
15
  import mergedeep
16
16
 
17
17
  from opencos import util
18
+ from opencos.util import safe_emoji
18
19
  from opencos.utils.markup_helpers import yaml_safe_load, yaml_safe_writer
19
20
 
20
21
  class Defaults:
21
- '''Defaults is a global placeholder for constants and supported features.'''
22
+ '''Defaults is a global namespace for constants and supported features.
23
+
24
+ Defaults.config_yml is set depending on search order for default eda_config[_defaults].yml
25
+ '''
22
26
 
23
27
  environ_override_config_yml = os.environ.get('EDA_CONFIG_YML', '')
24
28
  home_override_config_yml = os.path.join(
@@ -63,14 +67,15 @@ class Defaults:
63
67
  'simulate-args',
64
68
  'simulate-waves-args',
65
69
  'simulate-waivers',
70
+ 'simulate-coverage-tcl',
66
71
  'coverage-args',
67
72
  ])
68
73
 
69
74
  EDA_OUTPUT_CONFIG_FNAME = 'eda_output_config.yml'
70
75
 
71
- if os.path.exists(Defaults.environ_override_config_yml):
76
+ if os.path.isfile(Defaults.environ_override_config_yml):
72
77
  Defaults.config_yml = Defaults.environ_override_config_yml
73
- elif os.path.exists(Defaults.home_override_config_yml):
78
+ elif os.path.isfile(Defaults.home_override_config_yml):
74
79
  Defaults.config_yml = Defaults.home_override_config_yml
75
80
  else:
76
81
  Defaults.config_yml = Defaults.opencos_config_yml
@@ -149,29 +154,9 @@ def update_config_auto_tool_order_for_tool(tool: str, config: dict) -> str:
149
154
  Input arg tool can be in the form (for example):
150
155
  tool='verlator', tool='verilator=/path/to/verilator.exe'
151
156
 
152
- Performs no update if tool has no = in it. Returns tool (str) w/out = in it
157
+ Performs no update if tool has no = or : in it. Returns tool (str) w/out = in it
153
158
  '''
154
- if not tool or '=' not in tool:
155
- return tool
156
-
157
- tool, user_exe = tool.split('=')[0:2]
158
-
159
- user_exe = shutil.which(user_exe)
160
-
161
- # try adding to $PATH if in form --tool=/path/to/exe
162
- tool_try_add_to_path(tool)
163
-
164
- if tool not in config['auto_tools_order'][0]:
165
- return tool
166
- if not user_exe:
167
- return tool
168
-
169
- old_exe = config['auto_tools_order'][0][tool].get('exe', str())
170
- if isinstance(old_exe, list):
171
- config['auto_tools_order'][0][tool]['exe'][0] = user_exe
172
- else:
173
- config['auto_tools_order'][0][tool]['exe'] = user_exe
174
- return tool
159
+ return tool_try_add_to_path(tool=tool, config=config, update_config=True)
175
160
 
176
161
 
177
162
  def update_config_auto_tool_order_for_tools(tools: list, config: dict) -> list:
@@ -247,7 +232,7 @@ def get_config_merged_with_defaults(config:dict) -> dict:
247
232
  def get_argparser() -> argparse.ArgumentParser:
248
233
  '''Returns an ArgumentParser, handles --config-yml=<filename> arg'''
249
234
  parser = argparse.ArgumentParser(
250
- prog='opencos eda config options', add_help=False, allow_abbrev=False
235
+ prog=f'{safe_emoji("🔎 ")}opencos eda config options', add_help=False, allow_abbrev=False
251
236
  )
252
237
  parser.add_argument('--config-yml', type=str, default=Defaults.config_yml,
253
238
  help=('YAML filename to use for configuration (default'
@@ -327,32 +312,92 @@ def write_eda_config_and_args(
327
312
  yaml_safe_writer(data=data, filepath=fullpath)
328
313
 
329
314
 
330
- def tool_try_add_to_path(tool: str) -> None:
331
- '''Since we support --tool=<name>=/path/to/bin/exe, attempt to prepend $PATH
315
+ def tool_arg_get_parts(tool: str) -> list:
316
+ '''Given a tool (str or None) that may be in form <name>=/path/to/something
317
+
318
+ Return the parts [<name>, <path>, ..]
319
+ '''
320
+ if not tool or ('=' not in tool and ':' not in tool):
321
+ return [tool]
322
+
323
+ if '=' in tool:
324
+ parts = tool.split('=')
325
+ else:
326
+ parts = tool.split(':')
327
+
328
+ return parts
329
+
330
+ def tool_arg_remove_path_information(tool: str) -> str:
331
+ '''Given a tool (str or None) that may be in form <name>=/path/to/something
332
+
333
+ Return the <name> only
334
+ '''
335
+ if not tool:
336
+ return tool
337
+ return tool_arg_get_parts(tool)[0]
338
+
339
+
340
+ def tool_try_add_to_path( # pylint: disable=too-many-branches
341
+ tool: str, config: dict, update_config: bool
342
+ ) -> str:
343
+ '''Since we support --tool=<name>=/path/to/bin[/exe], attempt to prepend $PATH
344
+
345
+ (also works for --tool=<name>:/path/to/bin[/exe] )
332
346
 
333
347
  with this information for this tool (which will nicely affect all subprocesses,
334
348
  but not wreck our original shell).'''
335
349
 
336
- if not tool or '=' not in tool:
337
- return
350
+ name_path_parts = tool_arg_get_parts(tool)
351
+ if len(name_path_parts) == 1:
352
+ return name_path_parts[0]
353
+
354
+ name, path_arg = name_path_parts[0:2]
355
+
356
+ if name not in config['auto_tools_order'][0]:
357
+ return name
358
+
359
+ config_exe = config['auto_tools_order'][0][name].get('exe', str())
360
+ if isinstance(config_exe, list):
361
+ orig_exe = config_exe[0]
362
+ else:
363
+ orig_exe = config_exe
338
364
 
339
- name, exe = tool.split('=')
340
- if os.path.isdir(name):
365
+ if path_arg and os.path.isfile(path_arg):
366
+ # Someone passes us --tool=<name>=/path/to/bin/exe, remove the exe from path:
367
+ path, exe = os.path.split(path_arg)
368
+ elif path_arg and os.path.isdir(path_arg):
341
369
  # Someone passes us --tool=<name>=/path/to/bin/ (did not have exe)
342
- path = name
370
+ path, exe = path_arg, orig_exe
343
371
  else:
344
- # Someone passes us --tool=<name>=/path/to/bin/exe, remove the exe.
345
- path, _ = os.path.split(exe)
346
- if not path:
347
- return
372
+ path, exe = '', ''
373
+
374
+ if not path or not exe:
375
+ util.error(f'Can not find path or exe for --tool={tool}: {name=} path={path_arg}')
376
+ return name
348
377
 
349
378
  path = os.path.abspath(path)
350
379
  if os.path.isdir(path):
351
- paths = os.environ['PATH'].split(':')
380
+ paths = os.environ.get('PATH', '').split(':')
352
381
  if path not in paths:
353
- os.environ['PATH'] = path + ':' + os.environ['PATH']
354
382
  util.info(f'--tool={tool} has path information, prepending PATH with: {path}')
383
+ os.environ['PATH'] = path + ':' + os.environ.get('PATH', '')
355
384
  else:
356
385
  util.info(f'--tool={tool} has path information, but {path} already in $PATH')
357
- if exe and os.path.isfile(exe):
358
- util.info(f'--tool={tool} has path information, using exe {shutil.which(exe)}')
386
+
387
+ user_exe = os.path.join(path, exe)
388
+ if not os.access(user_exe, os.X_OK):
389
+ util.error(f'--tool setting for {tool}: {user_exe} is not an executable')
390
+ return name
391
+
392
+ user_exe = shutil.which(exe)
393
+
394
+ if update_config:
395
+ if isinstance(config_exe, list):
396
+ config['auto_tools_order'][0][name]['exe'][0] = user_exe
397
+ else:
398
+ config['auto_tools_order'][0][name]['exe'] = user_exe
399
+ util.debug(f'For {tool=}, auto_tools_order config updated')
400
+
401
+ util.debug(f'For {tool=}, final {user_exe=}')
402
+
403
+ return name
@@ -94,6 +94,11 @@ file_extensions:
94
94
  dotf:
95
95
  - .f
96
96
  - .vc
97
+ python:
98
+ - .py
99
+ makefile:
100
+ - .mk
101
+
97
102
 
98
103
  inferred_top:
99
104
  # file extensions that we can infer "top" module from, if --top omitted.
@@ -247,6 +252,9 @@ tools:
247
252
  +accb +accr +access +r+w
248
253
  coverage-args: |
249
254
  -acdb -acdb_cov sbfectapm
255
+ simulate-coverage-tcl:
256
+ - acdb save
257
+ - acdb report -db work.acdb -txt -o cov.txt
250
258
 
251
259
 
252
260
  modelsim_ase:
@@ -329,7 +337,6 @@ tools:
329
337
  - "COCOTB_TEST_FAILED"
330
338
  log-must-strings:
331
339
  - "passed"
332
- - "Cocotb test completed successfully!"
333
340
 
334
341
 
335
342
  quartus:
File without changes
@@ -68,3 +68,22 @@ def get_all_handler_commands(config=None, tools_loaded=None) -> dict:
68
68
  all_handler_commands[command].append(tool)
69
69
 
70
70
  return all_handler_commands
71
+
72
+
73
+ def get_handler_tool_version(tool: str, eda_command: str, config: dict) -> str:
74
+ '''Attempts to get a Command Handler's version given tool + eda_command'''
75
+
76
+ entry = config['auto_tools_order'][0].get(tool, {})
77
+ if not entry:
78
+ return ''
79
+
80
+ handler_name = entry.get('handlers', {}).get(eda_command, '')
81
+ if not handler_name:
82
+ return ''
83
+
84
+ module = util.import_class_from_string(handler_name)
85
+ obj = module(config=config)
86
+ if not getattr(obj, 'get_versions', None):
87
+ return ''
88
+
89
+ return obj.get_versions()
opencos/export_helper.py CHANGED
@@ -42,12 +42,18 @@ def json_paths_to_jsonl(
42
42
  with open(output_json_path, 'w', encoding='utf-8') as outf:
43
43
 
44
44
  # jsonl is every line of the file is a json.
45
+ # We would expect the JSON to have "tests": [ ... ] with >= 1 test.
45
46
  for json_file_path in json_file_paths:
46
47
  with open(json_file_path, encoding='utf-8') as f:
47
48
  data = json.load(f)
48
49
  if len(assert_json_types) > 0 and type(data) not in assert_json_types:
49
50
  error(f'{json_file_path=} JSON data is not a Table (py dict) {type(data)=}')
50
- json.dump(data, outf)
51
+ if 'tests' in data and isinstance(data['tests'], list):
52
+ for test in data['tests']:
53
+ json.dump(test, outf)
54
+ else:
55
+ json.dump(data, outf)
56
+
51
57
  outf.write('\n')
52
58
  info(f'Wrote {len(json_file_paths)} tests to {output_json_path=}')
53
59
 
@@ -78,7 +84,12 @@ def json_paths_to_single_json(
78
84
  data = json.load(f)
79
85
  if len(assert_json_types) > 0 and type(data) not in assert_json_types:
80
86
  error(f'{json_file_path=} JSON data is not a Table (py dict) {type(data)=}')
81
- out_json_data['tests'].append(data)
87
+ if 'tests' in data and isinstance(data['tests'], list):
88
+ for test in data['tests']:
89
+ out_json_data['tests'].append(test)
90
+ else:
91
+ out_json_data['tests'].append(data)
92
+
82
93
  json.dump(out_json_data, outf)
83
94
  outf.write('\n')
84
95
  info(f'Wrote {len(json_file_paths)} tests {output_json_path=}')
@@ -471,7 +482,52 @@ class ExportHelper:
471
482
  yaml_safe_writer(data=data, filepath=dst)
472
483
 
473
484
 
474
- def create_export_json_in_out_dir( # pylint: disable=unused-argument
485
+ def create_combined_env_file_in_out_dir(self) -> list:
486
+ ''' Returns list of all .env lines to be put in output directory
487
+
488
+ Creates single exported .env file if any --env-file(s)
489
+ were loaded by CLI or DEPS targets. --input-file/-f was handled as args
490
+ already, but --env-files need to be special cased. We do not add this
491
+ combined ".env" file to the all_files list, just build it on the fly if needed.
492
+ '''
493
+ env_lines = []
494
+ for filepath in util.env_files_loaded:
495
+ with open(filepath, encoding='utf-8') as f:
496
+ env_lines.extend(f.readlines() + ['\n'])
497
+
498
+ if env_lines:
499
+ dst = os.path.join(self.out_dir, '.env')
500
+
501
+ # Write this to the export directory too:
502
+ with open(dst, 'w', encoding='utf-8') as f:
503
+ for lineno, line in enumerate(env_lines):
504
+
505
+ # modify VERILOG_SOURCES line if present, although it is known via DEPS.yml,
506
+ # we can set it according to files_v and files_sv:
507
+ if line.strip().startswith('VERILOG_SOURCES'):
508
+ base_verilog_filenames = [
509
+ os.path.split(x)[1] for x in \
510
+ self.cmd_design_obj.files_v + self.cmd_design_obj.files_sv
511
+ ]
512
+ env_lines[lineno] = (
513
+ f'VERILOG_SOURCES = {" ".join(base_verilog_filenames)}'
514
+ )
515
+ continue
516
+
517
+ # remove PYTHONPATH from .env
518
+ if line.strip().startswith('PYTHONPATH'):
519
+ env_lines[lineno] = ''
520
+ continue
521
+
522
+ f.write(line)
523
+
524
+
525
+ info(f'export_helper: Wrote {dst}')
526
+
527
+ return env_lines
528
+
529
+
530
+ def create_export_json_in_out_dir( # pylint: disable=unused-argument,too-many-locals,too-many-branches
475
531
  self, eda_config:dict={}, **kwargs
476
532
  ) -> None:
477
533
  '''Optionally creates an exported JSON file in the output directory'''
@@ -482,40 +538,43 @@ class ExportHelper:
482
538
  # assumes we've run self.create_deps_yml_in_out_dir():
483
539
  assert self.target
484
540
  assert self.out_deps_file
541
+ full_eda_cmd_list = ['eda', self.eda_command]
542
+ if self.args.get('waves', False):
543
+ full_eda_cmd_list.append('--waves')
544
+ if self.args.get('tool', ''):
545
+ full_eda_cmd_list.append(f'--tool={self.args["tool"]}')
546
+ full_eda_cmd_list.append(self.target)
485
547
 
486
548
  data = {
487
- 'name': self.target,
488
- 'eda': {
489
- 'enable': True,
490
- 'multi': False, # Not yet implemented.
491
- 'command': self.eda_command,
492
- 'targets': [self.target],
493
- 'args': [],
494
- 'waves': self.args.get('waves', False),
495
- # tool - eda.CommandSimVerilator has this set in self.args:
496
- 'tool': self.args.get('tool', None),
497
- },
498
- 'files': [],
549
+ 'correlationId': self.target,
550
+ 'jobType': 'edaCmd',
551
+ 'cmd': ' '.join(full_eda_cmd_list),
552
+ 'timeout': 600,
553
+ 'filesList': [], # filename (str), content (str)
499
554
  }
500
555
 
501
- # allow caller to override eda - tool, or eda - args, etc.
502
- for k,v in eda_config.items():
503
- if k in data['eda'] and v is not None:
504
- data['eda'][k] = v
505
-
506
556
  # Note that args may already be set via:
507
557
  # create_deps_yml_in_out_dir(deps_file_args=some_list)
508
558
  # For example, eda.CommandSim.do_export() will set certain allow-listed
509
559
  # args if present with non-default values.
510
560
 
511
-
512
561
  all_files = [self.out_deps_file] + self.included_files \
513
562
  + self.cmd_design_obj.files_sv + self.cmd_design_obj.files_v \
514
563
  + self.cmd_design_obj.files_vhd + self.cmd_design_obj.files_cpp \
564
+ + self.cmd_design_obj.files_sdc + self.cmd_design_obj.files_py \
565
+ + self.cmd_design_obj.files_makefile + self.cmd_design_obj.files_non_source
566
+
567
+ all_files = list(dict.fromkeys(all_files)) # uniqify list.
568
+
569
+ # The last file we handle is a single exported .env file if any --env-file(s)
570
+ env_lines = self.create_combined_env_file_in_out_dir()
571
+ if env_lines:
572
+ # write the updated env_lines to the export.json file.
573
+ data['filesList'].append({
574
+ 'filename': '.env',
575
+ 'content': ''.join(env_lines), # already has \n per line
576
+ })
515
577
 
516
- for x in self.cmd_design_obj.files_non_source:
517
- if x not in all_files:
518
- all_files.append(x)
519
578
 
520
579
  for somefile in all_files:
521
580
 
@@ -532,18 +591,17 @@ class ExportHelper:
532
591
 
533
592
  assert os.path.exists(somefile)
534
593
  with open(somefile, encoding='utf-8') as f:
535
- filestr = ''.join(f.readlines())
536
- data['files'].append({
537
- 'name': os.path.split(somefile)[1],
538
- 'content': filestr,
594
+ data['filesList'].append({
595
+ 'filename': os.path.split(somefile)[1],
596
+ 'content': ''.join(f.readlines()),
539
597
  })
540
598
 
541
-
599
+ test_runner_data = {'tests': [data]} # single test for test runner.
542
600
  dst = os.path.join(self.out_dir, 'export.json')
543
601
  with open(dst, 'w', encoding='utf-8') as f:
544
- json.dump(data, f)
602
+ json.dump(test_runner_data, f)
545
603
  f.write('\n')
546
- info(f'export_helper: Wrote {dst=}')
604
+ info(f'export_helper: Wrote {dst}')
547
605
 
548
606
  # If this was from an `export` command, and the self.out_dir != self.args['work-dir'], then
549
607
  # copy the export.json to the work-dir:
opencos/files.py CHANGED
@@ -24,7 +24,9 @@ FORCE_PREFIX_DICT = {
24
24
  'vhdl@': 'vhdl',
25
25
  'cpp@': 'cpp',
26
26
  'sdc@': 'synth_constraints',
27
- 'f@': 'dotf'
27
+ 'f@': 'dotf',
28
+ 'py@' : 'python',
29
+ 'makefile@': 'makefile'
28
30
  }
29
31
 
30
32
  ALL_FORCED_PREFIXES = set(list(FORCE_PREFIX_DICT.keys()))
opencos/hw/oc_cli.py CHANGED
File without changes
opencos/tests/helpers.py CHANGED
@@ -16,6 +16,7 @@ from opencos.utils.subprocess_helpers import subprocess_run_background
16
16
 
17
17
  # Figure out what tools the system has available, without calling eda.main(..)
18
18
  config, tools_loaded = eda_tool_helper.get_config_and_tools_loaded()
19
+ known_tool_versions = {}
19
20
 
20
21
 
21
22
  def eda_wrap_is_sim_fail(rc: int, quiet: bool = False) -> bool:
@@ -32,6 +33,16 @@ def eda_wrap_is_sim_fail(rc: int, quiet: bool = False) -> bool:
32
33
  status_constants.EDA_DEFAULT_ERROR
33
34
  )
34
35
 
36
+ def handle_tool_version(tool: str, eda_command: str, cfg: dict = config) -> None:
37
+ '''Attempts to use a command handler for tool + eda_command and we'll
38
+ track the version globally'''
39
+
40
+ if not known_tool_versions.get(tool, ''):
41
+ handler_version = eda_tool_helper.get_handler_tool_version(
42
+ tool=tool, eda_command=eda_command, config=cfg
43
+ )
44
+ known_tool_versions[tool] = handler_version
45
+
35
46
  def can_run_eda_command(*commands, cfg: dict = config) -> bool:
36
47
  '''Returns True if we have any installed tool that can run: eda <command>'''
37
48
  runnable = []
@@ -50,10 +61,36 @@ def can_run_eda_command(*commands, cfg: dict = config) -> bool:
50
61
  if entry and entry.get('disable-auto', False):
51
62
  # This tool cannot automatically run our command.
52
63
  return False
64
+ # While we're here, set known tool versions.
65
+ handle_tool_version(tool=tool, eda_command=command, cfg=cfg)
53
66
 
54
67
  runnable.append(True)
55
68
  return runnable and all(runnable)
56
69
 
70
+ def can_uvm(tool: str) -> bool:
71
+ '''Returns True if we can run UVM, per tool'''
72
+
73
+ if tool not in tools_loaded:
74
+ return False
75
+
76
+ if tool == 'verilator':
77
+ # requires UVM_HOME to be set with uvm_pkg.sv existing.
78
+ uvm_home = os.environ.get('UVM_HOME', '')
79
+ uvm_pkg = os.path.join(uvm_home, 'uvm_pkg.sv')
80
+ if not all((os.path.isdir(uvm_home), os.path.isfile(uvm_pkg))):
81
+ return False
82
+
83
+ handle_tool_version(tool=tool, eda_command='sim', cfg=config)
84
+ version_list = known_tool_versions.get('verilator', '').split('.')
85
+ if int(version_list[0]) < 5 or \
86
+ (int(version_list[0]) == 5 and int(version_list[1]) < 42):
87
+ return False
88
+
89
+ return True
90
+
91
+ # default return False:
92
+ return False
93
+
57
94
  def can_run_eda_sim(cfg: dict = config) -> bool:
58
95
  '''Returns True if we have any installed tool that can run: eda sim'''
59
96
  return can_run_eda_command('sim', cfg=cfg)
@@ -127,38 +164,43 @@ def assert_gen_deps_yml_good(filepath:str, want_target:str='') -> dict:
127
164
 
128
165
  def assert_export_json_good(filepath:str) -> dict:
129
166
  '''Checks that an exported JSON (from eda export, or eda <command> --export) has known keys'''
130
- assert os.path.exists(filepath), f'{filepath=} does not exist'
167
+ assert os.path.isfile(filepath), f'{filepath=} does not exist'
131
168
  with open(filepath, encoding='utf-8') as f:
132
169
  data = json.load(f)
133
- assert 'name' in data
134
- assert 'eda' in data
135
- assert any(x in data for x in ['files', 'tb'])
170
+ assert 'tests' in data
171
+ assert len(data.get('tests', [])) >= 1
172
+ for test in data.get('tests', []):
173
+ check_test_runner_schema(test)
136
174
  return data
137
175
 
176
+ def check_test_runner_schema(test: dict) -> None:
177
+ '''Confirm that a single test's JSON/JSONL schema is OK.'''
178
+ assert 'correlationId' in test
179
+ assert 'jobType' in test
180
+ assert 'cmd' in test
181
+ assert 'filesList' in test # 0 files is OK.
182
+
138
183
 
139
184
  def assert_export_jsonl_good(filepath:str, jsonl:bool=True) -> list:
140
185
  '''Checks that an exported JSONL (from eda multi --export) has known keys'''
141
- assert os.path.exists(filepath), f'{filepath=} does not exist'
186
+ assert os.path.isfile(filepath), f'{filepath=} does not exist'
142
187
  ret = []
143
188
  with open(filepath, encoding='utf-8') as f:
144
189
  if jsonl:
190
+ print(f'Using JSONL for {filepath=}')
145
191
  for line in f.readlines():
146
192
  line = line.strip()
147
- data = json.loads(line)
148
- assert 'name' in data
149
- assert 'eda' in data
150
- assert any(x in data for x in ['files', 'tb'])
151
- ret.append(data)
193
+ test = json.loads(line)
194
+ check_test_runner_schema(test)
195
+ ret.append(test)
152
196
  else:
197
+ print(f'Using JSON for {filepath=}')
153
198
  data = json.load(f)
154
199
  assert 'tests' in data
155
- assert isinstance(data['tests'], list)
156
- for entry in data['tests']:
157
- assert 'name' in entry
158
- assert 'eda' in entry
159
- assert any(x in entry for x in ['files', 'tb'])
160
- ret.append(entry)
161
-
200
+ assert len(data.get('tests', [])) >= 1
201
+ for test in data.get('tests', []):
202
+ check_test_runner_schema(test)
203
+ ret.append(test)
162
204
 
163
205
  return ret
164
206
 
@@ -228,6 +270,7 @@ class Helpers:
228
270
  background=True,
229
271
  tee_fpath=logfile
230
272
  )
273
+ print(f'Wrote: {os.path.abspath(logfile)=}')
231
274
  else:
232
275
  with open(logfile, 'w', encoding='utf-8') as f:
233
276
  with redirect_stdout(f), redirect_stderr(f):
opencos/tests/test_eda.py CHANGED
@@ -100,9 +100,19 @@ class TestsRequiresVerilator( # pylint: disable=too-many-public-methods
100
100
  def test_args_sim_tool_with_path(self):
101
101
  '''Test for calling a tool as --tool=<tool>=</path/to/tool-exe>'''
102
102
  verilator_fullpath = shutil.which('verilator')
103
+ verilator_path, _ = os.path.split(verilator_fullpath)
104
+
103
105
  chdir_remove_work_dir('../../lib/tests')
104
106
  rc = eda_sim_wrap('--tool', f'verilator={verilator_fullpath}', 'oclib_fifo_test')
105
- print(f'{rc=}')
107
+ assert rc == 0
108
+
109
+ rc = eda_sim_wrap('--tool', f'verilator:{verilator_fullpath}', 'oclib_fifo_test')
110
+ assert rc == 0
111
+
112
+ rc = eda_sim_wrap('--tool', f'verilator={verilator_path}', 'oclib_fifo_test')
113
+ assert rc == 0
114
+
115
+ rc = eda_sim_wrap('--tool', f'verilator:{verilator_fullpath}', 'oclib_fifo_test')
106
116
  assert rc == 0
107
117
 
108
118
  def test_args_sim_with_coverage(self):