opencos-eda 0.2.51__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.
opencos/commands/waves.py CHANGED
@@ -32,6 +32,8 @@ class CommandWaves(CommandDesign):
32
32
 
33
33
  VSIM_TOOLS = set([
34
34
  'questa',
35
+ 'questa_fse',
36
+ 'riviera',
35
37
  'modelsim_ase',
36
38
  ])
37
39
 
@@ -137,13 +139,21 @@ class CommandWaves(CommandDesign):
137
139
  self.error(f"Don't know how to open {wave_file} without one of",
138
140
  f"{self.VSIM_TOOLS} in PATH")
139
141
  elif wave_file.endswith('.fst'):
140
- if 'gtkwave' in self.config['tools_loaded'] and shutil.which('gtkwave'):
142
+ if ('vaporview' in self.config['tools_loaded'] or \
143
+ 'surfer' in self.config['tools_loaded']) and shutil.which('code'):
144
+ command_list = ['code', '-n', '.', wave_file]
145
+ self.exec(os.path.dirname(wave_file), command_list)
146
+ elif 'gtkwave' in self.config['tools_loaded'] and shutil.which('gtkwave'):
141
147
  command_list = ['gtkwave', wave_file]
142
148
  self.exec(os.path.dirname(wave_file), command_list)
143
149
  else:
144
150
  self.error(f"Don't know how to open {wave_file} without GtkWave in PATH")
145
151
  elif wave_file.endswith('.vcd'):
146
- if 'gtkwave' in self.config['tools_loaded'] and shutil.which('gtkwave'):
152
+ if ('vaporview' in self.config['tools_loaded'] or \
153
+ 'surfer' in self.config['tools_loaded']) and shutil.which('code'):
154
+ command_list = ['code', '-n', '.', wave_file]
155
+ self.exec(os.path.dirname(wave_file), command_list)
156
+ elif 'gtkwave' in self.config['tools_loaded'] and shutil.which('gtkwave'):
147
157
  command_list = ['gtkwave', wave_file]
148
158
  self.exec(os.path.dirname(wave_file), command_list)
149
159
  elif self._vsim_available(from_tools=self.VSIM_VCD_TOOLS):
opencos/eda.py CHANGED
@@ -21,7 +21,7 @@ from pathlib import Path
21
21
  import opencos
22
22
  from opencos import util, eda_config, eda_base
23
23
  from opencos.eda_base import Tool, which_tool
24
- from opencos.utils import vsim_helper
24
+ from opencos.utils import vsim_helper, vscode_helper
25
25
 
26
26
  # Configure util:
27
27
  util.progname = "EDA"
