opencos-eda 0.3.10__py3-none-any.whl → 0.3.11__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/deps_help.py +63 -113
- opencos/commands/export.py +7 -2
- opencos/commands/multi.py +3 -3
- opencos/commands/sim.py +14 -15
- opencos/commands/synth.py +1 -2
- opencos/commands/upload.py +192 -4
- opencos/commands/waves.py +8 -8
- opencos/deps/deps_commands.py +6 -6
- opencos/deps/deps_processor.py +129 -50
- opencos/docs/Architecture.md +45 -0
- opencos/docs/ConnectingApps.md +29 -0
- opencos/docs/DEPS.md +199 -0
- opencos/docs/Debug.md +77 -0
- opencos/docs/DirectoryStructure.md +22 -0
- opencos/docs/Installation.md +117 -0
- opencos/docs/OcVivadoTcl.md +63 -0
- opencos/docs/OpenQuestions.md +7 -0
- opencos/docs/README.md +13 -0
- opencos/docs/RtlCodingStyle.md +54 -0
- opencos/docs/eda.md +147 -0
- opencos/docs/oc_cli.md +135 -0
- opencos/eda.py +132 -35
- opencos/eda_base.py +173 -47
- opencos/eda_config.py +56 -17
- opencos/eda_config_defaults.yml +21 -4
- opencos/files.py +26 -1
- opencos/tools/cocotb.py +5 -5
- opencos/tools/invio.py +2 -2
- opencos/tools/invio_yosys.py +2 -1
- opencos/tools/iverilog.py +3 -3
- opencos/tools/quartus.py +113 -115
- opencos/tools/questa_common.py +2 -2
- opencos/tools/riviera.py +3 -3
- opencos/tools/slang.py +11 -7
- opencos/tools/slang_yosys.py +1 -0
- opencos/tools/surelog.py +4 -3
- opencos/tools/verilator.py +4 -4
- opencos/tools/vivado.py +307 -176
- opencos/tools/yosys.py +4 -4
- opencos/util.py +6 -3
- opencos/utils/dict_helpers.py +31 -0
- opencos/utils/markup_helpers.py +2 -2
- opencos/utils/subprocess_helpers.py +3 -3
- opencos/utils/vscode_helper.py +2 -2
- opencos/utils/vsim_helper.py +16 -5
- {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.11.dist-info}/METADATA +1 -1
- opencos_eda-0.3.11.dist-info/RECORD +94 -0
- opencos_eda-0.3.10.dist-info/RECORD +0 -81
- {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.11.dist-info}/WHEEL +0 -0
- {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.11.dist-info}/entry_points.txt +0 -0
- {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.11.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.11.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.11.dist-info}/top_level.txt +0 -0
opencos/deps/deps_processor.py
CHANGED
|
@@ -5,15 +5,17 @@ CommandDesign ref object
|
|
|
5
5
|
'''
|
|
6
6
|
|
|
7
7
|
import argparse
|
|
8
|
+
import copy
|
|
8
9
|
import os
|
|
9
10
|
|
|
10
11
|
from opencos import files
|
|
11
12
|
from opencos import eda_config
|
|
12
|
-
from opencos.util import debug, info, warning, error, read_tokens_from_dot_f, \
|
|
13
|
+
from opencos.util import Colors, debug, info, warning, error, read_tokens_from_dot_f, \
|
|
13
14
|
patch_args_for_dir, load_env_file
|
|
14
15
|
from opencos.utils.str_helpers import dep_str2list
|
|
15
16
|
from opencos.deps.deps_file import deps_target_get_deps_list
|
|
16
17
|
from opencos.deps.deps_commands import deps_commands_handler
|
|
18
|
+
from opencos.utils.dict_helpers import dict_diff
|
|
17
19
|
|
|
18
20
|
from opencos.deps.defaults import SUPPORTED_TARGET_TABLE_KEYS, SUPPORTED_TAG_KEYS, \
|
|
19
21
|
SUPPORTED_DEP_KEYS_BY_TYPE
|
|
@@ -53,6 +55,14 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
53
55
|
self.deps_dir, _ = os.path.split(deps_file)
|
|
54
56
|
self.caller_info = caller_info
|
|
55
57
|
|
|
58
|
+
# Check if it's a Command instead of CommandDesign:
|
|
59
|
+
self.is_command_design = bool(
|
|
60
|
+
getattr(command_design_ref, 'process_plusarg', None) and \
|
|
61
|
+
getattr(command_design_ref, 'set_parameter', None) and \
|
|
62
|
+
isinstance(getattr(command_design_ref, 'incdirs', None), list)
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
56
66
|
assert isinstance(deps_entry, dict), \
|
|
57
67
|
f'{deps_entry=} for {target_node=} in {deps_file=} must be a dict'
|
|
58
68
|
assert command_design_ref is not None, \
|
|
@@ -79,6 +89,8 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
79
89
|
|
|
80
90
|
def apply_defines(self, defines_dict: dict) -> None:
|
|
81
91
|
'''Given defines_dict, applies them to our self.command_design_ref obj'''
|
|
92
|
+
if not self.is_command_design:
|
|
93
|
+
return
|
|
82
94
|
if not isinstance(defines_dict, dict):
|
|
83
95
|
self.error(f"{defines_dict=} is not type dict, can't apply defines,",
|
|
84
96
|
f"in {self.caller_info}")
|
|
@@ -97,6 +109,8 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
97
109
|
|
|
98
110
|
def apply_plusargs(self, plusargs_dict: dict) -> None:
|
|
99
111
|
'''Given plusarsg_dict, applies them to our self.command_design_ref obj'''
|
|
112
|
+
if not self.is_command_design:
|
|
113
|
+
return
|
|
100
114
|
if not isinstance(plusargs_dict, dict):
|
|
101
115
|
self.error(f"{plusargs_dict=} is not type dict, can't apply plusargs,",
|
|
102
116
|
f"in {self.caller_info}")
|
|
@@ -109,6 +123,8 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
109
123
|
|
|
110
124
|
def apply_parameters(self, parameters_dict: dict) -> None:
|
|
111
125
|
'''Given parameters_dict, applies them to our self.command_design_ref obj'''
|
|
126
|
+
if not self.is_command_design:
|
|
127
|
+
return
|
|
112
128
|
if not isinstance(parameters_dict, dict):
|
|
113
129
|
self.error(f"{parameters_dict=} is not type dict, can't apply defines,",
|
|
114
130
|
f"in {self.caller_info}")
|
|
@@ -124,6 +140,8 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
124
140
|
|
|
125
141
|
def apply_incdirs(self, incdirs_list:list) -> None:
|
|
126
142
|
'''Given incdirs_list, applies them to our self.command_design_ref obj'''
|
|
143
|
+
if not self.is_command_design:
|
|
144
|
+
return
|
|
127
145
|
if not isinstance(incdirs_list, (str, list)):
|
|
128
146
|
self.error(f"{incdirs_list=} is not type str/list, can't apply incdirs",
|
|
129
147
|
f"in {self.caller_info}")
|
|
@@ -135,38 +153,59 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
135
153
|
debug(f'Added include dir {abspath} from {self.caller_info}')
|
|
136
154
|
|
|
137
155
|
|
|
138
|
-
def _apply_args_check_tools(self, tokens: list) -> list:
|
|
139
|
-
'''Helper for apply_args(list), returns list strips --tool args under certain conditions
|
|
156
|
+
def _apply_args_check_tools(self, tokens: list, tagname: str) -> list:
|
|
157
|
+
'''Helper for apply_args(list), returns list strips --tool args under certain conditions
|
|
158
|
+
|
|
159
|
+
Basically, we want to see if a DEPS target want to set arg --tool, if so:
|
|
160
|
+
Accept it:
|
|
161
|
+
- this is not a tool class
|
|
162
|
+
- tool was automatically chosen by eda.py's auto-tool-order (--tool not set at
|
|
163
|
+
CLI)
|
|
164
|
+
- accepting means set self.args['tool'], so we can respawn with this.
|
|
165
|
+
Reject/Warn if:
|
|
166
|
+
- previous tool was not automatically applied (--tool was set at CLI)
|
|
167
|
+
and you're trying to change the --tool value.
|
|
168
|
+
- This can happen if you have complicated DEPS targets trying to set the
|
|
169
|
+
tool to different values.
|
|
170
|
+
'''
|
|
140
171
|
|
|
172
|
+
parser = argparse.ArgumentParser(
|
|
173
|
+
prog='deps_processor --tool', add_help=False, allow_abbrev=False
|
|
174
|
+
)
|
|
175
|
+
parser.add_argument('--tool', default='')
|
|
176
|
+
try:
|
|
177
|
+
parsed, unparsed = parser.parse_known_args(tokens + [''])
|
|
178
|
+
tokens2 = list(filter(None, unparsed))
|
|
179
|
+
except argparse.ArgumentError:
|
|
180
|
+
error('deps_processor --tool problem attempting to parse_known_args for:',
|
|
181
|
+
f'{tokens}, {self.caller_info} {tagname}')
|
|
182
|
+
tokens2 = tokens
|
|
183
|
+
|
|
184
|
+
_tool_class = 'tool' in self.args
|
|
141
185
|
_orig_tool = self.args.get('tool', '')
|
|
142
|
-
if not self.command_design_ref.auto_tool_applied and \
|
|
143
|
-
any(x.startswith('--tool') for x in tokens) and _orig_tool:
|
|
144
|
-
warn_tool = ''
|
|
145
|
-
for i, item in enumerate(list(tokens)):
|
|
146
|
-
if item == '--tool':
|
|
147
|
-
if tokens[i + 1] != _orig_tool:
|
|
148
|
-
warn_tool = tokens[i + 1]
|
|
149
|
-
tokens[i : i+2] = ['', ''] # remove this and next arg
|
|
150
|
-
elif item.startswith('--tool='):
|
|
151
|
-
if item[7:] != _orig_tool:
|
|
152
|
-
warn_tool = item
|
|
153
|
-
tokens[i] = '' # remove just this arg.
|
|
154
|
-
|
|
155
|
-
if warn_tool:
|
|
156
|
-
warning(
|
|
157
|
-
f'Attempting to set --tool {warn_tool} from DEPS',
|
|
158
|
-
f'(file={self.deps_file}:{self.target_node})',
|
|
159
|
-
f'however the tool was already chosen as: {_orig_tool}. The --tool arg will',
|
|
160
|
-
f'not be applied from: {tokens}'
|
|
161
|
-
)
|
|
162
186
|
|
|
163
|
-
|
|
187
|
+
if not self.command_design_ref.auto_tool_applied and \
|
|
188
|
+
_tool_class and parsed.tool and _orig_tool \
|
|
189
|
+
and parsed.tool != _orig_tool:
|
|
190
|
+
# tool arg present, --tool in this DEPS args, and tool already set.
|
|
191
|
+
warning(
|
|
192
|
+
f'Attempting to set --tool {parsed.tool} from DEPS',
|
|
193
|
+
f'(file={self.deps_file}:{self.target_node})',
|
|
194
|
+
f'however the tool was already chosen as: {_orig_tool}. The --tool arg will',
|
|
195
|
+
f'not be applied from: {tokens}'
|
|
196
|
+
)
|
|
197
|
+
elif (self.command_design_ref.auto_tool_applied or not _tool_class) and parsed.tool:
|
|
198
|
+
# tool arg wasn't present (not a Tool class), or it was auto-applied,
|
|
199
|
+
# then add the arg anyway so we can later respawn with the correct tool.
|
|
200
|
+
self.args['tool'] = parsed.tool
|
|
201
|
+
debug(f'setting arg.tool to {parsed.tool=} from {self.caller_info}')
|
|
164
202
|
|
|
165
|
-
|
|
203
|
+
# remove blanks, '--tool[=value| value]' removed.
|
|
204
|
+
return [item for item in tokens2 if item != '']
|
|
166
205
|
|
|
167
206
|
|
|
168
|
-
def apply_args( # pylint: disable=too-many-locals,too-many-branches
|
|
169
|
-
self, args_list:list
|
|
207
|
+
def apply_args( # pylint: disable=too-many-locals,too-many-branches,too-many-statements
|
|
208
|
+
self, args_list:list, tagname: str = ''
|
|
170
209
|
) -> list:
|
|
171
210
|
'''Given args_list, applies them to our self.command_design_ref obj
|
|
172
211
|
|
|
@@ -174,9 +213,15 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
174
213
|
unparsed args will show up as eda.py warnings, but will not fail. Most callers do not
|
|
175
214
|
use the unparsed args from this method.
|
|
176
215
|
'''
|
|
216
|
+
if tagname:
|
|
217
|
+
tagname = f'{tagname=}'
|
|
218
|
+
|
|
177
219
|
if not isinstance(args_list, (str, list)):
|
|
178
220
|
self.error(f"{args_list=} is not type str/list, can't apply args",
|
|
179
|
-
f"in {self.caller_info}")
|
|
221
|
+
f"in {self.caller_info} {tagname}")
|
|
222
|
+
|
|
223
|
+
prev_args = copy.deepcopy(self.args)
|
|
224
|
+
|
|
180
225
|
tokens = dep_str2list(args_list)
|
|
181
226
|
|
|
182
227
|
# patch args relative to the DEPS (if self.deps_dir exists) so things like
|
|
@@ -211,7 +256,7 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
211
256
|
tokens2 = list(filter(None, unparsed))
|
|
212
257
|
except argparse.ArgumentError:
|
|
213
258
|
error('deps_processor -f/--input-file, problem attempting to parse_known_args for:',
|
|
214
|
-
f'{tokens}')
|
|
259
|
+
f'{tokens}, {self.caller_info} {tagname}')
|
|
215
260
|
tokens2 = tokens
|
|
216
261
|
|
|
217
262
|
if parsed.input_file:
|
|
@@ -242,13 +287,12 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
242
287
|
# the user may think they were allowed to set --tool, but in our flow the Command handler
|
|
243
288
|
# (self.command_design_ref) has already been chosen, so setting the tool can have
|
|
244
289
|
# strange side-effects.
|
|
290
|
+
_tool_class = 'tool' in self.args
|
|
245
291
|
_orig_tool = self.args.get('tool', '')
|
|
246
|
-
tokens = self._apply_args_check_tools(tokens=tokens)
|
|
247
|
-
if not tokens:
|
|
248
|
-
return []
|
|
292
|
+
tokens = self._apply_args_check_tools(tokens=tokens, tagname=tagname)
|
|
249
293
|
|
|
250
294
|
debug(f'deps_processor - custom apply_args with {tokens=}',
|
|
251
|
-
f'from {self.caller_info}')
|
|
295
|
+
f'from {self.caller_info} {tagname}')
|
|
252
296
|
_, unparsed = self.command_design_ref.run_argparser_on_list(
|
|
253
297
|
tokens=tokens
|
|
254
298
|
)
|
|
@@ -256,7 +300,7 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
256
300
|
# Annoying, but check for plusargs in unparsed, and have referenced CommandDesign
|
|
257
301
|
# or CommandSim class handle it with process_plusarg.
|
|
258
302
|
for arg in list(unparsed):
|
|
259
|
-
if arg.startswith('+'):
|
|
303
|
+
if arg.startswith('+') and self.is_command_design:
|
|
260
304
|
self.command_design_ref.process_plusarg(plusarg=arg, pwd=self.target_path)
|
|
261
305
|
unparsed.remove(arg)
|
|
262
306
|
|
|
@@ -264,10 +308,7 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
264
308
|
for arg in list(unparsed):
|
|
265
309
|
# Since this isn't command line, we have to assume for files, the path is relative
|
|
266
310
|
# to this DEPS file.
|
|
267
|
-
|
|
268
|
-
target = arg
|
|
269
|
-
else:
|
|
270
|
-
target = os.path.join(self.deps_dir, arg)
|
|
311
|
+
target = self.correct_a_deps_target(target=arg, deps_dir=self.deps_dir)
|
|
271
312
|
|
|
272
313
|
file_exists, fpath, forced_extension = files.get_source_file(target)
|
|
273
314
|
if file_exists:
|
|
@@ -279,6 +320,7 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
279
320
|
|
|
280
321
|
else:
|
|
281
322
|
if not os.path.isdir(target) and \
|
|
323
|
+
self.is_command_design and \
|
|
282
324
|
self.command_design_ref.resolve_target_core(
|
|
283
325
|
target=target, no_recursion=False, caller_info=self.caller_info,
|
|
284
326
|
error_on_not_found=False
|
|
@@ -293,7 +335,10 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
293
335
|
warning(f'For {self.command_design_ref.command_name}:' \
|
|
294
336
|
+ f' in {self.caller_info} has unknown args {unparsed=}')
|
|
295
337
|
|
|
296
|
-
if self.command_design_ref.auto_tool_applied
|
|
338
|
+
if (self.command_design_ref.auto_tool_applied or not _tool_class) and \
|
|
339
|
+
_orig_tool != self.args.get('tool', ''):
|
|
340
|
+
# If there was an auto tool applied (tool class or not) then attempt to pick
|
|
341
|
+
# a new sub-command-object with that tool.
|
|
297
342
|
debug(f'deps_processor.apply_args: tool changed, {self.args["tool"]=}, will attempt',
|
|
298
343
|
f'to respawn the job using original args: {self.config["eda_original_args"]}')
|
|
299
344
|
self.command_design_ref.tool_changed_respawn = {
|
|
@@ -302,6 +347,12 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
302
347
|
'from': self.caller_info,
|
|
303
348
|
}
|
|
304
349
|
|
|
350
|
+
diff_args = dict_diff(prev_args, self.args)
|
|
351
|
+
if diff_args:
|
|
352
|
+
args_list = [item for item in args_list if item != ''] # remove blanks
|
|
353
|
+
info(f'{Colors.yellow}{self.caller_info} {tagname}{Colors.green}:',
|
|
354
|
+
f'applying args for {args_list}: {Colors.cyan}{diff_args}')
|
|
355
|
+
|
|
305
356
|
return unparsed
|
|
306
357
|
|
|
307
358
|
def apply_reqs(self, reqs_list:list) -> None:
|
|
@@ -551,9 +602,9 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
551
602
|
' skipped args due to args disabled.')
|
|
552
603
|
if args_list:
|
|
553
604
|
# This will apply knowns args to the target dep:
|
|
554
|
-
|
|
605
|
+
debug(f'{tagname=} in {self.caller_info=}:',
|
|
555
606
|
f'applying args for {args_list=}')
|
|
556
|
-
self.apply_args(args_list)
|
|
607
|
+
self.apply_args(args_list, tagname=tagname)
|
|
557
608
|
|
|
558
609
|
elif key == 'reqs':
|
|
559
610
|
reqs_list = deps_target_get_deps_list(entry=value,
|
|
@@ -578,11 +629,16 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
578
629
|
# apply replace-config-tools
|
|
579
630
|
# This will replace lists (compile-waivers).
|
|
580
631
|
tool_config = value.get('replace-config-tools', {}).get(tool, None)
|
|
581
|
-
|
|
632
|
+
ref_has_tool_config = isinstance(
|
|
633
|
+
getattr(self.command_design_ref, 'tool_config', None), dict
|
|
634
|
+
)
|
|
635
|
+
if tool_config and (not deps_tags_enables.get('replace-config-tools', None) or \
|
|
636
|
+
not ref_has_tool_config):
|
|
582
637
|
tool_config = None
|
|
583
638
|
warning(f'{tagname=} in {self.caller_info}:',
|
|
584
|
-
'
|
|
585
|
-
|
|
639
|
+
'skipped replace-config-tools b/c it is disabled or not present for',
|
|
640
|
+
'this tool and command')
|
|
641
|
+
if ref_has_tool_config and tool_config and isinstance(tool_config, dict):
|
|
586
642
|
# apply it to self.tool_config:
|
|
587
643
|
info(f'{tagname=} in {self.caller_info}:',
|
|
588
644
|
f'applying replace-config-tools for {tool=}: {tool_config}')
|
|
@@ -595,11 +651,13 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
595
651
|
# apply additive-config-tools
|
|
596
652
|
# This will append to lists (compile-waivers)
|
|
597
653
|
tool_config = value.get('additive-config-tools', {}).get(tool, None)
|
|
598
|
-
if tool_config and not deps_tags_enables.get('additive-config-tools', None)
|
|
654
|
+
if tool_config and (not deps_tags_enables.get('additive-config-tools', None) or \
|
|
655
|
+
not ref_has_tool_config):
|
|
599
656
|
tool_config = None
|
|
600
657
|
warning(f'{tagname=} in {self.caller_info}:',
|
|
601
|
-
' skipped additive-config-tools b/c it is
|
|
602
|
-
|
|
658
|
+
' skipped additive-config-tools b/c it is disable or not present for',
|
|
659
|
+
'this tool and command')
|
|
660
|
+
if ref_has_tool_config and tool_config and isinstance(tool_config, dict):
|
|
603
661
|
# apply it to self.tool_config:
|
|
604
662
|
info(f'{tagname=} in {self.caller_info}:',
|
|
605
663
|
f'applying additive-config-tools for {tool=}: {tool_config}')
|
|
@@ -774,6 +832,12 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
774
832
|
|
|
775
833
|
shell_commands_list, work_dir_add_srcs_list = self.get_commands(commands=commands, dep=dep)
|
|
776
834
|
|
|
835
|
+
if shell_commands_list and \
|
|
836
|
+
not self.is_command_design:
|
|
837
|
+
warning(f'Not applying shell commands from {self.caller_info}, not supported',
|
|
838
|
+
'for this tool and command')
|
|
839
|
+
return
|
|
840
|
+
|
|
777
841
|
# add these commands lists to self.command_design_ref:
|
|
778
842
|
# Process all shell_commands_list:
|
|
779
843
|
# This will track each shell command with its target_node and target_path
|
|
@@ -850,7 +914,8 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
850
914
|
|
|
851
915
|
|
|
852
916
|
elif isinstance(dep, str) and \
|
|
853
|
-
any(dep.startswith(x) for x in ['+define+', '+incdir+'])
|
|
917
|
+
any(dep.startswith(x) for x in ['+define+', '+incdir+']) and \
|
|
918
|
+
self.is_command_design:
|
|
854
919
|
# Note: we still support +define+ and +incdir in the deps list.
|
|
855
920
|
# check for compile-time Verilog style plusarg, which are supported under targets
|
|
856
921
|
# These are not run-time Verilog style plusargs comsumable from within the .sv:
|
|
@@ -860,10 +925,11 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
860
925
|
else:
|
|
861
926
|
# If we made it this far, dep better be a str type.
|
|
862
927
|
assert isinstance(dep, str), f'{dep=} {type(dep)=} must be str'
|
|
863
|
-
dep_path =
|
|
928
|
+
dep_path = self.correct_a_deps_target(target=dep, deps_dir=self.target_path)
|
|
864
929
|
debug(f"Got dep {dep_path=} for in {self.caller_info}")
|
|
865
930
|
|
|
866
|
-
if
|
|
931
|
+
if self.is_command_design and \
|
|
932
|
+
dep_path in self.command_design_ref.targets_dict or \
|
|
867
933
|
dep_path in deps_targets_to_resolve:
|
|
868
934
|
debug(" - already processed, skipping")
|
|
869
935
|
else:
|
|
@@ -888,3 +954,16 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
888
954
|
# dep3: 'command_tuple',
|
|
889
955
|
# }
|
|
890
956
|
return deps_targets_to_resolve
|
|
957
|
+
|
|
958
|
+
|
|
959
|
+
def correct_a_deps_target(self, target: str, deps_dir: str) -> str:
|
|
960
|
+
'''Give a target/file in a deps: list, return a patched version
|
|
961
|
+
|
|
962
|
+
- $VAR replacment
|
|
963
|
+
- relative to current DEPS file dir (or not if it was abspath)
|
|
964
|
+
'''
|
|
965
|
+
if self.config['deps_expandvars_enable']:
|
|
966
|
+
target = os.path.expandvars(target)
|
|
967
|
+
if not os.path.isabs(target):
|
|
968
|
+
target = os.path.join(deps_dir, target)
|
|
969
|
+
return target
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
Looking at it roughly bottom up.
|
|
4
|
+
|
|
5
|
+
## oc_user.sv
|
|
6
|
+
|
|
7
|
+
A "userspace" app is loaded, via pointing a build to a DEPS target that pulls in an oc_user.sv.
|
|
8
|
+
|
|
9
|
+
There is an associated oc_user.vh which contains:
|
|
10
|
+
- `defines which configure the internals of the user-space app, and can be whatever is required by the application. They don't need OC_* prefixes, etc
|
|
11
|
+
- `defines which configure the interface to the operating system, and are defined by OpenCOS. These OC_USER_* defines configure the ports of oc_user and it's instantiation in oc_cos.
|
|
12
|
+
|
|
13
|
+
Userspace apps which "ship" with OpenCOS are in the <oc>/user directory, but they can be located anywhere (TBD: document how that is done)
|
|
14
|
+
|
|
15
|
+
TODO: define a userspace testbench. The OC_COS and above levels are implemented behaviorally for flexible testing at the userspace API level.
|
|
16
|
+
|
|
17
|
+
## oc_cos.sv
|
|
18
|
+
|
|
19
|
+
The "operating system", which instantiates the userspace app, as well as a collection of top level modules. It is stores in <oc>/top
|
|
20
|
+
|
|
21
|
+
oc_cos configures itself via two sets of defines:
|
|
22
|
+
- OC_COS_* defines which enable services from the operating system. For example the userspace could request that ethernet bridging is enabled between 4 external and 4 userspace ports, instead of direct connection. The board may setup features such as fan and LED controls. Meanwhile the integrator may enable things like bitstream licensing.
|
|
23
|
+
- OC_BOARD_* defines which are set by the board target DEPS, and configure the ports of oc_cos. The goal is for OC_BOARD defines to setup the defaults for the parameters of oc_cos, not for them to be used repeatedly internally. Some things cannot be done that way (for example setting module names).
|
|
24
|
+
|
|
25
|
+
Testing is performed via <oc>/top/tests/oc_cos_*test targets, which should aim to instantiate as much as possible, while testing the subset that the testbench knows how to test. Practically speaking, the kinds of tests run at this level should consider not duplicating the tests that operate at the next stage up (board level).
|
|
26
|
+
|
|
27
|
+
TODO: do we require board target to use DEPS? what if they just want to have defines in a file, how do we ensure it's read before oc_cos (we could add a way for oc_cos to pull in a header file)
|
|
28
|
+
|
|
29
|
+
## oc_chip_top.sv
|
|
30
|
+
|
|
31
|
+
This is the typical (but not mandatory) name of the top level for a given target. This is stored in a <oc>/boards/<board> directory.
|
|
32
|
+
|
|
33
|
+
The concept of oc_chip_top is like a "board support package" for an operating system, the thin shim that connects the shared code with the unique target. It likely instantiates I/O buffers, maybe connects some pins that are unique to the board (things to do with bringup sequence, custom thermal solutions, etc). Basically, anything that OC_COS cannot handle, needs to be put up here.
|
|
34
|
+
|
|
35
|
+
It's entirely optional for oc_chip_top to configure oc_cos via parameter. oc_cos will setup it's ports based on OC_BOARD defines, and overriding the parameters so they don't match the defaults could result in inconsistent state if the design looks at the OC_BOARD defines again. oc_chip_top
|
|
36
|
+
|
|
37
|
+
TODO: work on the naming. it's called a target, a board, oc_chip_top, etc, and it's confusing.
|
|
38
|
+
|
|
39
|
+
## oc_chip_harness.sv
|
|
40
|
+
|
|
41
|
+
The purpose of oc_chip_harness is to "reverse" the transformation between oc_cos ports (ledOut[1:0], pciRxP[15:0], etc) and oc_chip_top ports (LED_GREEN, LED_RED, PCI_RXP_15, PCI_RXP_14, etc). It terminates any custom interface coming out of oc_chip_top in a way that makes sense. Upwards, it shows ports that look like oc_cos. In this way, oc_cos tests (including those that test userspace apps) can be run on oc_chip_top (i.e. including all board customizations).
|
|
42
|
+
|
|
43
|
+
The upward facing ports need to match the upward facing oc_cos ports. This can be done "with care", or can be done by examining OC_BOARD_* defines and setting up params and ports to match.
|
|
44
|
+
|
|
45
|
+
oc_chip_harness does not accept params, and oc_cos_test runs in a mode where it doesn't push params. Instead it is expected that OC_BOARD_* defines from the board DEPS will configure oc_cos (definitely) and oc_chip_top/oc_chip_harness (optionally).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Connecting Apps
|
|
2
|
+
|
|
3
|
+
The API between oc_top and oc_user is perhaps the area where OpenCOS diverges the most from the OS/Userspace parallel in the software world.
|
|
4
|
+
|
|
5
|
+
Similarities:
|
|
6
|
+
- In both cases, we desire to give a stable API to our userspace apps. Existing APIs should not change.
|
|
7
|
+
- As new APIs are made available, they shouldn't break existing applications.
|
|
8
|
+
Differences:
|
|
9
|
+
- The OS can add new APIs and expose them to userspace, without significant cost, and without changes to userspace. In hardware, connectivity between OS and userspace is in the form of physical wires that must be connected.
|
|
10
|
+
|
|
11
|
+
Furthermore, module ports (and connections to instance ports) cannot be made dependent on parameters. Either all ports are always there, or they must be made conditional on `defines.
|
|
12
|
+
|
|
13
|
+
We desire to not burden userspace with unnecessary complexity. For example, if it does not require networking, it shouldn't have to declare network ports.
|
|
14
|
+
|
|
15
|
+
We desire that the userspace can be flexible. For example, if it is a memory tester, it would be nice if it could scale the number, width, and type of interfaces (for example, connecting to four 512-bit AXI-3 interfaces, or thirty-two 256-bit AXI-4 interfaces).
|
|
16
|
+
|
|
17
|
+
We desire that the top level (`oc_top`) only contains logic required to meet user-space requests. We don't "build everything in" and then just connect what we need.
|
|
18
|
+
|
|
19
|
+
The following methodology is used:
|
|
20
|
+
|
|
21
|
+
- Userspace declares via a header (`oc_user_defines.vh`) what types of interfaces it can connect to.
|
|
22
|
+
- This includes a class (local memory), an interface type (AXI-4), a width (512-bit), and a count (4)
|
|
23
|
+
- This file is not expected to be edited by the integrator. But it may note defines that can be set in `oc_board_defines.vh` to configure functionality within the userspace app.
|
|
24
|
+
- The board comes with a header file that (`oc_board_defines.vh`) that the integrator edits to constrain which external interfaces are connected. This file will also set the defines which activate `oc_top` ports connecting out to the chip interfaces. It can also hint to `oc_top` about internal features to enable, when they can't be inferred just from the interfaces (for example, a define could enable Ethernet switching functionality between external network interfaces and userspace)
|
|
25
|
+
- The board-specific `oc_chip_top` will take the `oc_board_defines.vh` and drive parameters into `oc_top`
|
|
26
|
+
- do we need this?
|
|
27
|
+
- `oc_top` will read `oc_board_defines.vh` to enable ports out to chip interfaces.
|
|
28
|
+
- `oc_top` will read `oc_user_defines.vh` to know what interfaces must be connected to userspace.
|
|
29
|
+
- `oc_top` will infer what internal functions are needed based on board and user defines.
|
opencos/docs/DEPS.md
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# DEPS.yml schema:
|
|
2
|
+
|
|
3
|
+
(Note this information, DEPS.md, is also available via: eda deps-help --verbose)
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
DEFAULTS: # <table> defaults applied to ALL targets in this file, local targets ** override ** the defaults.
|
|
7
|
+
|
|
8
|
+
METADATA: # <table> unstructured data, any UPPERCASE first level key is not considered a target.
|
|
9
|
+
|
|
10
|
+
target-spec:
|
|
11
|
+
|
|
12
|
+
args: # <array or | separated str>
|
|
13
|
+
- --waves
|
|
14
|
+
- --sim_plusargs="+info=500"
|
|
15
|
+
|
|
16
|
+
defines: # <table>
|
|
17
|
+
SOME_DEFINE: value
|
|
18
|
+
SOME_DEFINE_NO_VALUE: # we just leave this blank, or use nil (yaml's None)
|
|
19
|
+
|
|
20
|
+
plusargs: # <table>
|
|
21
|
+
variable0: value
|
|
22
|
+
variable1: # blank for no value, or use nil (yaml's None)
|
|
23
|
+
|
|
24
|
+
parameters: # <table>
|
|
25
|
+
SomeParameter: value
|
|
26
|
+
SOME_OTHER_PARAMETER: value
|
|
27
|
+
|
|
28
|
+
incdirs: # <array>
|
|
29
|
+
- some/relative/path
|
|
30
|
+
|
|
31
|
+
top: # <string>
|
|
32
|
+
|
|
33
|
+
deps: # <array or | space separated string>
|
|
34
|
+
- some_relative_target # <string> aka, a target
|
|
35
|
+
- some_file.sv # <string> aka, a file
|
|
36
|
+
- sv@some_file.txt # <string> aka, ext@file where we'd like a file not ending in .sv to be
|
|
37
|
+
# treated as a .sv file for tools.
|
|
38
|
+
# Supported for sv@, v@, vhdl@, cpp@, sdc@, f@, py@, makefile@
|
|
39
|
+
- commands: # <table> with key 'commands' for a <array>: support for built-in commands
|
|
40
|
+
# Note this cannot be confused for other targets or files.
|
|
41
|
+
- shell: # <string>
|
|
42
|
+
var-subst-args: # <bool> default false. If true, substitute vars in commands, such as {fpga}
|
|
43
|
+
# substituted from eda arg --fpga=SomeFpga, such that {fpga} becomes SomeFpga
|
|
44
|
+
var-subst-os-env: #<bool> default false. If true, substitute vars in commands using os.environ vars,
|
|
45
|
+
# such as $FPGA could get substituted by env value for it.
|
|
46
|
+
tee: # <string> optional filename, otherwise shell commands write to {target-spec}__shell_0.log
|
|
47
|
+
run-from-work-dir: #<bool> default true. If false, runs from the directory of this DEPS file.
|
|
48
|
+
filepath-subst-target-dir: #<bool> default true. If false, disables shell file path
|
|
49
|
+
# substituion on this target's directory (this DEPS file dir).
|
|
50
|
+
dirpath-subst-target-dir: #<bool> default false. If true, enables shell directory path
|
|
51
|
+
# substituion on this target's directory (this DEPS file dir).
|
|
52
|
+
run-after-tool: # <bool> default false. Set to true to run after any EDA tools, or
|
|
53
|
+
# any command handlers have completed.
|
|
54
|
+
- shell: echo "Hello World!"
|
|
55
|
+
- work-dir-add-sources: # <array or | space separated string>, this is how to add generated files
|
|
56
|
+
# to compile order list.
|
|
57
|
+
- peakrdl: # <string> ## peakrdl command to generate CSRs
|
|
58
|
+
|
|
59
|
+
reqs: # <array or | space separated string>
|
|
60
|
+
- some_file.mem # <string> aka, a non-source file required for this target.
|
|
61
|
+
# This file is checked for existence prior to invoking the tool involved, for example,
|
|
62
|
+
# in a simulation this would be done prior to a compile step.
|
|
63
|
+
|
|
64
|
+
multi:
|
|
65
|
+
ignore-this-target: # <array of tables> eda commands to be ignored in `eda multi <command>` for this target only
|
|
66
|
+
# this is checked in the matching multi targets list, and is not inherited through dependencies.
|
|
67
|
+
- commands: synth # space separated strings
|
|
68
|
+
tools: vivado # space separated strings
|
|
69
|
+
|
|
70
|
+
- commands: sim # omit tools, ignores 'sim' commands for all tools, for this target only, when this target
|
|
71
|
+
# is in the target list called by `eda multi`.
|
|
72
|
+
|
|
73
|
+
- tools: vivado # omit commands, ignores all commands if tool is vivado, for this target only, when this target
|
|
74
|
+
# is in the target list called by `eda multi`.
|
|
75
|
+
|
|
76
|
+
args: # <array> additional args added to all multi commands of this target.
|
|
77
|
+
# Note that all args are POSIX with dashes, --sim-plusargs=value, etc.
|
|
78
|
+
|
|
79
|
+
<eda-command>: # key is one of sim, flist, build, synth, etc.
|
|
80
|
+
# can be used instead of 'tags' to support different args or deps.
|
|
81
|
+
args: # <array or | space separated string>
|
|
82
|
+
defines: ## <table>
|
|
83
|
+
plusargs: ## <table>
|
|
84
|
+
parameters: ## <table>
|
|
85
|
+
incdirs: ## <array>
|
|
86
|
+
|
|
87
|
+
tags: # <table> this is the currently support tags features in a target.
|
|
88
|
+
<tag-name>: # <string> key for table, can be anything, name is not used.
|
|
89
|
+
with-tools: <array or | space separated string>
|
|
90
|
+
# If using one of these tools, apply these values.
|
|
91
|
+
# entries can be in the form: vivado, or vivado:2024.1
|
|
92
|
+
with-commands: <array or | space separated string>
|
|
93
|
+
# apply if this was the `eda` command, such as: sim
|
|
94
|
+
with-args: # <table> (optional) arg key/value pairs to match for this tag.
|
|
95
|
+
# this would be an alternative to running eda with --tags=value
|
|
96
|
+
# The existence of an argument with correct value would enable a tag.
|
|
97
|
+
# And example would be:
|
|
98
|
+
# with-args:
|
|
99
|
+
# waves: true
|
|
100
|
+
args: <array or | space separated string> # args to be applied if this target is used, with a matching
|
|
101
|
+
# tool in 'with-tools'.
|
|
102
|
+
deps: <array or | space separated string, applied with tag>
|
|
103
|
+
defines: <table, applied with tag>
|
|
104
|
+
plusargs: <table, applied with tag>
|
|
105
|
+
parameters: <table, applied with tag>
|
|
106
|
+
incdirs: <array, applied with tag>
|
|
107
|
+
replace-config-tools: <table> # spec matching eda_config_defaults.yml::tools.<tool> (replace merge strategy)
|
|
108
|
+
additive-config-tools: <table> # spec matching eda_config_defaults.yml::tools.<tool> (additive merge strategy)
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Examples
|
|
113
|
+
|
|
114
|
+
### Target with tag-mode:
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
target-foo-with-tags:
|
|
118
|
+
deps: some_file1.sv some_file2.sv
|
|
119
|
+
tags:
|
|
120
|
+
|
|
121
|
+
# This can be invoked with eda --tool=vivado --gui
|
|
122
|
+
xilinx_mode:
|
|
123
|
+
with-args:
|
|
124
|
+
gui: true
|
|
125
|
+
with-tools: vivado
|
|
126
|
+
deps: <some_deps_for_this_fpga>
|
|
127
|
+
defines:
|
|
128
|
+
XILINX_GUI_MODE: 1
|
|
129
|
+
|
|
130
|
+
defaults:
|
|
131
|
+
deps: some_dummy_dep_not_fpga
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
### Simple Target, with deps as an explicit list
|
|
137
|
+
|
|
138
|
+
This is the basic, common format for a target. target - deps - (array of files or other relative targets). The file order is compile order, top to bottom.
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
some-target:
|
|
142
|
+
deps:
|
|
143
|
+
- ../foo/some_prev_target
|
|
144
|
+
- some_other_target
|
|
145
|
+
- some_file.sv
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Simple Target, with deps as a string with \n separators
|
|
149
|
+
|
|
150
|
+
This is more terse variant ` deps: |` followed by a string of targets and source files.
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
some-target2:
|
|
154
|
+
deps: |
|
|
155
|
+
../bar/some_prev_target2
|
|
156
|
+
some_file2.sv
|
|
157
|
+
some_other_target2
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Simple Target, non-table (strings)
|
|
161
|
+
|
|
162
|
+
The most terse variant for a target is a array or \n separated string, not a table, with no `deps` key, then it is assumed that all entries are other targets or sources.
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
some-target3: |
|
|
166
|
+
../bar/some_prev_target3
|
|
167
|
+
some_file3.sv
|
|
168
|
+
some_other_target3
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
You could also explicitly make a list without a `deps` key:
|
|
172
|
+
```
|
|
173
|
+
## target entry is an explicit list
|
|
174
|
+
some-target4:
|
|
175
|
+
- ../bar/some_prev_target4
|
|
176
|
+
- some_file4.sv
|
|
177
|
+
- some_other_target4
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
You can also have space separated in a string:
|
|
181
|
+
```
|
|
182
|
+
some-target5: ../bar/some_prev_target5 some_file5.sv
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Defines with relative path
|
|
186
|
+
|
|
187
|
+
Defines with file path information relative to a DEPS.yml file are supported with special words `%PWD%` as a replacement for ./ in defines only. This is necessary because we do not do path substituion on defines.
|
|
188
|
+
|
|
189
|
+
Example:
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
my_define_target:
|
|
193
|
+
deps:
|
|
194
|
+
- my_dut.sv
|
|
195
|
+
- my_testbench.sv
|
|
196
|
+
defines:
|
|
197
|
+
SOME_FILE: >
|
|
198
|
+
"%PWD%/my_pcap.pcap"
|
|
199
|
+
```
|