opencos-eda 0.2.50__py3-none-any.whl → 0.2.52__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.
@@ -280,6 +280,20 @@ tools:
280
280
  simulate-waves-args: |
281
281
  +trace
282
282
 
283
+ cocotb:
284
+ defines:
285
+ OC_TOOL_COCOTB: null
286
+ log-bad-strings:
287
+ - "ERROR"
288
+ - "FAILED"
289
+ - "AssertionError"
290
+ - "Exception"
291
+ - "Traceback"
292
+ - "COCOTB_TEST_FAILED"
293
+ log-must-strings:
294
+ - "passed"
295
+ - "Cocotb test completed successfully!"
296
+
283
297
 
284
298
  vivado:
285
299
  sim-libraries:
@@ -345,6 +359,17 @@ auto_tools_order:
345
359
  handlers:
346
360
  elab: opencos.tools.invio.CommandElabInvio
347
361
 
362
+ vaporview:
363
+ exe: code
364
+ requires_vscode_extension:
365
+ - lramseyer.vaporview
366
+ handlers: { }
367
+
368
+ surfer:
369
+ exe: code
370
+ requires_vscode_extension:
371
+ - surfer-project.surfer
372
+ handlers: { }
348
373
 
349
374
  gtkwave:
350
375
  exe: gtkwave
@@ -398,9 +423,7 @@ auto_tools_order:
398
423
 
399
424
  questa:
400
425
  exe: qrun
401
- requires_cmd:
402
- - qrun -version
403
- - vsim -version
426
+ requires_vsim_helper: True
404
427
  handlers:
405
428
  elab: opencos.tools.queta.CommandElabQuesta
406
429
  sim: opencos.tools.queta.CommandSimQuesta
@@ -408,30 +431,22 @@ auto_tools_order:
408
431
  riviera:
409
432
  exe: vsim
410
433
  requires_cmd:
411
- - vsim -version
412
434
  - which riviera # Do not run it, make sure it's in PATH
413
- requires_in_exe_path:
414
- - riviera
435
+ requires_vsim_helper: True
415
436
  handlers:
416
437
  elab: opencos.tools.riviera.CommandElabRiviera
417
438
  sim: opencos.tools.riviera.CommandSimRiviera
418
439
 
419
440
  modelsim_ase:
420
441
  exe: vsim
421
- requires_cmd:
422
- - vsim -version
423
- requires_in_exe_path:
424
- - modelsim
442
+ requires_vsim_helper: True
425
443
  handlers:
426
444
  elab: opencos.tools.modelsim_ase.CommandElabModelsimAse
427
445
  sim: opencos.tools.modelsim_ase.CommandSimModelsimAse
428
446
 
429
447
  questa_fse: # free student edition, works similar to modelsim_ase
430
448
  exe: vsim
431
- requires_cmd:
432
- - vsim -version
433
- requires_in_exe_path:
434
- - questa_fse
449
+ requires_vsim_helper: True
435
450
  handlers:
436
451
  elab: opencos.tools.questa_fse.CommandElabQuestaFse
437
452
  sim: opencos.tools.questa_fse.CommandSimQuestaFse
@@ -441,3 +456,12 @@ auto_tools_order:
441
456
  handlers:
442
457
  elab: opencos.tools.iverilog.CommandElabIverilog
443
458
  sim: opencos.tools.iverilog.CommandSimIverilog
