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 +12 -2
- opencos/eda.py +16 -3
- opencos/eda_config.py +1 -1
- opencos/eda_config_defaults.yml +34 -0
- opencos/tests/test_tools.py +195 -1
- opencos/tools/cocotb.py +483 -0
- opencos/tools/verilator.py +31 -0
- opencos/util.py +2 -0
- opencos/utils/vscode_helper.py +47 -0
- {opencos_eda-0.2.51.dist-info → opencos_eda-0.2.52.dist-info}/METADATA +2 -1
- {opencos_eda-0.2.51.dist-info → opencos_eda-0.2.52.dist-info}/RECORD +16 -14
- {opencos_eda-0.2.51.dist-info → opencos_eda-0.2.52.dist-info}/WHEEL +0 -0
- {opencos_eda-0.2.51.dist-info → opencos_eda-0.2.52.dist-info}/entry_points.txt +0 -0
- {opencos_eda-0.2.51.dist-info → opencos_eda-0.2.52.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.2.51.dist-info → opencos_eda-0.2.52.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.2.51.dist-info → opencos_eda-0.2.52.dist-info}/top_level.txt +0 -0
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 '
|
|
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 '
|
|
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(
|
|
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([
|
opencos/eda_config_defaults.yml
CHANGED
|
@@ -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
|
opencos/tests/test_tools.py
CHANGED
|
@@ -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
|
-
|
|
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"
|
opencos/tools/cocotb.py
ADDED
|
@@ -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 []
|
opencos/tools/verilator.py
CHANGED
|
@@ -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.
|
|
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=
|
|
5
|
+
opencos/eda.py,sha256=bK5wX9EUib7UkIR0LcM_kGBjv_eGxjBBYjYkutYLTgI,21501
|
|
6
6
|
opencos/eda_base.py,sha256=q0uKEzAMzLmqb4jMBcGv7rTpdKnfrhnMZo7MyB8YN8Y,95706
|
|
7
|
-
opencos/eda_config.py,sha256=
|
|
8
|
-
opencos/eda_config_defaults.yml,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
87
|
-
opencos_eda-0.2.
|
|
88
|
-
opencos_eda-0.2.
|
|
89
|
-
opencos_eda-0.2.
|
|
90
|
-
opencos_eda-0.2.
|
|
91
|
-
opencos_eda-0.2.
|
|
92
|
-
opencos_eda-0.2.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|