@@ -213,6 +213,19 @@ def auto_tool_setup( # pylint: disable=too-many-locals,too-many-branches,too-man
213
213
  vsim_helper.init() # only runs checks once internally
214
214
  has_vsim_helper = vsim_helper.TOOL_IS.get(name, False)
215
215
 
216
+ has_vscode_helper = True
217
+ needs_vscode_extensions = value.get('requires_vscode_extension', None)
218
+ if needs_vscode_extensions:
219
+ if not isinstance(needs_vscode_extensions, list):
220
+ util.error(
221
+ f'eda config issue, tool {name}: requires_vscode_extension must be a list'
222
+ )
223
+ else:
224
+ vscode_helper.init() # only runs checks once internally
225
+ has_vscode_helper = all(
226
+ x in vscode_helper.EXTENSIONS for x in needs_vscode_extensions
227
+ )
228
+
216
229
  if has_all_exe:
217
230
  requires_cmd_list = value.get('requires_cmd', [])
218
231
  for cmd in requires_cmd_list:
@@ -230,8 +243,8 @@ def auto_tool_setup( # pylint: disable=too-many-locals,too-many-branches,too-man
230
243
  util.debug(f"... No, exception {e} running {cmd_list}")
231
244
 
232
245
 
233
- if all([has_all_py, has_all_env, has_all_exe, has_all_in_exe_path,
234
- has_vsim_helper]):
246
+ if all((has_all_py, has_all_env, has_all_exe, has_all_in_exe_path,
247
+ has_vsim_helper, has_vscode_helper)):
235
248
  exe = exe_list[0]
236
249
  p = shutil.which(exe)
237
250
  config['auto_tools_found'][name] = exe # populate key-value pairs w/ first exe in list
opencos/eda_config.py CHANGED
@@ -46,7 +46,7 @@ class Defaults:
46
46
  supported_config_auto_tools_order_keys = set([
47
47
  'exe', 'handlers',
48
48
  'requires_env', 'requires_py', 'requires_cmd', 'requires_in_exe_path',
49
- 'requires_vsim_helper',
49
+ 'requires_vsim_helper', 'requires_vscode_extension',
50
50
  'disable-tools-multi',
51
51
  ])
52
52
  supported_config_tool_keys = set([
@@ -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
@@ -431,3 +456,12 @@ auto_tools_order:
431
456
  handlers:
432
457
  elab: opencos.tools.iverilog.CommandElabIverilog
433
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
@@ -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"
@@ -0,0 +1,483 @@
1
+ ''' opencos.tools.cocotb - Used by opencos.eda for sim commands with --tool=cocotb.
2
+
3
+ Contains classes for ToolCocotb, CommandSimCocotb.
4
+ '''
5
+
6
+ import os
7
+ import shutil
8
+ import subprocess
9
+
10
+ from opencos import util
11
+ from opencos.eda_base import Tool
12
+ from opencos.commands import CommandSim
13
+ from opencos.utils.str_helpers import sanitize_defines_for_sh
14
+
15
+
16
+ class ToolCocotb(Tool):
17
+ '''ToolCocotb used by opencos.eda for --tool=cocotb'''
18
+
19
+ _TOOL = 'cocotb'
20
+ _EXE = 'python'
21
+ _URL = 'https://github.com/cocotb/cocotb'
22
+
23
+ cocotb_version = ''
24
+ python_exe = ''
25
+
26
+ def get_versions(self) -> str:
27
+ if self._VERSION:
28
+ return self._VERSION
29
+
30
+ # Check if python is available
31
+ python_path = shutil.which('python') or shutil.which('python3')
32
+ if not python_path:
33
+ self.error('"python" or "python3" not in path, required for cocotb')
34
+ else:
35
+ self.python_exe = python_path
36
+
37
+ # Check if cocotb is installed
38
+ try:
39
+ version_ret = subprocess.run(
40
+ [self.python_exe, '-c', 'import cocotb; print(cocotb.__version__)'],
41
+ capture_output=True,
42
+ check=True,
43
+ text=True
44
+ )
45
+ version = version_ret.stdout.strip()
46
+ self.cocotb_version = version
47
+ self._VERSION = version
48
+ util.debug(f'Found cocotb version: {version}')
49
+ return self._VERSION
50
+ except subprocess.CalledProcessError:
51
+ self.error('cocotb package not installed in python environment. '
52
+ 'Install with: pip install cocotb')
53
+ except Exception as e:
54
+ self.error(f'Failed to check cocotb version: {e}')
55
+
56
+ return ''
57
+
58
+ def set_tool_defines(self):
59
+ super().set_tool_defines()
60
+ self.defines.update({
61
+ 'SIMULATION': 1,
62
+ 'COCOTB': 1,
63
+ 'OC_TOOL_COCOTB': None,
64
+ })
65
+
66
+
67
+ class CommandSimCocotb(CommandSim, ToolCocotb):
68
+ '''CommandSimCocotb is a command handler for: eda sim --tool=cocotb'''
69
+
70
+ def __init__(self, config: dict):
71
+ CommandSim.__init__(self, config)
72
+ ToolCocotb.__init__(self, config=self.config)
73
+
74
+ self.args.update({
75
+ 'gui': False,
76
+ 'tcl-file': None,
77
+ 'cocotb-test-module': None,
78
+ 'cocotb-test-runner': 'python',
79
+ 'cocotb-simulator': 'verilator',
80
+ 'cocotb-makefile': False,
81
+ 'cocotb-python-runner': True,
82
+ 'cocotb-standalone-makefile': False,
83
+ })
84
+
85
+ self.args_help.update({
86
+ 'waves': 'Include waveforms by setting COCOTB_ENABLE_WAVES=1',
87
+ 'cocotb-test-module': 'Python test module name (e.g., test_my_design)',
88
+ 'cocotb-test-runner': 'Test runner to use: python (default) or pytest',
89
+ 'cocotb-simulator': ('Simulator backend: verilator (default), icarus, etc.'
90
+ ' Note that iverilog will convert to icarus here'),
91
+ 'cocotb-makefile': 'Use traditional Makefile system instead of Python runner',
92
+ 'cocotb-python-runner': 'Use Python-based runner system (default, cocotb 1.8+)',
93
+ 'cocotb-standalone-makefile': ('Use provided Makefile as-is, '
94
+ 'run make in source directory'),
95
+ })
96
+
97
+ self.cocotb_command_lists = []
98
+ self.cocotb_test_files = []
99
+
100
+ def set_tool_defines(self):
101
+ ToolCocotb.set_tool_defines(self)
102
+
103
+ def prepare_compile(self):
104
+ self.set_tool_defines()
105
+
106
+ # Fix iverilog -> icarus
107
+ if self.args['cocotb-simulator'] == 'iverilog':
108
+ self.args['cocotb-simulator'] = 'icarus'
109
+
110
+ # Find cocotb test files
111
+ self._find_cocotb_test_files()
112
+
113
+ if self.args['cocotb-standalone-makefile']:
114
+ self._prepare_standalone_makefile_system()
115
+ elif self.args['cocotb-makefile']:
116
+ self._prepare_makefile_system()
117
+ else:
118
+ self._prepare_python_runner_system()
119
+
120
+ # Create directories
121
+ paths = ['logs', 'sim_build']
122
+ util.safe_mkdirs(base=self.args['work-dir'], new_dirs=paths)
123
+
124
+ # Write shell scripts
125
+ util.write_shell_command_file(
126
+ dirpath=self.args['work-dir'],
127
+ filename='cocotb_test.sh',
128
+ command_lists=self.cocotb_command_lists,
129
+ line_breaks=True
130
+ )
131
+
132
+ util.write_shell_command_file(
133
+ dirpath=self.args['work-dir'],
134
+ filename='all.sh',
135
+ command_lists=[
136
+ ['./pre_compile_dep_shell_commands.sh'],
137
+ ['./cocotb_test.sh'],
138
+ ]
139
+ )
140
+
141
+ def _find_cocotb_test_files(self):
142
+ '''Find Python test files that contain cocotb tests'''
143
+ self.cocotb_test_files = []
144
+
145
+ # Look for test files in the current directory and deps
146
+ for file_path in self.files_non_source:
147
+ if (file_path.endswith('.py') and
148
+ ('test' in file_path.lower() or 'tb' in file_path.lower())):
149
+ # Check if it's a cocotb test file
150
+ try:
151
+ with open(file_path, 'r', encoding='utf-8') as f:
152
+ content = f.read()
153
+ if 'import cocotb' in content or 'from cocotb' in content:
154
+ self.cocotb_test_files.append(file_path)
155
+ util.debug(f'Found cocotb test file: {file_path}')
156
+ except Exception as e:
157
+ util.debug(f'Could not read {file_path}: {e}')
158
+
159
+ # If no test files found, look for explicit test module
160
+ if not self.cocotb_test_files and self.args.get('cocotb-test-module'):
161
+ test_module = self.args['cocotb-test-module']
162
+ if not test_module.endswith('.py'):
163
+ test_module += '.py'
164
+
165
+ # Look in work directory and target directory
166
+ for search_dir in [self.args['work-dir'], os.path.dirname(self.target or '.')]:
167
+ test_path = os.path.join(search_dir, test_module)
168
+ if os.path.exists(test_path):
169
+ self.cocotb_test_files.append(test_path)
170
+ break
171
+
172
+ if not self.cocotb_test_files:
173
+ self.error('No cocotb test files found. Either include .py files with '
174
+ 'cocotb imports in your deps, or specify --cocotb-test-module')
175
+
176
+ def _prepare_python_runner_system(self):
177
+ '''Prepare cocotb using the Python-based runner system (cocotb 1.8+)'''
178
+
179
+ # Create a Python runner script
180
+ runner_script = self._create_python_runner_script()
181
+
182
+ if self.args['cocotb-test-runner'] == 'pytest':
183
+ # Use pytest to run the tests
184
+ runner_script_name = os.path.basename(runner_script)
185
+ cmd_list = [
186
+ self.python_exe, '-m', 'pytest',
187
+ runner_script_name,
188
+ '-v', '-s'
189
+ ]
190
+ else:
191
+ # Run the Python script directly
192
+ runner_script_name = os.path.basename(runner_script)
193
+ cmd_list = [self.python_exe, runner_script_name]
194
+
195
+ # Set environment variables
196
+ env_vars = self._get_cocotb_env_vars()
197
+
198
+ # Create command with environment variables
199
+ command_list = self._create_env_command(env_vars) + cmd_list
200
+ self.cocotb_command_lists = [util.ShellCommandList(command_list,
201
+ tee_fpath='cocotb_test.log')]
202
+
203
+ def _prepare_makefile_system(self):
204
+ '''Prepare cocotb using the traditional Makefile system'''
205
+
206
+ makefile_path = os.path.join(self.args['work-dir'], 'Makefile')
207
+ with open(makefile_path, 'w', encoding='utf-8') as f:
208
+ f.write(self._create_makefile_content())
209
+
210
+ cmd_list = self._create_shell_command_with_success('make -f Makefile')
211
+ self.cocotb_command_lists = [util.ShellCommandList(cmd_list,
212
+ tee_fpath='cocotb_makefile.log')]
213
+
214
+ def _prepare_standalone_makefile_system(self):
215
+ '''Use provided Makefile as-is, run make in source directory'''
216
+
217
+ # Find the Makefile in our dependencies
218
+ makefile_path = None
219
+ for file_path in self.files_non_source:
220
+ if os.path.basename(file_path).lower() == 'makefile':
221
+ makefile_path = file_path
222
+ break
223
+
224
+ if not makefile_path:
225
+ self.error('No Makefile found in deps for --cocotb-standalone-makefile')
226
+
227
+ makefile_dir = os.path.dirname(os.path.abspath(makefile_path))
228
+ cmd_list = self._create_shell_command_with_success(f'cd {makefile_dir} && make')
229
+ self.cocotb_command_lists = [util.ShellCommandList(
230
+ cmd_list, tee_fpath='cocotb_standalone.log')]
231
+
232
+ def _get_test_module_name(self) -> str:
233
+ '''Get the test module name from args or detected files'''
234
+ if self.args.get('cocotb-test-module'):
235
+ return self.args['cocotb-test-module']
236
+ if self.cocotb_test_files:
237
+ # Use the first test file found, strip .py extension
238
+ test_file = os.path.basename(self.cocotb_test_files[0])
239
+ return test_file.replace('.py', '')
240
+ return 'test_design'
241
+
242
+
243
+ def _get_hdl_sources(self) -> dict:
244
+ '''Get HDL source files organized by type'''
245
+ return {
246
+ 'verilog': (self.files_sv or []) + (self.files_v or []),
247
+ 'vhdl': self.files_vhd or [],
248
+ 'all': (self.files_sv or []) + (self.files_v or []) + (self.files_vhd or [])
249
+ }
250
+
251
+ def _create_python_runner_script(self) -> str:
252
+ '''Create the Python runner script and return its path'''
253
+ test_module = self._get_test_module_name()
254
+ hdl_sources = self._get_hdl_sources()['all']
255
+
256
+ script_content = self._generate_runner_script_content(test_module, hdl_sources)
257
+ script_path = os.path.join(self.args['work-dir'], 'cocotb_runner.py')
258
+
259
+ with open(script_path, 'w', encoding='utf-8') as f:
260
+ f.write(script_content)
261
+
262
+ os.chmod(script_path, 0o755)
263
+
264
+ return script_path
265
+
266
+ def _generate_runner_script_content(self, test_module: str, hdl_sources: list) -> str:
267
+ '''Generate the content for the Python runner script'''
268
+ return f'''#!/usr/bin/env python3
269
+ """
270
+ Cocotb test runner script generated by opencos
271
+ """
272
+
273
+ import os
274
+ import sys
275
+ from pathlib import Path
276
+
277
+ # Add current directory to Python path for test imports
278
+ sys.path.insert(0, os.getcwd())
279
+
280
+ try:
281
+ from cocotb_tools.runner import get_runner
282
+ except ImportError:
283
+ print("ERROR: cocotb not found. Install with: pip install cocotb")
284
+ sys.exit(1)
285
+
286
+ def run_cocotb_test():
287
+ """Run cocotb test using the Python runner system"""
288
+
289
+ # Configuration
290
+ simulator = "{self.args['cocotb-simulator']}"
291
+ hdl_toplevel = "{self.args.get('top', 'top')}"
292
+ test_module = "{test_module}"
293
+
294
+ # HDL source files
295
+ hdl_sources = {hdl_sources!r}
296
+
297
+ # Convert to Path objects and make absolute
298
+ sources = []
299
+ for src in hdl_sources:
300
+ src_path = Path(src)
301
+ if not src_path.is_absolute():
302
+ src_path = Path.cwd() / src_path
303
+ sources.append(src_path)
304
+
305
+ # Include directories
306
+ include_dirs = {list(self.incdirs)!r}
307
+
308
+ # Defines (filter out None values for cocotb compatibility)
309
+ all_defines = {dict(self.defines)!r}
310
+ defines = {{k: v for k, v in all_defines.items() if v is not None}}
311
+
312
+ # Parameters (empty for simple modules without parameters)
313
+ parameters = {{}}
314
+
315
+ try:
316
+ # Get the runner for the specified simulator
317
+ runner = get_runner(simulator)
318
+
319
+ # Build the design
320
+ runner.build(
321
+ sources=sources,
322
+ hdl_toplevel=hdl_toplevel,
323
+ includes=include_dirs,
324
+ defines=defines,
325
+ parameters=parameters,
326
+ build_dir="sim_build",
327
+ )
328
+
329
+ # Run the test
330
+ runner.test(
331
+ hdl_toplevel=hdl_toplevel,
332
+ test_module=test_module,
333
+ test_dir=".",
334
+ )
335
+
336
+ print("{self._get_success_message()}")
337
+
338
+ except Exception as e:
339
+ print(f"ERROR: Cocotb test failed: {{e}}")
340
+ sys.exit(1)
341
+
342
+ if __name__ == "__main__":
343
+ run_cocotb_test()
344
+ '''
345
+
346
+ def _create_makefile_content(self) -> str:
347
+ '''Create Makefile content for cocotb traditional system'''
348
+
349
+ # Determine test module
350
+ test_module = self._get_test_module_name()
351
+
352
+ # Get HDL sources and determine language
353
+ hdl_sources = self._get_hdl_sources()
354
+ verilog_sources = hdl_sources['verilog']
355
+ vhdl_sources = hdl_sources['vhdl']
356
+
357
+ # Determine HDL language
358
+ if vhdl_sources and not verilog_sources:
359
+ hdl_lang = 'vhdl'
360
+ sources_var = 'VHDL_SOURCES'
361
+ sources_list = ' '.join(vhdl_sources)
362
+ else:
363
+ hdl_lang = 'verilog'
364
+ sources_var = 'VERILOG_SOURCES'
365
+ sources_list = ' '.join(verilog_sources)
366
+
367
+ makefile_content = f'''# Cocotb Makefile generated by opencos
368
+
369
+ # Simulator selection
370
+ SIM ?= {self.args['cocotb-simulator']}
371
+
372
+ # HDL language
373
+ TOPLEVEL_LANG = {hdl_lang}
374
+
375
+ # HDL sources
376
+ {sources_var} = {sources_list}
377
+
378
+ # Top level module
379
+ TOPLEVEL = {self.args.get('top', 'top')}
380
+
381
+ # Test module
382
+ MODULE = {test_module}
383
+
384
+ # Include directories
385
+ '''
386
+
387
+ if self.incdirs:
388
+ makefile_content += ('COMPILE_ARGS += ' +
389
+ ' '.join(f'-I{inc}' for inc in self.incdirs) + '\n')
390
+
391
+ # Add defines
392
+ if self.defines:
393
+ define_args = []
394
+ for k, v in self.defines.items():
395
+ if v is None:
396
+ define_args.append(f'-D{k}')
397
+ else:
398
+ define_args.append(f'-D{k}={sanitize_defines_for_sh(v)}')
399
+ makefile_content += 'COMPILE_ARGS += ' + ' '.join(define_args) + '\n'
400
+
401
+ makefile_content += '''
402
+ # Waves support
403
+ ifeq ($(WAVES),1)
404
+ COCOTB_ENABLE_WAVES = 1
405
+ export COCOTB_ENABLE_WAVES
406
+ endif
407
+
408
+ # Include cocotb's Makefile
409
+ include $(shell cocotb-config --makefiles)/Makefile.sim
410
+ '''
411
+
412
+ return makefile_content
413
+
414
+ def _get_success_message(self) -> str:
415
+ '''Get standardized success message'''
416
+ return "Cocotb test completed successfully!"
417
+
418
+ def _create_shell_command_with_success(self, base_command: str) -> list:
419
+ '''Create a shell command list with success message'''
420
+ return ['sh', '-c', f'{base_command} && echo "{self._get_success_message()}"']
421
+
422
+ def _get_cocotb_env_vars(self) -> dict:
423
+ '''Get environment variables for cocotb execution'''
424
+ env_vars = {}
425
+
426
+ # Basic cocotb configuration
427
+ env_vars['SIM'] = self.args['cocotb-simulator']
428
+ env_vars['TOPLEVEL'] = self.args.get('top', 'top')
429
+
430
+ # Enable waves if requested
431
+ if self.args.get('waves', False):
432
+ env_vars['COCOTB_ENABLE_WAVES'] = '1'
433
+
434
+ # Set log level based on verbosity
435
+ if util.args.get('verbose', False):
436
+ env_vars['COCOTB_LOG_LEVEL'] = 'DEBUG'
437
+ else:
438
+ env_vars['COCOTB_LOG_LEVEL'] = 'INFO'
439
+
440
+ # Random seed
441
+ if self.args.get('seed'):
442
+ env_vars['COCOTB_RANDOM_SEED'] = str(self.args['seed'])
443
+
444
+ return env_vars
445
+
446
+ def _create_env_command(self, env_vars: dict) -> list:
447
+ '''Create environment variable command prefix'''
448
+ env_cmd = []
449
+ for key, value in env_vars.items():
450
+ env_cmd.extend(['env', f'{key}={value}'])
451
+ return env_cmd
452
+
453
+ def compile(self):
454
+ # For cocotb, compilation happens as part of the test run
455
+ if self.args['stop-before-compile']:
456
+ return
457
+ util.info('Cocotb: compilation will happen during test execution')
458
+
459
+ def elaborate(self):
460
+ # For cocotb, elaboration happens as part of the test run
461
+ pass
462
+
463
+ def simulate(self):
464
+ if self.args['stop-before-compile'] or self.args['stop-after-compile'] or \
465
+ self.args['stop-after-elaborate']:
466
+ return
467
+
468
+ # Run the cocotb tests
469
+ self.run_commands_check_logs(self.cocotb_command_lists)
470
+
471
+ def get_compile_command_lists(self, **kwargs) -> list:
472
+ # Cocotb handles compilation internally
473
+ return []
474
+
475
+ def get_elaborate_command_lists(self, **kwargs) -> list:
476
+ # Cocotb handles elaboration internally
477
+ return []
478
+
479
+ def get_simulate_command_lists(self, **kwargs) -> list:
480
+ return self.cocotb_command_lists
481
+
482
+ def get_post_simulate_command_lists(self, **kwargs) -> list:
483
+ return []
@@ -79,6 +79,8 @@ class VerilatorSim(CommandSim, ToolVerilator):
79
79
  'lint-only': False,
80
80
  'cc-mode': False,
81
81
  'verilator-coverage-args': [],
82
+ 'x-assign': '',
83
+ 'x-initial': '',
82
84
  })
83
85
 
84
86
  self.args_help.update({
@@ -92,6 +94,14 @@ class VerilatorSim(CommandSim, ToolVerilator):
92
94
  'gui': 'Not supported for Verilator',
93
95
  'cc-mode': 'Run verilator with --cc, requires a sim_main.cpp or similar sources',
94
96
  'optimize': 'Run verilator with: -CLAGS -O3, if no other CFLAGS args are presented',
97
+ 'x-assign': ('String value to added to verilator call: --x-assign <string>;'
98
+ ' where valid string values are: 0, 1, unique, fast.'
99
+ ' Also conditinally adds to verilated exe call:'
100
+ ' +verilator+rand+reset+[0,1,2] for arg values 0, 1, unique|fast'),
101
+ 'x-initial': ('String value to added to verilator call: --x-initial <string>;'
102
+ ' where valid string values are: 0, unique, fast.'
103
+ ' Also conditinally adds to verilated exe call:'
104
+ ' +verilator+rand+reset+[0,2] for arg values 0, unique|fast'),
95
105
  })
96
106
 
97
107
  self.verilate_command_lists = []
@@ -219,6 +229,14 @@ class VerilatorSim(CommandSim, ToolVerilator):
219
229
  '-o', 'sim.exe',
220
230
  ]
221
231
 
232
+ for arg in ('x-assign', 'x-initial'):
233
+ if self.args[arg] and f'--{arg}' not in verilate_command_list:
234
+ # Only add this if arg is set, and not present in verilator call
235
+ # this takes care of it being in our self.tool_config for compile-args.
236
+ verilate_command_list += [
237
+ f'--{arg}', self.args[arg]
238
+ ]
239
+
222
240
  # incdirs
223
241
  for value in self.incdirs:
224
242
  verilate_command_list += [ f"+incdir+{value}" ]
@@ -295,6 +313,19 @@ class VerilatorSim(CommandSim, ToolVerilator):
295
313
  if not any(x.startswith('+verilator+seed+') for x in verilated_exec_command_list):
296
314
  verilated_exec_command_list.append(f'+verilator+seed+{verilator_seed}')
297
315
 
316
+ if any(self.args[arg] in ('unique', 'fast') for arg in ('x-assign', 'x-initial')) and \
317
+ not any(x.startswith('+verilator+rand+reset') for x in verilated_exec_command_list):
318
+ # Only add this if arg is one of x-assign/x-initial is set to "unique" or "fast",
319
+ # we use the encoded value "2" for +verilator+rand+reset+2
320
+ verilated_exec_command_list.append('+verilator+rand+reset+2')
321
+
322
+ if self.args['x-assign'] == '1' and \
323
+ not any(x.startswith('+verilator+rand+reset') for x in verilated_exec_command_list):
324
+ # Only add this if --x-assign=1 (not valid for --x-initial),
325
+ # we use the encoded value "1" for +verilator+rand+reset+1
326
+ verilated_exec_command_list.append('+verilator+rand+reset+1')
327
+
328
+
298
329
  return [
299
330
  util.ShellCommandList(verilated_exec_command_list, tee_fpath='sim.log')
300
331
  ] # single entry list
opencos/util.py CHANGED
@@ -425,10 +425,12 @@ def process_tokens(tokens:list) -> (argparse.Namespace, list):
425
425
  elif parsed.logfile:
426
426
  start_log(parsed.logfile, force=False)
427
427
  elif parsed.default_log and \
428
+ not parsed.version and \
428
429
  not any(x in unparsed for x in ('help', '-h', '--help')) and \
429
430
  (parsed.force_logfile is None and parsed.logfile is None):
430
431
  # Use a forced logfile in the eda.work/eda.log:
431
432
  # avoid this if someone has --help arg not yet parsed.
433
+ # avoid this if someone called --version, b/c that will print and exit.
432
434
  start_log(global_log.default_log_filepath, force=True)
433
435
 
434
436
  parsed_as_dict = vars(parsed)
@@ -0,0 +1,47 @@
1
+ '''Because > 1 tools use the exe 'code', I don't want to run
2
+
3
+ `code --list-extensions --show-versionsvsim -version` for every tool that needs
4
+ to check if they exist in VScode land.
5
+
6
+ Instead, eda.py can call this once, and then query if the VScode extension exists when
7
+ running opencos.eda.auto_tool_setup(..)
8
+ '''
9
+
10
+ import shutil
11
+ import subprocess
12
+
13
+ from opencos.util import debug
14
+
15
+ vscode_path = shutil.which('code')
16
+
17
+ INIT_HAS_RUN = False
18
+ EXTENSIONS = {} # dict of {name: version} for VScode extensions of name
19
+
20
+
21
+ def init() -> None:
22
+ '''Sets INIT_HAS_RUN=True (only runs once) and one of TOOL_IS[tool] = True'''
23
+ global INIT_HAS_RUN # pylint: disable=global-statement
24
+
25
+ if INIT_HAS_RUN:
26
+ return
27
+
28
+ INIT_HAS_RUN = True
29
+
30
+ if not vscode_path:
31
+ return
32
+
33
+ proc = None
34
+ try:
35
+ proc = subprocess.run([vscode_path, '--list-extensions', '--show-versions'],
36
+ capture_output=True, check=False)
37
+ except Exception as e:
38
+ debug(f'vscode --list-extensions --show-versions: exception {e}')
39
+
40
+ if proc is None or proc.returncode != 0:
41
+ return
42
+
43
+ for line in proc.stdout.decode('utf-8', errors='replace').split('\n'):
44
+ if '@' in line:
45
+ parts = line.split('@')
46
+ if parts[0] and parts[1]:
47
+ EXTENSIONS[parts[0]] = parts[1]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencos-eda
3
- Version: 0.2.51
3
+ Version: 0.2.52
4
4
  Summary: A simple Python package for wrapping RTL simuliatons and synthesis
5
5
  Author-email: Simon Sabato <simon@cognichip.ai>, Drew Ranck <drew@cognichip.ai>
6
6
  Project-URL: Homepage, https://github.com/cognichip/opencos
@@ -15,4 +15,5 @@ Requires-Dist: schema>=0.7.7
15
15
  Requires-Dist: toml>=0.10.2
16
16
  Requires-Dist: yamllint>=1.35.1
17
17
  Requires-Dist: PySerial>=3.5
18
+ Requires-Dist: cocotb>=2.0.0b1
18
19
  Dynamic: license-file
@@ -2,10 +2,10 @@ opencos/__init__.py,sha256=RwJA9oc1uUlvNX7v5zoqwjnSRNq2NZwRlHqtS-ICJkI,122
2
2
  opencos/_version.py,sha256=XiHFZjCofkkZvI9befTFKsRt5zLouUo1CIwhW4srWU0,582
3
3
  opencos/_waves_pkg.sv,sha256=1lbhQOVGc3t_R8czYjP40hssP0I3FlZOpHTkI7yKFbI,1251
4
4
  opencos/deps_schema.py,sha256=pdJpKmfXxCYZsfXTxsnACw-s6Mzk55QH06ZSA5uqPV0,15454
5
- opencos/eda.py,sha256=rYBt4Qu2y45zdwD4jDx_p3l2u5LMS1nsaGQCP2ZYdwY,20883
5
+ opencos/eda.py,sha256=bK5wX9EUib7UkIR0LcM_kGBjv_eGxjBBYjYkutYLTgI,21501
6
6
  opencos/eda_base.py,sha256=q0uKEzAMzLmqb4jMBcGv7rTpdKnfrhnMZo7MyB8YN8Y,95706
7
- opencos/eda_config.py,sha256=Xma8w7kBLCNPCSrCKpceieU7GUBv4i54XZlHjnZ95X4,11353
8
- opencos/eda_config_defaults.yml,sha256=PdR3xHWQIdGeIyCTr2c3W9Vfq9RC1E9HbqiSydyDYZE,12417
7
+ opencos/eda_config.py,sha256=R6tbcuBJWA0vv1s9K_L928EVbjhv-lqDFLZqk_UGe9w,11382
8
+ opencos/eda_config_defaults.yml,sha256=dy5MKm5wbam8aT39frSLbJJm6LPJpoeI7PO_XOpt27A,13147
9
9
  opencos/eda_config_max_verilator_waivers.yml,sha256=lTAU4IOEbUWVlPzuer1YYhIyxpPINeA4EJqcRIT-Ymk,840
10
10
  opencos/eda_config_reduced.yml,sha256=cQ9jY4J7EvAbeHTiP6bvpDSVJAYiitjLZPSxxLKIEbk,1440
11
11
  opencos/eda_deps_bash_completion.bash,sha256=jMkQKY82HBgOnQeMdA1hMrXguRFtB52SMBxUemKovL4,1958
@@ -18,7 +18,7 @@ opencos/files.py,sha256=aoq0O2KfISzZb-Vi_q_0TTGBER9xJc--FkVZf0ga7pA,1549
18
18
  opencos/names.py,sha256=iC37PV7Pz0PicTDg09vbQ9NXAj-5m6RKrWHkkcHB8As,1145
19
19
  opencos/peakrdl_cleanup.py,sha256=vHNGtalTrIVP335PhRjPt9RhoccgpK1HJAi-E4M8Kc8,736
20
20
  opencos/seed.py,sha256=IL9Yg-r9SLSRseMVWaEHmuw2_DNi_eyut11EafoNTsU,942
21
- opencos/util.py,sha256=hnE66Mu7Uu-OGdAE5gkJ8nsKc_zUNMptXcuBFkm30wU,33975
21
+ opencos/util.py,sha256=L36MM2cqR0xWbs617AJN52JvrTkrgggELHG3Y3s2jPI,34089
22
22
  opencos/commands/__init__.py,sha256=DtOA56oWJu68l-_1_7Gdv0N-gtXVB3-p9IhGzAYex8U,1014
23
23
  opencos/commands/build.py,sha256=jI5ul53qfwn6X-yfSdSQIcLBhGtzZUk7r_wKBBmKJI0,1425
24
24
  opencos/commands/elab.py,sha256=m6Gk03wSzX8UkcmReooK7turF7LpqO0IcdOZwJ8XiyI,1596
@@ -34,7 +34,7 @@ opencos/commands/sweep.py,sha256=ni4XFgnFF8HLXtwPhETyLWfvc2kgtm4bcxFcKzUhkf0,934
34
34
  opencos/commands/synth.py,sha256=quB-HWS4LKYTiFBHiYarQi4pMnRmt12wQTZpi14VvlE,4355
35
35
  opencos/commands/targets.py,sha256=_jRNhm2Fqj0fmMvTw6Ba39DCsRHf_r_uZCy_R064kpA,1472
36
36
  opencos/commands/upload.py,sha256=nlb4nlxrDCQPcabEmH3nP19g4PFILDqFDab4LwJ95Z4,796
37
- opencos/commands/waves.py,sha256=dsWwtjpDgH-YsiIjJgqTvteY3OZ48UnEAWc3blV2Fog,7055
37
+ opencos/commands/waves.py,sha256=s8xYyNFBDqUtGdpioVIHWDKE4ZSnY6e1gdZNIm7yXBE,7654
38
38
  opencos/deps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
39
  opencos/deps/defaults.py,sha256=4VgTRXf0hSJrj4tMk0t-mmvgEaaQQFDIYrxWrcKfYWk,1241
40
40
  opencos/deps/deps_commands.py,sha256=OlqueYFK8U83loasok3xJGzUDpNcj2DPk37332DfmRo,17039
@@ -53,7 +53,7 @@ opencos/tests/test_eda.py,sha256=bg7SjSVCJSJ2lk51ddfjuVckMOps6_HU-3tCwAIRO7c,391
53
53
  opencos/tests/test_eda_elab.py,sha256=VyYiaLLyfJi26FkoN9p_Kb9cMHfY-0u_0Mx1iVpsg-I,2657
54
54
  opencos/tests/test_eda_synth.py,sha256=LOM8CKpNyjka_sKS2c00YObOAQeVgpRmuM12Bn-PHqU,5234
55
55
  opencos/tests/test_oc_cli.py,sha256=w-F-LjSSWVql3D2WG8tcV4_C52i-hL_2WT3oDpKQn9s,734
56
- opencos/tests/test_tools.py,sha256=HEjKm86OMcrZznJMiiDFsDBN8FlBf4LR9egGO1pOh5c,5285
56
+ opencos/tests/test_tools.py,sha256=1x6zaDhhkkzcKpZjCdWqzYJPzrK_pnqBeUWiuy8cqAg,12128
57
57
  opencos/tests/deps_files/command_order/DEPS.yml,sha256=vloOzWZ5qU3yGNFaDlrAJdEzYxK6qf8gfac3zqF-0FI,438
58
58
  opencos/tests/deps_files/error_msgs/DEPS.yml,sha256=fYvHouIscOlr8V28bqx9SoxRBpDBLX4AG-AkVXh8qbo,717
59
59
  opencos/tests/deps_files/iverilog_test/DEPS.yml,sha256=vDylEuLt642lhRSvOr3F5ziB5lhPSwkaUGN4_mWJw-c,40
@@ -62,6 +62,7 @@ opencos/tests/deps_files/non_sv_reqs/DEPS.yml,sha256=VZkahO1AKhD9GUV5lK8VwUONEi5
62
62
  opencos/tests/deps_files/tags_with_tools/DEPS.yml,sha256=-5U1qfJElgpVhmkLEu3lYuvDYva8kDlt6JOdB9jidmc,1377
63
63
  opencos/tests/deps_files/test_err_fatal/DEPS.yml,sha256=GnXIUJvshQWR9PlYxX67T53ejf5KhDbtD8sUJB4Rwd0,95
64
64
  opencos/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
65
+ opencos/tools/cocotb.py,sha256=0AzT2pJWKwepZMT_hlgYYMV_Fr3Qr4gH7BgMDtNuLK0,16614
65
66
  opencos/tools/invio.py,sha256=q9E9n6xsozDfar-1rLvJEZbCpPb_bQEy6WKEI3KS3dk,3163
66
67
  opencos/tools/invio_helpers.py,sha256=1au4CYmV5aC7DHjaZBNemydH6Eq0i-Yt5L3HyKfQOfY,7638
67
68
  opencos/tools/invio_yosys.py,sha256=asSjbdPjBXB76KxNZIhoDRn2DoXKsZEQ1YDX_WBzKiA,6019
@@ -74,7 +75,7 @@ opencos/tools/slang.py,sha256=mFw58vhnCTRR9yaQ2zHPlNB5HKSf3Y078XcaVnpLaAc,7798
74
75
  opencos/tools/slang_yosys.py,sha256=3fyLRRdTXhSppNtUhhUl00oG-cT9TyyPTH6JvasS9ZE,9804
75
76
  opencos/tools/surelog.py,sha256=dtj3ApAKoQasnGdg42n9DPgeqoJ5nCuurIkIO3G5ZCY,5011
76
77
  opencos/tools/tabbycad_yosys.py,sha256=2LePPgYXBVdsy7YcffPIWN-I0B7queLQ_f_pme2SCGw,7803
77
- opencos/tools/verilator.py,sha256=h5VScZyJYxk2cXDRrTTD47C_yAVHuyXv9_p7UL1o5mk,19486
78
+ opencos/tools/verilator.py,sha256=dIhrvLH9hHiTV_DWQbMNlAjolKctCbBMO9u9V7yAHuE,21400
78
79
  opencos/tools/vivado.py,sha256=roM0MxrHoNIT_DsUiql1vQFlez2ql4qhwAfB-rGH4Qg,40485
79
80
  opencos/tools/yosys.py,sha256=UKWzvc2rSi3J9U2TROqRbmePdBmjskap664giumeRBk,26323
80
81
  opencos/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -82,11 +83,12 @@ opencos/utils/markup_helpers.py,sha256=A8Ev5UJ4EVKjdcF2g85SQbjdPZR4jGpNqCLaBy_4v
82
83
  opencos/utils/status_constants.py,sha256=aQui30ohPCEaWNDh2iujJQ_KQa3plry_rk7uDzS3vWk,603
83
84
  opencos/utils/str_helpers.py,sha256=DOkwfKJR6aENM3U2BkJ41ELDU5Uj_zyhEfxuaQEcpEY,3352
84
85
  opencos/utils/subprocess_helpers.py,sha256=xemAGPey6M0sWY_FElvr-Z0phCfdjaC-znP8FKihPaE,3535
86
+ opencos/utils/vscode_helper.py,sha256=2YPjcDH_vTlwJvcITyMfvx8dLzPSRKzAvFRJz7BaJk8,1332
85
87
  opencos/utils/vsim_helper.py,sha256=2voGRZI2iAQ2Pv2ZI5g2why6xpgig-To8im-LVXtuDU,1517
86
- opencos_eda-0.2.51.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
87
- opencos_eda-0.2.51.dist-info/licenses/LICENSE.spdx,sha256=8gn1610RMP6eFgT3Hm6q9VKXt0RvdTItL_oxMo72jII,189
88
- opencos_eda-0.2.51.dist-info/METADATA,sha256=XAX9j35DEWh0nLU7Gt8unosqGzK9dGeqHtGcj8IgK-8,604
89
- opencos_eda-0.2.51.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
90
- opencos_eda-0.2.51.dist-info/entry_points.txt,sha256=6n1T5NwVYDhN5l1h5zmyT197G4pE0SySDreB0QJzJR0,218
91
- opencos_eda-0.2.51.dist-info/top_level.txt,sha256=J4JDP-LpRyJqPNeh9bSjx6yrLz2Mk0h6un6YLmtqql4,8
92
- opencos_eda-0.2.51.dist-info/RECORD,,
88
+ opencos_eda-0.2.52.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
89
+ opencos_eda-0.2.52.dist-info/licenses/LICENSE.spdx,sha256=8gn1610RMP6eFgT3Hm6q9VKXt0RvdTItL_oxMo72jII,189
90
+ opencos_eda-0.2.52.dist-info/METADATA,sha256=NLxBxJ0aSl6Vfizn3tZqnskXn6VuWpDSdZQdKb4BGig,635
91
+ opencos_eda-0.2.52.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
92
+ opencos_eda-0.2.52.dist-info/entry_points.txt,sha256=6n1T5NwVYDhN5l1h5zmyT197G4pE0SySDreB0QJzJR0,218
93
+ opencos_eda-0.2.52.dist-info/top_level.txt,sha256=J4JDP-LpRyJqPNeh9bSjx6yrLz2Mk0h6un6YLmtqql4,8
94
+ opencos_eda-0.2.52.dist-info/RECORD,,