459
+
460
+ cocotb:
461
+ exe: python
462
+ requires_cmd:
463
+ - python -c "import cocotb; print(cocotb.__version__)"
464
+ requires_py:
465
+ - cocotb
466
+ handlers:
467
+ sim: opencos.tools.cocotb.CommandSimCocotb
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env python3
2
+
3
+ '''opencos.eda_deps_sanitize is an executable script
4
+
5
+ Usage:
6
+ eda_deps_sanitize --dir=<path>
7
+
8
+ Will print santized JSON data for the DEPS file found in --dir=<path>
9
+ '''
10
+
11
+ import argparse
12
+ import os
13
+ import sys
14
+
15
+ from pathlib import Path
16
+
17
+ from opencos import util
18
+ from opencos.deps import deps_file
19
+ from opencos.utils import status_constants
20
+
21
+
22
+ def run(*args) -> (int, str):
23
+ '''Runs the DEPS sanitizer, prints results to stdout'''
24
+
25
+
26
+ bool_kwargs = util.get_argparse_bool_action_kwargs()
27
+
28
+ parser = argparse.ArgumentParser(
29
+ prog='opencos eda_deps_sanitize', add_help=True, allow_abbrev=False
30
+ )
31
+
32
+ parser.add_argument('--yaml', **bool_kwargs,
33
+ help='Print output as YAML text, otherwise default is JSON text')
34
+ parser.add_argument('dir', type=str, default=str(Path('.')),
35
+ help='Directory to look for DEPS.[markup] file')
36
+
37
+ try:
38
+ parsed, unparsed = parser.parse_known_args(list(args) + [''])
39
+ unparsed = list(filter(None, unparsed))
40
+ except argparse.ArgumentError:
41
+ return 1, f'problem attempting to parse_known_args for {args=}'
42
+
43
+ deps_path = parsed.dir
44
+ if os.path.isfile(deps_path):
45
+ deps_path, _ = os.path.split(deps_path)
46
+
47
+ try:
48
+ my_depsfile_obj = deps_file.DepsFile(None, deps_path, {})
49
+ if not my_depsfile_obj.deps_file:
50
+ return status_constants.EDA_DEPS_FILE_NOT_FOUND, f'No DEPS markup file at {parsed.dir}'
51
+
52
+ ret_str = my_depsfile_obj.str_sanitized_markup(as_yaml=parsed.yaml)
53
+ rc = util.get_return_code()
54
+ return rc, ret_str
55
+ except Exception as e:
56
+ rc = 1, str(e)
57
+
58
+ return 0, ''
59
+
60
+
61
+ def main() -> None:
62
+ '''calls sys.exit(), main entrypoint'''
63
+ args = []
64
+ if len(sys.argv) > 1 and not args:
65
+ args = sys.argv[1:]
66
+
67
+ rc, deps_str = run(*args)
68
+ print(deps_str)
69
+ sys.exit(rc)
70
+
71
+
72
+ if __name__ == '__main__':
73
+ main()
@@ -10,6 +10,7 @@ from opencos import eda, eda_tool_helper, eda_base
10
10
 
11
11
  from opencos.tools.verilator import ToolVerilator
12
12
  from opencos.tools.vivado import ToolVivado
13
+ from opencos.tools.cocotb import ToolCocotb
13
14
  from opencos.tests import helpers
14
15
  from opencos.tests.helpers import eda_wrap
15
16
  from opencos.utils.markup_helpers import yaml_safe_load
@@ -48,7 +49,18 @@ def test_tools_loaded():
48
49
  full_ver = obj.get_full_tool_and_versions()
49
50
  assert chk_str in full_ver, f'{chk_str=} not in {full_ver=}'
50
51
  ver_num = full_ver.rsplit(':', maxsplit=1)[-1]
51
- assert float(ver_num), f'{ver_num=} is not a float, from {full_ver=}'
52
+ if 'b' in ver_num:
53
+ ver_num = ver_num.split('b')[0] # TODO(chaitanya): remove once cocotb 2.0 is released
54
+ if '.' in ver_num:
55
+ major_ver = ver_num.split('.')[0]
56
+ assert major_ver.isdigit(), (
57
+ f'Major version {major_ver=} is not a digit, from {full_ver=}'
58
+ )
59
+ assert float(major_ver) >= 0, (
60
+ f'{major_ver=} is not a valid version number, from {full_ver=}'
61
+ )
62
+ else:
63
+ assert float(ver_num), f'{ver_num=} is not a float, from {full_ver=}'
52
64
 
53
65
 
54
66
  # Do some very crude checks on the eda.Tool methods, and make
@@ -61,6 +73,9 @@ def test_tools_loaded():
61
73
  my_tool = ToolVivado(config={})
62
74
  version_checker(obj=my_tool, chk_str='vivado:')
63
75
 
76
+ if 'cocotb' in tools_loaded:
77
+ my_tool = ToolCocotb(config={})
78
+ version_checker(obj=my_tool, chk_str='cocotb:')
64
79
 
65
80
  # Run these on simulation tools.
