opencos-eda 0.2.57__py3-none-any.whl → 0.3.0__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/_version.py +6 -3
- opencos/_waves_pkg.sv +34 -2
- opencos/commands/build.py +1 -0
- opencos/commands/export.py +1 -0
- opencos/commands/flist.py +1 -0
- opencos/commands/lec.py +1 -0
- opencos/commands/proj.py +1 -0
- opencos/commands/shell.py +4 -0
- opencos/commands/sim.py +47 -1
- opencos/commands/synth.py +4 -0
- opencos/deps/defaults.py +15 -7
- opencos/deps/deps_commands.py +84 -74
- opencos/deps_schema.py +3 -0
- opencos/eda.py +2 -1
- opencos/eda_base.py +61 -16
- opencos/eda_config_defaults.yml +2 -1
- opencos/tests/deps_files/command_order/DEPS.yml +11 -0
- opencos/tests/helpers.py +48 -19
- opencos/tests/test_deps_helpers.py +37 -25
- opencos/tests/test_eda.py +26 -60
- opencos/tools/modelsim_ase.py +17 -9
- opencos/tools/riviera.py +31 -6
- opencos/tools/verilator.py +28 -38
- opencos/util.py +63 -12
- opencos/utils/vscode_helper.py +1 -1
- opencos/utils/vsim_helper.py +1 -1
- {opencos_eda-0.2.57.dist-info → opencos_eda-0.3.0.dist-info}/METADATA +2 -1
- {opencos_eda-0.2.57.dist-info → opencos_eda-0.3.0.dist-info}/RECORD +33 -33
- {opencos_eda-0.2.57.dist-info → opencos_eda-0.3.0.dist-info}/WHEEL +0 -0
- {opencos_eda-0.2.57.dist-info → opencos_eda-0.3.0.dist-info}/entry_points.txt +0 -0
- {opencos_eda-0.2.57.dist-info → opencos_eda-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.2.57.dist-info → opencos_eda-0.3.0.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.2.57.dist-info → opencos_eda-0.3.0.dist-info}/top_level.txt +0 -0
opencos/eda_base.py
CHANGED
|
@@ -506,12 +506,11 @@ class Command: # pylint: disable=too-many-public-methods
|
|
|
506
506
|
self.args[key].update(value)
|
|
507
507
|
|
|
508
508
|
elif isinstance(cur_value, list):
|
|
509
|
-
# if list, append (
|
|
509
|
+
# if list, append (allow duplicates)
|
|
510
510
|
if isinstance(value, list):
|
|
511
511
|
# new value also a list
|
|
512
512
|
for x in value:
|
|
513
|
-
|
|
514
|
-
self.args[key].append(x)
|
|
513
|
+
self.args[key].append(x)
|
|
515
514
|
elif value not in cur_value:
|
|
516
515
|
self.args[key].append(value)
|
|
517
516
|
|
|
@@ -970,13 +969,16 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
970
969
|
self.targets_dict = {} # key = targets that we've already processed in DEPS files
|
|
971
970
|
self.last_added_source_file_inferred_top = ''
|
|
972
971
|
|
|
973
|
-
self.
|
|
972
|
+
self.has_pre_compile_dep_shell_commands = False
|
|
973
|
+
self.has_post_tool_dep_shell_commands = False
|
|
974
974
|
|
|
975
975
|
|
|
976
976
|
def run_dep_commands(self) -> None:
|
|
977
|
-
'''Run shell/peakrdl style commands from DEPS files
|
|
977
|
+
'''Run shell/peakrdl style commands from DEPS files, this is peformed before
|
|
978
978
|
|
|
979
|
-
These are deferred to maintain the deps ordering, and
|
|
979
|
+
any tool compile step. These are deferred to maintain the deps ordering, and
|
|
980
|
+
run in that order. Note this will NOT run any DEPS command marked with
|
|
981
|
+
run-after-tool=True.
|
|
980
982
|
'''
|
|
981
983
|
self.run_dep_shell_commands()
|
|
982
984
|
# Update any work_dir_add_srcs@ in our self.files, self.files_v, etc, b/c
|
|
@@ -986,17 +988,41 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
986
988
|
self.update_non_source_files_in_work_dir()
|
|
987
989
|
|
|
988
990
|
|
|
989
|
-
def
|
|
990
|
-
'''
|
|
991
|
+
def run_post_tool_dep_commands(self) -> None:
|
|
992
|
+
'''Run shell style commands from DEPS files that have been marked with
|
|
993
|
+
|
|
994
|
+
run-after-tool=True. Note these are skipped if any args like
|
|
995
|
+
stop-before- or stop-after- are set.
|
|
996
|
+
'''
|
|
997
|
+
|
|
998
|
+
self.run_dep_shell_commands(filter_run_after_tool=True)
|
|
999
|
+
|
|
1000
|
+
|
|
1001
|
+
def run_dep_shell_commands( # pylint: disable=too-many-branches,too-many-locals
|
|
1002
|
+
self, filter_run_after_tool: bool = False
|
|
1003
|
+
) -> None:
|
|
1004
|
+
'''Runs collected shell command from DEPS files.
|
|
1005
|
+
|
|
1006
|
+
There are two flavors of shell commands: with or without 'run-after-tool'
|
|
1007
|
+
set. The default is to run shell command before the compile step of any tool,
|
|
1008
|
+
by calling this method with default pre_compile=True before any tool runs
|
|
1009
|
+
(for generating code, etc). However, it may be useful to run shell commands
|
|
1010
|
+
after a tool is complete (check timing, coverage, etc).
|
|
1011
|
+
'''
|
|
991
1012
|
|
|
992
1013
|
# Runs from self.args['work-dir']
|
|
993
1014
|
all_cmds_lists = []
|
|
994
1015
|
|
|
995
1016
|
log_fnames_count = {} # count per target_node.
|
|
996
1017
|
|
|
997
|
-
|
|
1018
|
+
filtered_dep_shell_commands = []
|
|
1019
|
+
for value in self.dep_shell_commands:
|
|
1020
|
+
if value['attributes']['run-after-tool'] == filter_run_after_tool:
|
|
1021
|
+
filtered_dep_shell_commands.append(value)
|
|
1022
|
+
|
|
1023
|
+
|
|
1024
|
+
for i, d in enumerate(filtered_dep_shell_commands):
|
|
998
1025
|
clist = util.ShellCommandList(d['exec_list'])
|
|
999
|
-
run_from_work_dir = d['run_from_work_dir'] # default True
|
|
1000
1026
|
log = clist.tee_fpath
|
|
1001
1027
|
target_node = d["target_node"]
|
|
1002
1028
|
if clist.tee_fpath is None:
|
|
@@ -1011,29 +1037,48 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1011
1037
|
# (or tee name from DEPS.yml)
|
|
1012
1038
|
[f'# command {i}: target: {d["target_path"]} : {target_node} --> {log}'],
|
|
1013
1039
|
]
|
|
1014
|
-
if not
|
|
1040
|
+
if not d['attributes']['run-from-work-dir']:
|
|
1015
1041
|
all_cmds_lists.append([f'cd {d["target_path"]}'])
|
|
1016
1042
|
|
|
1017
1043
|
# actual command (list or util.ShellCommandList)
|
|
1018
1044
|
all_cmds_lists.append(clist)
|
|
1019
1045
|
|
|
1020
|
-
if not
|
|
1046
|
+
if not d['attributes']['run-from-work-dir']:
|
|
1021
1047
|
all_cmds_lists.append([f'cd {os.path.abspath(self.args["work-dir"])}'])
|
|
1022
1048
|
|
|
1023
1049
|
d['exec_list'] = clist # update to tee_fpath is set.
|
|
1024
1050
|
|
|
1025
1051
|
if all_cmds_lists:
|
|
1052
|
+
if filter_run_after_tool:
|
|
1053
|
+
filename='post_tool_dep_shell_commands.sh'
|
|
1054
|
+
self.has_post_tool_dep_shell_commands = True
|
|
1055
|
+
else:
|
|
1056
|
+
filename='pre_compile_dep_shell_commands.sh'
|
|
1057
|
+
self.has_pre_compile_dep_shell_commands = True
|
|
1058
|
+
|
|
1026
1059
|
util.write_shell_command_file(
|
|
1027
|
-
dirpath=self.args['work-dir'], filename=
|
|
1060
|
+
dirpath=self.args['work-dir'], filename=filename,
|
|
1028
1061
|
command_lists=all_cmds_lists
|
|
1029
1062
|
)
|
|
1030
|
-
self.has_dep_shell_commands = True
|
|
1031
1063
|
|
|
1032
|
-
|
|
1064
|
+
|
|
1065
|
+
if all_cmds_lists and filter_run_after_tool and \
|
|
1066
|
+
any(self.args.get(x, False) for x in (
|
|
1067
|
+
"stop-before-compile",
|
|
1068
|
+
"stop-after-compile",
|
|
1069
|
+
"stop-after-elaborate"
|
|
1070
|
+
)):
|
|
1071
|
+
args_set = [key for key,value in self.args.items() if \
|
|
1072
|
+
key.startswith('stop-') and value]
|
|
1073
|
+
util.info(f'Skipping DEPS run-after-tool commands due to args {args_set}')
|
|
1074
|
+
util.debug(f'Skipped commands: {filtered_dep_shell_commands=}')
|
|
1075
|
+
return
|
|
1076
|
+
|
|
1077
|
+
for i, d in enumerate(filtered_dep_shell_commands):
|
|
1033
1078
|
util.info(f'run_dep_shell_commands {i=}: {d=}')
|
|
1034
1079
|
clist = util.ShellCommandList(d['exec_list'])
|
|
1035
1080
|
tee_fpath=clist.tee_fpath
|
|
1036
|
-
if d['
|
|
1081
|
+
if d['attributes']['run-from-work-dir']:
|
|
1037
1082
|
run_from_dir = self.args['work-dir']
|
|
1038
1083
|
else:
|
|
1039
1084
|
# Run from the target's directory (not the `eda` caller $PWD)
|
opencos/eda_config_defaults.yml
CHANGED
|
@@ -229,6 +229,7 @@ tools:
|
|
|
229
229
|
riviera:
|
|
230
230
|
defines:
|
|
231
231
|
OC_TOOL_RIVIERA: 1
|
|
232
|
+
RIVIERA: 1
|
|
232
233
|
log-bad-strings:
|
|
233
234
|
- "Error:"
|
|
234
235
|
log-must-strings:
|
|
@@ -292,7 +293,7 @@ tools:
|
|
|
292
293
|
- 3009 # 3009: [TSCALE] - Module 'myname' does not have a timeunit/timeprecision
|
|
293
294
|
# specification in effect, but other modules do.
|
|
294
295
|
simulate-waves-args: |
|
|
295
|
-
-voptargs=+acc=
|
|
296
|
+
-voptargs=+acc=bcnprst
|
|
296
297
|
|
|
297
298
|
|
|
298
299
|
iverilog:
|
|
@@ -18,3 +18,14 @@ target_echo_hi_bye:
|
|
|
18
18
|
target_test:
|
|
19
19
|
deps: target_echo_hi_bye
|
|
20
20
|
top: foo
|
|
21
|
+
|
|
22
|
+
target_test_with_post_tool_commands:
|
|
23
|
+
deps:
|
|
24
|
+
# In this test, we want to put a new command in the front of the ordered "deps" list,
|
|
25
|
+
# but with run-after-tool=true, so it should run after any of the normal pre-compile
|
|
26
|
+
# shell commands.
|
|
27
|
+
- commands:
|
|
28
|
+
- shell: echo "final goodbye"
|
|
29
|
+
run-after-tool: true
|
|
30
|
+
- target_echo_hi_bye
|
|
31
|
+
top: foo
|
opencos/tests/helpers.py
CHANGED
|
@@ -13,6 +13,7 @@ from opencos import eda
|
|
|
13
13
|
from opencos import deps_schema
|
|
14
14
|
from opencos.utils.markup_helpers import yaml_safe_load
|
|
15
15
|
from opencos.utils import status_constants
|
|
16
|
+
from opencos.utils.subprocess_helpers import subprocess_run_background
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
def eda_wrap_is_sim_fail(rc: int, quiet: bool = False) -> bool:
|
|
@@ -157,6 +158,15 @@ class Helpers:
|
|
|
157
158
|
DEFAULT_DIR = ''
|
|
158
159
|
DEFAULT_LOG_DIR = os.getcwd()
|
|
159
160
|
DEFAULT_LOG = os.path.join(DEFAULT_LOG_DIR, '.pytest.eda.log')
|
|
161
|
+
|
|
162
|
+
# How should the job run? subprocess? eda_wrap? eda.main?
|
|
163
|
+
# Note - if using eda.main, args like --debug will persist in opencos.util.args,
|
|
164
|
+
# so if you need those to be re-loaded, set RUN_IN_SUBPROCESS=True.
|
|
165
|
+
# Note - if you mess with os.enviorn, it may persist through subprocess.
|
|
166
|
+
RUN_IN_SUBPROCESS = True
|
|
167
|
+
USE_EDA_WRAP = True
|
|
168
|
+
PRESERVE_ENV = False
|
|
169
|
+
|
|
160
170
|
def chdir(self):
|
|
161
171
|
'''Changes directory to self.DEFAULT_DIR and removes eda.work, eda.export paths'''
|
|
162
172
|
chdir_remove_work_dir('', self.DEFAULT_DIR)
|
|
@@ -173,7 +183,11 @@ class Helpers:
|
|
|
173
183
|
ret = os.path.join(self.DEFAULT_LOG_DIR, right)
|
|
174
184
|
return ret
|
|
175
185
|
|
|
176
|
-
def log_it(
|
|
186
|
+
def log_it(
|
|
187
|
+
self, command_str: str, logfile=None, use_eda_wrap: bool = True,
|
|
188
|
+
run_in_subprocess: bool = False,
|
|
189
|
+
preserve_env: bool = False
|
|
190
|
+
) -> int:
|
|
177
191
|
'''Replacement for calling eda.main or eda_wrap, when you want an internal logfile
|
|
178
192
|
|
|
179
193
|
Usage:
|
|
@@ -183,6 +197,10 @@ class Helpers:
|
|
|
183
197
|
Note this will run with --no-default-log to avoid a Windows problem with stomping
|
|
184
198
|
on a log file.
|
|
185
199
|
'''
|
|
200
|
+
|
|
201
|
+
if self.PRESERVE_ENV or preserve_env:
|
|
202
|
+
saved_env = os.environ.copy()
|
|
203
|
+
|
|
186
204
|
logfile = self._resolve_logfile(logfile)
|
|
187
205
|
rc = 50
|
|
188
206
|
|
|
@@ -191,13 +209,26 @@ class Helpers:
|
|
|
191
209
|
# look at eda.work/{target}.sim/sim.log or xsim.log.
|
|
192
210
|
print(f'{os.getcwd()=}')
|
|
193
211
|
print(f'{command_str=}')
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
212
|
+
if run_in_subprocess or self.RUN_IN_SUBPROCESS:
|
|
213
|
+
command_list = ['eda', '--no-default-log'] + command_str.split()
|
|
214
|
+
_, _, rc = subprocess_run_background(
|
|
215
|
+
work_dir=self.DEFAULT_DIR,
|
|
216
|
+
command_list=command_list,
|
|
217
|
+
background=True,
|
|
218
|
+
tee_fpath=logfile
|
|
219
|
+
)
|
|
220
|
+
else:
|
|
221
|
+
with open(logfile, 'w', encoding='utf-8') as f:
|
|
222
|
+
with redirect_stdout(f), redirect_stderr(f):
|
|
223
|
+
if use_eda_wrap or self.USE_EDA_WRAP:
|
|
224
|
+
rc = eda_wrap('--no-default-log', *(command_str.split()))
|
|
225
|
+
else:
|
|
226
|
+
rc = eda.main('--no-default-log', *(command_str.split()))
|
|
227
|
+
print(f'Wrote: {os.path.abspath(logfile)=}')
|
|
228
|
+
|
|
229
|
+
if self.PRESERVE_ENV or preserve_env:
|
|
230
|
+
os.environ = saved_env
|
|
231
|
+
|
|
201
232
|
return rc
|
|
202
233
|
|
|
203
234
|
def is_in_log(self, *want_str, logfile=None, windows_path_support=False):
|
|
@@ -216,33 +247,31 @@ class Helpers:
|
|
|
216
247
|
'''gets all log lines with any of want_str args are in the logfile, or self.DEFAULT_LOG'''
|
|
217
248
|
logfile = self._resolve_logfile(logfile)
|
|
218
249
|
ret_list = []
|
|
219
|
-
want_str0 = ' '.join(list(want_str))
|
|
220
|
-
want_str1 = want_str0.replace('/', '\\')
|
|
221
250
|
with open(logfile, encoding='utf-8') as f:
|
|
222
251
|
for line in f.readlines():
|
|
223
|
-
if
|
|
252
|
+
if any(x in line for x in list(want_str)):
|
|
224
253
|
ret_list.append(line)
|
|
225
|
-
elif windows_path_support and
|
|
254
|
+
elif windows_path_support and \
|
|
255
|
+
any(x.replace('/', '\\') in line for x in list(want_str)):
|
|
226
256
|
ret_list.append(line)
|
|
227
257
|
return ret_list
|
|
228
258
|
|
|
229
259
|
def get_log_words_with(self, *want_str, logfile=None, windows_path_support=False):
|
|
230
|
-
'''gets all log
|
|
260
|
+
'''gets all log words with any of *want_str within a single word
|
|
231
261
|
in the logfile or self.DEFAULT_LOG
|
|
232
262
|
'''
|
|
233
263
|
logfile = self._resolve_logfile(logfile)
|
|
234
264
|
ret_list = []
|
|
235
|
-
want_str0 = ' '.join(list(want_str))
|
|
236
|
-
want_str1 = want_str0.replace('/', '\\')
|
|
237
265
|
with open(logfile, encoding='utf-8') as f:
|
|
238
266
|
for line in f.readlines():
|
|
239
|
-
if
|
|
267
|
+
if any(x in line for x in list(want_str)):
|
|
240
268
|
for word in line.split():
|
|
241
|
-
if
|
|
269
|
+
if any(x in word for x in list(want_str)):
|
|
242
270
|
ret_list.append(word)
|
|
243
|
-
elif windows_path_support and
|
|
271
|
+
elif windows_path_support and \
|
|
272
|
+
any(x.replace('/', '\\') in line for x in list(want_str)):
|
|
244
273
|
for word in line.split():
|
|
245
|
-
if
|
|
274
|
+
if any(x.replace('/', '\\') in word for x in list(want_str)):
|
|
246
275
|
ret_list.append(word)
|
|
247
276
|
|
|
248
277
|
return ret_list
|
|
@@ -59,26 +59,26 @@ def test_get_all_targets_eda_multi():
|
|
|
59
59
|
|
|
60
60
|
def test_parse_deps_shell_str__no_parse():
|
|
61
61
|
line = 'some_file.sv'
|
|
62
|
-
d = deps_commands.parse_deps_shell_str(line, '', '')
|
|
62
|
+
d = deps_commands.parse_deps_shell_str(line, '', '', attributes={})
|
|
63
63
|
assert not d, f'{d=}'
|
|
64
64
|
|
|
65
65
|
line = 'some_target:'
|
|
66
|
-
d = deps_commands.parse_deps_shell_str(line, '', '')
|
|
66
|
+
d = deps_commands.parse_deps_shell_str(line, '', '', attributes={})
|
|
67
67
|
assert not d, f'{d=}'
|
|
68
68
|
|
|
69
69
|
line = ' csr@some_file.sv'
|
|
70
|
-
d = deps_commands.parse_deps_shell_str(line, '', '')
|
|
70
|
+
d = deps_commands.parse_deps_shell_str(line, '', '', attributes={})
|
|
71
71
|
assert not d, f'{d=}'
|
|
72
72
|
|
|
73
73
|
def test_parse_deps_shell_str__cp():
|
|
74
74
|
line = ' shell@ cp ./oclib_fifo_test.sv oclib_fifo_test_COPY.sv ;'
|
|
75
|
-
d = deps_commands.parse_deps_shell_str(line, '', '')
|
|
75
|
+
d = deps_commands.parse_deps_shell_str(line, '', '', attributes={})
|
|
76
76
|
assert d, f'{d=}'
|
|
77
77
|
assert d['exec_list'] == ['cp', './oclib_fifo_test.sv', 'oclib_fifo_test_COPY.sv', ';'], f'{d=}'
|
|
78
78
|
|
|
79
79
|
def test_parse_deps_shell_str__echo():
|
|
80
80
|
line = ' shell@echo "hello world"'
|
|
81
|
-
d = deps_commands.parse_deps_shell_str(line, '', '')
|
|
81
|
+
d = deps_commands.parse_deps_shell_str(line, '', '', attributes={})
|
|
82
82
|
assert d, f'{d=}'
|
|
83
83
|
assert d['exec_list'] == ['echo', '"hello', 'world"'], f'{d=}'
|
|
84
84
|
|
|
@@ -88,7 +88,9 @@ def test_parse_deps_shell_str__enable_filepath_replacement():
|
|
|
88
88
|
module_dir = os.path.dirname(os.path.abspath(__file__))
|
|
89
89
|
os.chdir(module_dir)
|
|
90
90
|
line = 'shell@cp ../deps/deps_commands.py .pytest.copied.py'
|
|
91
|
-
d = deps_commands.parse_deps_shell_str(
|
|
91
|
+
d = deps_commands.parse_deps_shell_str(
|
|
92
|
+
line, target_path='./', target_node='foo_target', attributes={}
|
|
93
|
+
)
|
|
92
94
|
assert d, f'{d=}'
|
|
93
95
|
spath = os.path.abspath(os.path.join('..', 'deps', 'deps_commands.py'))
|
|
94
96
|
assert d['exec_list'] == ['cp', spath, '.pytest.copied.py'], f'{d=}'
|
|
@@ -100,8 +102,10 @@ def test_parse_deps_shell_str__disable_filepath_replacement():
|
|
|
100
102
|
module_dir = os.path.dirname(os.path.abspath(__file__))
|
|
101
103
|
os.chdir(module_dir)
|
|
102
104
|
line = 'shell@cp ../deps/deps_commands.py .pytest.copied.py'
|
|
103
|
-
d = deps_commands.parse_deps_shell_str(
|
|
104
|
-
|
|
105
|
+
d = deps_commands.parse_deps_shell_str(
|
|
106
|
+
line, target_path='./', target_node='foo_target',
|
|
107
|
+
attributes={'filepath-subst-target-dir': False}
|
|
108
|
+
)
|
|
105
109
|
assert d, f'{d=}'
|
|
106
110
|
assert d['exec_list'] == ['cp', '../deps/deps_commands.py', '.pytest.copied.py'], f'{d=}'
|
|
107
111
|
assert d['target_node'] == 'foo_target'
|
|
@@ -112,8 +116,10 @@ def test_parse_deps_shell_str__enable_dirpath_replacement():
|
|
|
112
116
|
module_dir = os.path.dirname(os.path.abspath(__file__))
|
|
113
117
|
os.chdir(module_dir)
|
|
114
118
|
line = 'shell@ls -ltr ./'
|
|
115
|
-
d = deps_commands.parse_deps_shell_str(
|
|
116
|
-
|
|
119
|
+
d = deps_commands.parse_deps_shell_str(
|
|
120
|
+
line, target_path='./', target_node='foo_target',
|
|
121
|
+
attributes={'dirpath-subst-target-dir': True}
|
|
122
|
+
)
|
|
117
123
|
assert d, f'{d=}'
|
|
118
124
|
assert d['exec_list'] == ['ls', '-ltr', os.path.abspath('./')], f'{d=}'
|
|
119
125
|
assert d['target_node'] == 'foo_target'
|
|
@@ -125,7 +131,10 @@ def test_parse_deps_shell_str__disable_dirpath_replacement():
|
|
|
125
131
|
module_dir = os.path.dirname(os.path.abspath(__file__))
|
|
126
132
|
os.chdir(module_dir)
|
|
127
133
|
line = 'shell@ls -ltr ./'
|
|
128
|
-
d = deps_commands.parse_deps_shell_str(
|
|
134
|
+
d = deps_commands.parse_deps_shell_str(
|
|
135
|
+
line, target_path='./', target_node='foo_target',
|
|
136
|
+
attributes={}
|
|
137
|
+
)
|
|
129
138
|
assert d, f'{d=}'
|
|
130
139
|
assert d['exec_list'] == ['ls', '-ltr', './'], f'{d=}'
|
|
131
140
|
assert d['target_node'] == 'foo_target'
|
|
@@ -134,26 +143,26 @@ def test_parse_deps_shell_str__disable_dirpath_replacement():
|
|
|
134
143
|
|
|
135
144
|
def test_parse_deps_work_dir_add_srcs__no_parse():
|
|
136
145
|
line = 'some_file.sv'
|
|
137
|
-
d = deps_commands.parse_deps_work_dir_add_srcs(line, '', '')
|
|
146
|
+
d = deps_commands.parse_deps_work_dir_add_srcs(line, '', '', {})
|
|
138
147
|
assert not d, f'{d=}'
|
|
139
148
|
|
|
140
149
|
line = 'some_target:'
|
|
141
|
-
d = deps_commands.parse_deps_work_dir_add_srcs(line, '', '')
|
|
150
|
+
d = deps_commands.parse_deps_work_dir_add_srcs(line, '', '', {})
|
|
142
151
|
assert not d, f'{d=}'
|
|
143
152
|
|
|
144
153
|
line = ' csr@some_file.sv'
|
|
145
|
-
d = deps_commands.parse_deps_work_dir_add_srcs(line, '', '')
|
|
154
|
+
d = deps_commands.parse_deps_work_dir_add_srcs(line, '', '', {})
|
|
146
155
|
assert not d, f'{d=}'
|
|
147
156
|
|
|
148
157
|
def test_parse_deps_work_dir_add_srcs__single_file():
|
|
149
158
|
line = ' work_dir_add_srcs@ single_file.txt'
|
|
150
|
-
d = deps_commands.parse_deps_work_dir_add_srcs(line, '', '')
|
|
159
|
+
d = deps_commands.parse_deps_work_dir_add_srcs(line, '', '', {})
|
|
151
160
|
assert d, f'{d=}'
|
|
152
161
|
assert d['file_list'] == ['single_file.txt']
|
|
153
162
|
|
|
154
163
|
def test_parse_deps_work_dir_add_srcs__several_file():
|
|
155
164
|
line = ' work_dir_add_srcs@ single_file.txt another.sv gen-verilog/mine.v ./gen-vhdl/wordy.vhdl'
|
|
156
|
-
d = deps_commands.parse_deps_work_dir_add_srcs(line, '', '')
|
|
165
|
+
d = deps_commands.parse_deps_work_dir_add_srcs(line, '', '', {})
|
|
157
166
|
assert d, f'{d=}'
|
|
158
167
|
assert d['file_list'] == [
|
|
159
168
|
'single_file.txt', 'another.sv', 'gen-verilog/mine.v', './gen-vhdl/wordy.vhdl'
|
|
@@ -162,34 +171,37 @@ def test_parse_deps_work_dir_add_srcs__several_file():
|
|
|
162
171
|
|
|
163
172
|
def test_parse_deps_peakrdl__no_parse():
|
|
164
173
|
line = 'some_file.sv'
|
|
165
|
-
d = deps_commands.parse_deps_peakrdl(line, '', '')
|
|
174
|
+
d = deps_commands.parse_deps_peakrdl(line, '', '', {})
|
|
166
175
|
assert not d, f'{d=}'
|
|
167
176
|
|
|
168
177
|
line = 'some_target:'
|
|
169
|
-
d = deps_commands.parse_deps_peakrdl(line, '', '')
|
|
178
|
+
d = deps_commands.parse_deps_peakrdl(line, '', '', {})
|
|
170
179
|
assert not d, f'{d=}'
|
|
171
180
|
|
|
172
181
|
line = ' csr@some_file.sv'
|
|
173
|
-
d = deps_commands.parse_deps_peakrdl(line, '', '')
|
|
182
|
+
d = deps_commands.parse_deps_peakrdl(line, '', '', {})
|
|
174
183
|
assert not d, f'{d=}'
|
|
175
184
|
|
|
176
185
|
def test_parse_deps_peakrdl__with_top():
|
|
177
186
|
line = ' peakrdl@ --cpuif axi4-lite-flat --top my_fancy_csrs ./my_csrs.rdl'
|
|
178
|
-
d = deps_commands.parse_deps_peakrdl(line, '', '')
|
|
187
|
+
d = deps_commands.parse_deps_peakrdl(line, '', '', {})
|
|
179
188
|
assert d, f'{d=}'
|
|
180
189
|
assert len(d['shell_commands_list']) > 0
|
|
181
|
-
assert d['work_dir_add_srcs']['file_list'] == ['peakrdl/my_fancy_csrs_pkg.sv',
|
|
190
|
+
assert d['work_dir_add_srcs']['file_list'] == ['peakrdl/my_fancy_csrs_pkg.sv',
|
|
191
|
+
'peakrdl/my_fancy_csrs.sv']
|
|
182
192
|
|
|
183
193
|
def test_parse_deps_peakrdl__with_top2():
|
|
184
194
|
line = ' peakrdl@ --cpuif axi4-lite-flat --top=my_fancy_csrs ./my_csrs.rdl'
|
|
185
|
-
d = deps_commands.parse_deps_peakrdl(line, '', '')
|
|
195
|
+
d = deps_commands.parse_deps_peakrdl(line, '', '', {})
|
|
186
196
|
assert d, f'{d=}'
|
|
187
197
|
assert len(d['shell_commands_list']) > 0
|
|
188
|
-
assert d['work_dir_add_srcs']['file_list'] == ['peakrdl/my_fancy_csrs_pkg.sv',
|
|
198
|
+
assert d['work_dir_add_srcs']['file_list'] == ['peakrdl/my_fancy_csrs_pkg.sv',
|
|
199
|
+
'peakrdl/my_fancy_csrs.sv']
|
|
189
200
|
|
|
190
201
|
def test_parse_deps_peakrdl__infer_top():
|
|
191
202
|
line = ' peakrdl@ --cpuif axi4-lite-flat ./my_csrs.rdl'
|
|
192
|
-
d = deps_commands.parse_deps_peakrdl(line, '', '')
|
|
203
|
+
d = deps_commands.parse_deps_peakrdl(line, '', '', {})
|
|
193
204
|
assert d, f'{d=}'
|
|
194
205
|
assert len(d['shell_commands_list']) > 0
|
|
195
|
-
assert d['work_dir_add_srcs']['file_list'] == ['peakrdl/my_csrs_pkg.sv',
|
|
206
|
+
assert d['work_dir_add_srcs']['file_list'] == ['peakrdl/my_csrs_pkg.sv',
|
|
207
|
+
'peakrdl/my_csrs.sv']
|
opencos/tests/test_eda.py
CHANGED
|
@@ -20,7 +20,6 @@ and should be more gracefully handled.
|
|
|
20
20
|
import os
|
|
21
21
|
import shutil
|
|
22
22
|
import subprocess
|
|
23
|
-
from contextlib import redirect_stdout, redirect_stderr
|
|
24
23
|
|
|
25
24
|
import pytest
|
|
26
25
|
|
|
@@ -107,6 +106,7 @@ class TestsRequiresVerilator( # pylint: disable=too-many-public-methods
|
|
|
107
106
|
assert rc == 0
|
|
108
107
|
|
|
109
108
|
|
|
109
|
+
|
|
110
110
|
def test_args_sim_tool_with_path(self):
|
|
111
111
|
'''Test for calling a tool as --tool=<tool>=</path/to/tool-exe>'''
|
|
112
112
|
verilator_fullpath = shutil.which('verilator')
|
|
@@ -659,6 +659,31 @@ class TestsRequiresIVerilog(Helpers):
|
|
|
659
659
|
assert rc == 0
|
|
660
660
|
|
|
661
661
|
|
|
662
|
+
@pytest.mark.skipif(not can_run_eda_sim(), reason='no tool found to handle command: sim')
|
|
663
|
+
class TestArgs(Helpers):
|
|
664
|
+
'''Test some args features, needs a sim tool'''
|
|
665
|
+
DEFAULT_DIR = os.path.join(THISPATH, '..', '..', 'lib', 'tests')
|
|
666
|
+
|
|
667
|
+
def test_duplicate_args(self):
|
|
668
|
+
'''Use oclib_fifo_test to make sure we don't lose (do NOT uniquify) duplicate
|
|
669
|
+
list-style args'''
|
|
670
|
+
self.chdir()
|
|
671
|
+
rc = self.log_it(
|
|
672
|
+
'sim --stop-before-compile oclib_fifo_test --compile-args=-hi --compile-args=-hi',
|
|
673
|
+
use_eda_wrap=False
|
|
674
|
+
)
|
|
675
|
+
assert rc == 0
|
|
676
|
+
# Confirm we have two args in self.args['compile-args'] for: -hi
|
|
677
|
+
eda_config_yml_path = os.path.join(
|
|
678
|
+
os.getcwd(), 'eda.work', 'oclib_fifo_test.sim', 'eda_output_config.yml'
|
|
679
|
+
)
|
|
680
|
+
data = yaml_safe_load(eda_config_yml_path)
|
|
681
|
+
assert 'args' in data
|
|
682
|
+
assert 'compile-args' in data['args']
|
|
683
|
+
assert len(data['args']['compile-args']) == 2
|
|
684
|
+
assert data['args']['compile-args'] == ['-hi', '-hi']
|
|
685
|
+
|
|
686
|
+
|
|
662
687
|
@pytest.mark.skipif(not can_run_eda_sim(), reason='no tool found to handle command: sim')
|
|
663
688
|
class TestDepsReqs:
|
|
664
689
|
'''Tests for 'reqs' in the DEPS files. 'reqs' are requirements, like a .pcap or file
|
|
@@ -708,65 +733,6 @@ class TestDepsReqs:
|
|
|
708
733
|
assert rc > 1
|
|
709
734
|
|
|
710
735
|
|
|
711
|
-
@pytest.mark.parametrize("command", ['sim', 'shell'])
|
|
712
|
-
def test_deps_command_order(command):
|
|
713
|
-
'''Test for various "commands" within a DEPS target. This test checks that command
|
|
714
|
-
order is preserved in the top-to-bottom deps order, meaning that eda.py has to collect
|
|
715
|
-
all commands deps order, and then execute them in that order.'''
|
|
716
|
-
|
|
717
|
-
chdir_remove_work_dir('deps_files/command_order')
|
|
718
|
-
if command == 'sim' and not can_run_eda_sim():
|
|
719
|
-
pytest.skip(f'sim skipped, {can_run_eda_sim()=}')
|
|
720
|
-
return # skip/pass
|
|
721
|
-
|
|
722
|
-
if command == 'shell':
|
|
723
|
-
cmd_list = 'shell target_test'.split()
|
|
724
|
-
else:
|
|
725
|
-
cmd_list = 'sim --stop-before-compile target_test'.split()
|
|
726
|
-
|
|
727
|
-
with open('eda.log', 'w', encoding='utf-8') as f:
|
|
728
|
-
with redirect_stdout(f):
|
|
729
|
-
with redirect_stderr(f):
|
|
730
|
-
rc = eda.main(*cmd_list)
|
|
731
|
-
|
|
732
|
-
print(f'{rc=}')
|
|
733
|
-
assert rc == 0
|
|
734
|
-
|
|
735
|
-
# We should see "hi" before "bye" to confirm deps + command order is correct.
|
|
736
|
-
# see ./deps_files/command_order/DEPS.yml - target = target_test
|
|
737
|
-
found_str_list = [
|
|
738
|
-
'exec: echo "hi"',
|
|
739
|
-
'exec: echo "bye"',
|
|
740
|
-
]
|
|
741
|
-
found_lines_list = [None, None]
|
|
742
|
-
|
|
743
|
-
with open('eda.log', encoding='utf-8') as f:
|
|
744
|
-
for lineno, line in enumerate(f.readlines()):
|
|
745
|
-
line = line.rstrip()
|
|
746
|
-
for idx,key in enumerate(found_str_list):
|
|
747
|
-
if key in line:
|
|
748
|
-
found_lines_list[idx] = lineno
|
|
749
|
-
|
|
750
|
-
assert found_lines_list[0] # found hi
|
|
751
|
-
assert found_lines_list[1] # found bye
|
|
752
|
-
assert found_lines_list[0] < found_lines_list[1] # hi before bye
|
|
753
|
-
|
|
754
|
-
# Added check, we redirected to create eda.log earlier to confirm the targets worked,
|
|
755
|
-
# but as a general eda.py check, all shell commands should create their own
|
|
756
|
-
# {target}__shell_0.log file:
|
|
757
|
-
work_dir = os.path.join(
|
|
758
|
-
THISPATH, 'deps_files', 'command_order', 'eda.work', f'target_test.{command}'
|
|
759
|
-
)
|
|
760
|
-
# Note that eda will write out the returncode INFO line to tee'd log files, so
|
|
761
|
-
# there is more in the log file than "hi" or "bye".
|
|
762
|
-
with open(os.path.join(work_dir, 'target_echo_hi__shell_0.log'), encoding='utf-8') as f:
|
|
763
|
-
text = ' '.join(f.readlines()).strip()
|
|
764
|
-
assert any(text.startswith(x) for x in ['hi', '"hi"', '\\"hi\\"'])
|
|
765
|
-
# Added check, one of the targets uses a custom 'tee' file name, instead of the default log.
|
|
766
|
-
with open(os.path.join(work_dir, 'custom_tee_echo_bye.log'), encoding='utf-8') as f:
|
|
767
|
-
text = ''.join(f.readlines()).strip()
|
|
768
|
-
assert any(text.startswith(x) for x in ['bye', '"bye"', '\\"bye\\"'])
|
|
769
|
-
|
|
770
736
|
|
|
771
737
|
@pytest.mark.skipif('verilator' not in tools_loaded, reason="requires verilator")
|
|
772
738
|
class TestDepsOtherMarkup:
|
opencos/tools/modelsim_ase.py
CHANGED
|
@@ -32,6 +32,14 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
|
|
|
32
32
|
self.args.update({
|
|
33
33
|
'tool': self._TOOL, # override
|
|
34
34
|
'gui': False,
|
|
35
|
+
'vopt': self.use_vopt,
|
|
36
|
+
})
|
|
37
|
+
self.args_help.update({
|
|
38
|
+
'vopt': (
|
|
39
|
+
'Boolean to enable/disable use of vopt step prior to vsim step'
|
|
40
|
+
' Note that vopt args can be controlled with --elab-args=<value1>'
|
|
41
|
+
' --elab-args=<value2> ...'
|
|
42
|
+
)
|
|
35
43
|
})
|
|
36
44
|
|
|
37
45
|
def set_tool_defines(self):
|
|
@@ -158,7 +166,7 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
|
|
|
158
166
|
'compile-waivers',
|
|
159
167
|
[ #defaults:
|
|
160
168
|
'2275', # 2275 - Existing package 'foo_pkg' will be overwritten.
|
|
161
|
-
]):
|
|
169
|
+
]) + self.args['compile-waivers']:
|
|
162
170
|
vlog_dot_f_lines += ['-suppress', str(waiver)]
|
|
163
171
|
|
|
164
172
|
if self.args['gui'] or self.args['waves']:
|
|
@@ -252,15 +260,18 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
|
|
|
252
260
|
|
|
253
261
|
sim_plusargs_str = self._get_sim_plusargs_str()
|
|
254
262
|
vsim_suppress_list_str = self._get_vsim_suppress_list_str()
|
|
263
|
+
vsim_ext_args = ' '.join(self.args.get('sim-args', []))
|
|
255
264
|
|
|
256
|
-
voptargs_str =
|
|
265
|
+
voptargs_str = self.tool_config.get('elab-args', '')
|
|
266
|
+
voptargs_str += ' '.join(self.args.get('elab-args', []))
|
|
257
267
|
if self.args['gui'] or self.args['waves']:
|
|
258
|
-
voptargs_str
|
|
268
|
+
voptargs_str += ' ' + self.tool_config.get('simulate-waves-args', '+acc')
|
|
259
269
|
util.artifacts.add_extension(
|
|
260
270
|
search_paths=self.args['work-dir'], file_extension='wlf',
|
|
261
271
|
typ='waveform', description='Modelsim/Questa Waveform WLF (Wave Log Format) file'
|
|
262
272
|
)
|
|
263
273
|
|
|
274
|
+
# TODO(drew): support self.args['sim_libary'] (1 lists)
|
|
264
275
|
vlog_do_lines = []
|
|
265
276
|
vsim_do_lines = []
|
|
266
277
|
|
|
@@ -269,9 +280,6 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
|
|
|
269
280
|
voptargs_str += more_voptargs_str
|
|
270
281
|
|
|
271
282
|
|
|
272
|
-
# TODO(drew): support self.args['sim_libary', 'elab-args', sim-args'] (3 lists)
|
|
273
|
-
# to add to vsim_one_liner.
|
|
274
|
-
|
|
275
283
|
vopt_one_liner = ""
|
|
276
284
|
if self.use_vopt:
|
|
277
285
|
vopt_one_liner = (
|
|
@@ -283,12 +291,12 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
|
|
|
283
291
|
|
|
284
292
|
vsim_one_liner = "vsim -onfinish stop" \
|
|
285
293
|
+ f" -sv_seed {self.args['seed']} {sim_plusargs_str} {vsim_suppress_list_str}" \
|
|
286
|
-
+ f" opt__{self.args['top']}"
|
|
294
|
+
+ f" {vsim_ext_args} opt__{self.args['top']}"
|
|
287
295
|
else:
|
|
288
296
|
# vopt doesn't exist, use single vsim call after vlog call:
|
|
289
297
|
vsim_one_liner = "vsim -onfinish stop" \
|
|
290
298
|
+ f" -sv_seed {self.args['seed']} {sim_plusargs_str} {vsim_suppress_list_str}" \
|
|
291
|
-
+ f" {voptargs_str} work.{self.args['top']}"
|
|
299
|
+
+ f" {voptargs_str} {vsim_ext_args} work.{self.args['top']}"
|
|
292
300
|
|
|
293
301
|
|
|
294
302
|
vsim_one_liner = vsim_one_liner.replace('\n', ' ')
|
|
@@ -393,7 +401,7 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
|
|
|
393
401
|
#defaults:
|
|
394
402
|
'3009', # 3009: [TSCALE] - Module 'foo' does not have a timeunit/timeprecision
|
|
395
403
|
# specification in effect, but other modules do.
|
|
396
|
-
]):
|
|
404
|
+
]) + self.args['sim-waivers']:
|
|
397
405
|
vsim_suppress_list += ['-suppress', str(waiver)]
|
|
398
406
|
|
|
399
407
|
return ' '.join(vsim_suppress_list)
|