opencos-eda 0.3.10__py3-none-any.whl → 0.3.12__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.
Files changed (59) hide show
  1. opencos/commands/deps_help.py +63 -113
  2. opencos/commands/export.py +7 -2
  3. opencos/commands/multi.py +4 -4
  4. opencos/commands/sim.py +14 -15
  5. opencos/commands/sweep.py +1 -1
  6. opencos/commands/synth.py +1 -2
  7. opencos/commands/upload.py +192 -4
  8. opencos/commands/waves.py +52 -8
  9. opencos/deps/deps_commands.py +6 -6
  10. opencos/deps/deps_processor.py +129 -50
  11. opencos/docs/Architecture.md +45 -0
  12. opencos/docs/ConnectingApps.md +29 -0
  13. opencos/docs/DEPS.md +199 -0
  14. opencos/docs/Debug.md +77 -0
  15. opencos/docs/DirectoryStructure.md +22 -0
  16. opencos/docs/Installation.md +117 -0
  17. opencos/docs/OcVivadoTcl.md +63 -0
  18. opencos/docs/OpenQuestions.md +7 -0
  19. opencos/docs/README.md +13 -0
  20. opencos/docs/RtlCodingStyle.md +54 -0
  21. opencos/docs/eda.md +147 -0
  22. opencos/docs/oc_cli.md +135 -0
  23. opencos/eda.py +358 -155
  24. opencos/eda_base.py +187 -60
  25. opencos/eda_config.py +70 -35
  26. opencos/eda_config_defaults.yml +310 -186
  27. opencos/eda_config_reduced.yml +19 -39
  28. opencos/eda_tool_helper.py +190 -21
  29. opencos/files.py +26 -1
  30. opencos/tools/cocotb.py +11 -23
  31. opencos/tools/invio.py +2 -2
  32. opencos/tools/invio_yosys.py +2 -1
  33. opencos/tools/iverilog.py +3 -3
  34. opencos/tools/modelsim_ase.py +1 -1
  35. opencos/tools/quartus.py +172 -137
  36. opencos/tools/questa_common.py +50 -9
  37. opencos/tools/riviera.py +5 -4
  38. opencos/tools/slang.py +14 -10
  39. opencos/tools/slang_yosys.py +1 -0
  40. opencos/tools/surelog.py +7 -6
  41. opencos/tools/verilator.py +9 -7
  42. opencos/tools/vivado.py +315 -180
  43. opencos/tools/yosys.py +5 -5
  44. opencos/util.py +6 -3
  45. opencos/utils/dict_helpers.py +31 -0
  46. opencos/utils/markup_helpers.py +2 -2
  47. opencos/utils/str_helpers.py +38 -0
  48. opencos/utils/subprocess_helpers.py +3 -3
  49. opencos/utils/vscode_helper.py +2 -2
  50. opencos/utils/vsim_helper.py +16 -5
  51. {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.12.dist-info}/METADATA +1 -1
  52. opencos_eda-0.3.12.dist-info/RECORD +93 -0
  53. opencos/eda_config_max_verilator_waivers.yml +0 -39
  54. opencos_eda-0.3.10.dist-info/RECORD +0 -81
  55. {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.12.dist-info}/WHEEL +0 -0
  56. {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.12.dist-info}/entry_points.txt +0 -0
  57. {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.12.dist-info}/licenses/LICENSE +0 -0
  58. {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.12.dist-info}/licenses/LICENSE.spdx +0 -0
  59. {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.12.dist-info}/top_level.txt +0 -0
@@ -13,10 +13,11 @@ my_target:
13
13
  import os
14
14
  from pathlib import Path
15
15
  import re
16
- import shutil
17
16
 
18
- from opencos.util import debug, error, warning, ShellCommandList
19
17
  from opencos.deps.defaults import SUPPORTED_COMMAND_KEYS, COMMAND_ATTRIBUTES
18
+ from opencos.files import safe_shutil_which, PY_EXE
19
+ from opencos.util import debug, error, warning, ShellCommandList
20
+
20
21
 
21
22
  THISPATH = os.path.dirname(__file__)
22
23
  PEAKRDL_CLEANUP_PY = os.path.join(THISPATH, '..', 'peakrdl_cleanup.py')
@@ -221,7 +222,7 @@ def parse_deps_peakrdl( # pylint: disable=too-many-locals
221
222
  + ' however it is not enabled in edy.py - eda.config[dep_command_enables]')
222
223
  return None
223
224
 
224
- if not shutil.which('peakrdl'):
225
+ if not safe_shutil_which('peakrdl'):
225
226
  error('peakrdl: is not present in shell path, or the python package is not avaiable,' \
226
227
  + f' yet we encountered a peakrdl command in {target_path=} {target_node=}')
227
228
  return None
@@ -255,11 +256,10 @@ def parse_deps_peakrdl( # pylint: disable=too-many-locals
255
256
 
256
257
  sv_files += [ f'peakrdl/{top}_pkg.sv', f'peakrdl/{top}.sv' ]
257
258
 
258
-
259
259
  shell_commands = [
260
- [ 'peakrdl', 'regblock', '-o', str(Path('peakrdl/'))] + args_list,
260
+ [ safe_shutil_which('peakrdl'), 'regblock', '-o', str(Path('peakrdl/'))] + args_list,
261
261
  # Edit file to apply some verilator waivers, etc, from peakrdl_cleanup.py:
262
- [ 'python3', PEAKRDL_CLEANUP_PY, str(Path(f'peakrdl/{top}.sv')),
262
+ [ PY_EXE, PEAKRDL_CLEANUP_PY, str(Path(f'peakrdl/{top}.sv')),
263
263
  str(Path(f'peakrdl/{top}.sv')) ],
264
264
  ]
265
265
 
@@ -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
- tokens = [item for item in tokens if item != ''] # remove blanks
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
- return tokens
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
- if os.path.isabs(arg):
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 and _orig_tool != self.args.get('tool', ''):
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
- info(f'{tagname=} in {self.caller_info=}:',
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
- if tool_config and not deps_tags_enables.get('replace-config-tools', None):
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
- ' skipped replace-config-tools b/c it is disabled.')
585
- if tool_config and isinstance(tool_config, dict):
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 disabled.')
602
- if tool_config and isinstance(tool_config, dict):
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 = os.path.join(self.target_path, dep)
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 dep_path in self.command_design_ref.targets_dict or \
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
+ ```