66
81
  list_of_commands = [
@@ -82,6 +97,11 @@ list_of_deps_targets = [
82
97
  ('tb_dollar_err', False),
83
98
  ]
84
99
 
100
+ cannot_use_cocotb = 'cocotb' not in tools_loaded or \
101
+ ('iverilog' not in tools_loaded and \
102
+ 'verilator' not in tools_loaded)
103
+ CANNOT_USE_COCOTB_REASON = 'requires cocotb in tools_loaded, and one of (iverilog, verilator) too'
104
+
85
105
  @pytest.mark.parametrize("command", list_of_commands)
86
106
  @pytest.mark.parametrize("tool", list_of_tools)
87
107
  @pytest.mark.parametrize("target,sim_expect_pass", list_of_deps_targets)
@@ -154,3 +174,177 @@ def test_vivado_tool_defines():
154
174
 
155
175
  assert data['defines']['OC_LIBRARY'] == '1'
156
176
  assert data['defines']['OC_LIBRARY_ULTRASCALE_PLUS'] is None # key present, no value
177
+
178
+
179
+ @pytest.mark.skipif(cannot_use_cocotb, reason=CANNOT_USE_COCOTB_REASON)
180
+ def test_cocotb_tool_defines():
181
+ '''Test cocotb tool defines, configs, and integration.'''
182
+
183
+ chdir_remove_work_dir('../../examples/cocotb')
184
+
185
+ # Test 0: Using eda multi:
186
+ rc = eda_wrap('multi', 'sim', '--tool=cocotb', '*test')
187
+ assert rc == 0
188
+
189
+ # Test 1: basic cocotb sim command with Python runner (default)
190
+ rc = eda_wrap('sim', '--tool', 'cocotb', 'cocotb_counter_test')
191
+ assert rc == 0
192
+
193
+ # Test 2: cocotb works with different simulators/configurations
194
+ rc = eda_wrap('sim', '--tool', 'cocotb', 'cocotb_counter_waves_test')
195
+ assert rc == 0
196
+
197
+ # Test 3: Makefile approach
198
+ rc = eda_wrap('sim', '--tool', 'cocotb', 'cocotb_counter_makefile_test')
199
+ assert rc == 0
200
+
201
+ # Test 4: cocotb-specific defines are set correctly
202
+ eda_config_yml_path = os.path.join(
203
+ os.getcwd(), 'eda.work', 'cocotb_counter_test.sim', 'eda_output_config.yml'
204
+ )
205
+
206
+ data = yaml_safe_load(eda_config_yml_path)
207
+ assert 'args' in data
208
+ assert data['args'].get('top', '') == 'counter'
209
+ assert 'config' in data
210
+ assert 'eda_original_args' in data['config']
211
+ assert 'cocotb_counter_test' in data['config']['eda_original_args'] or \
212
+ './cocotb_counter_test' in data['config']['eda_original_args']
213
+ assert data.get('target', '') == 'cocotb_counter_test'
214
+
215
+ assert 'defines' in data
216
+ assert 'OC_TOOL_COCOTB' in data['defines']
217
+ assert 'SIMULATION' in data['defines']
218
+ assert 'COCOTB' in data['defines']
219
+
220
+ assert data['defines']['SIMULATION'] == 1
221
+ assert data['defines']['COCOTB'] == 1
222
+ assert data['defines']['OC_TOOL_COCOTB'] is None # key present, no value
223
+
224
+ assert 'VERILATOR' not in data['defines']
225
+ assert 'SYNTHESIS' not in data['defines']
226
+
227
+
228
+
229
+ @pytest.mark.parametrize("cocotb_simulator", ['verilator', 'iverilog'])
230
+ @pytest.mark.parametrize("waves_arg", ["", "--waves"])
231
+ @pytest.mark.skipif(cannot_use_cocotb, reason=CANNOT_USE_COCOTB_REASON)
232
+ def test_cocotb_different_simulators(cocotb_simulator, waves_arg):
233
+ '''Test cocotb with different simulator configurations.'''
234
+
235
+ if cocotb_simulator not in tools_loaded:
236
+ pytest.skip(f"{cocotb_simulator=} skipped, {tools_loaded=}")
237
+ return #skip/bypass
238
+
239
+ chdir_remove_work_dir('../../examples/cocotb')
240
+
241
+ rc = eda_wrap(
242
+ 'sim', '--tool', 'cocotb',
243
+ f'--cocotb-simulator={cocotb_simulator}',
244
+ waves_arg,
245
+ 'cocotb_counter_test',
246
+ )
247
+ assert rc == 0
248
+
249
+
250
+
251
+ @pytest.mark.skipif(cannot_use_cocotb, reason=CANNOT_USE_COCOTB_REASON)
252
+ def test_cocotb_tool_instantiation():
253
+ '''Test that ToolCocotb can be instantiated and has correct properties.'''
254
+
255
+ tool = ToolCocotb(config={})
256
+
257
+ # version detection works
258
+ version = tool.get_versions()
259
+ assert version, "Should return a non-empty version string"
260
+ assert isinstance(version, str)
261
+
262
+ # tool defines
263
+ tool.set_tool_defines()
264
+ defines = tool.defines
265
+ assert 'SIMULATION' in defines
266
+ assert 'COCOTB' in defines
267
+ assert 'OC_TOOL_COCOTB' in defines
268
+ assert defines['SIMULATION'] == 1
269
+ assert defines['COCOTB'] == 1
270
+
271
+
272
+
273
+ @pytest.mark.skipif(cannot_use_cocotb, reason=CANNOT_USE_COCOTB_REASON)
274
+ def test_cocotb_failure_cases():
275
+ '''Test cocotb failure scenarios to ensure proper error handling.'''
276
+
277
+ chdir_remove_work_dir('../../examples/cocotb')
278
+
279
+ # Test 1: missing test files should fail gracefully
280
+ rc = eda_wrap('sim', '--tool', 'cocotb', 'counter') # Just HDL, no test files
281
+ assert rc > 1, "Should fail when no cocotb test files are found"
282
+
283
+ # Test 2: non-existent target should fail
284
+ rc = eda_wrap('sim', '--tool', 'cocotb', 'nonexistent_target')
285
+ assert rc > 1, "Should fail for non-existent target"
286
+
287
+ # Test 3: invalid cocotb test module should fail
288
+ rc = eda_wrap(
289
+ 'sim', '--tool', 'cocotb',
290
+ '--cocotb-test-module=nonexistent_test',
291
+ 'cocotb_counter_test',
292
+ )
293
+ assert rc > 1, "Should fail with invalid test module"
294
+
295
+
296
+ @pytest.mark.skipif(cannot_use_cocotb, reason=CANNOT_USE_COCOTB_REASON)
297
+ def test_cocotb_missing_dependencies():
298
+ '''Test cocotb behavior when dependencies are missing.'''
299
+
300
+ # Test missing cocotb installation (simulate by checking error handling)
301
+ tool = ToolCocotb(config={})
302
+ version = tool.get_versions()
303
+ assert version, "Should return version when cocotb is properly installed"
304
+
305
+ # Test tool defines are properly set even with minimal config
306
+ tool.set_tool_defines()
307
+ defines = tool.defines
308
+ assert 'SIMULATION' in defines
309
+ assert 'COCOTB' in defines
310
+ assert 'OC_TOOL_COCOTB' in defines
311
+
312
+
313
+ @pytest.mark.skipif(cannot_use_cocotb, reason=CANNOT_USE_COCOTB_REASON)
314
+ def test_cocotb_invalid_simulator():
315
+ '''Test cocotb with invalid simulator configuration.'''
316
+
317
+ chdir_remove_work_dir('../../examples/cocotb')
318
+
319
+ rc = eda_wrap(
320
+ 'sim', '--tool', 'cocotb',
321
+ '--cocotb-simulator=invalid_sim',
322
+ 'cocotb_counter_test',
323
+ )
324
+ assert rc > 1, "Should fail with invalid simulator"
325
+
326
+ @pytest.mark.skipif(cannot_use_cocotb, reason=CANNOT_USE_COCOTB_REASON)
327
+ def test_cocotb_malformed_hdl():
328
+ '''Test cocotb with malformed HDL files.'''
329
+
330
+ chdir_remove_work_dir('../../lib/tests')
331
+
332
+ # Test with a target that has syntax errors - should fail during compilation
333
+ rc = eda_wrap(
334
+ 'sim', '--tool', 'cocotb',
335
+ '--cocotb-test-module=test_counter',
336
+ 'tb_dollar_fatal',
337
+ )
338
+
339
+ assert rc > 1, "Should fail with malformed HDL or failing test assertions"
340
+
341
+
342
+ @pytest.mark.skipif(cannot_use_cocotb, reason=CANNOT_USE_COCOTB_REASON)
343
+ def test_cocotb_test_failures():
344
+ '''Test that cocotb properly reports test failures.'''
345
+
346
+ chdir_remove_work_dir('../../examples/cocotb')
347
+
348
+ # Intentionally failing cocotb tests
349
+ rc = eda_wrap('sim', '--tool', 'cocotb', 'cocotb_failure_test')
350
+ assert rc > 1, "Should fail when cocotb tests contain assertion failures"