opencos-eda 0.3.12__py3-none-any.whl → 0.3.14__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 (36) hide show
  1. opencos/commands/flist.py +222 -39
  2. opencos/commands/multi.py +4 -2
  3. opencos/commands/sim.py +192 -8
  4. opencos/deps/deps_file.py +4 -1
  5. opencos/deps_schema.py +2 -2
  6. opencos/eda.py +12 -9
  7. opencos/eda_base.py +39 -8
  8. opencos/eda_config.py +37 -6
  9. opencos/eda_config_defaults.yml +30 -5
  10. opencos/eda_tool_helper.py +4 -4
  11. opencos/tools/cocotb.py +0 -11
  12. opencos/tools/invio.py +0 -6
  13. opencos/tools/iverilog.py +17 -16
  14. opencos/tools/modelsim_ase.py +0 -12
  15. opencos/tools/quartus.py +21 -1
  16. opencos/tools/questa.py +0 -14
  17. opencos/tools/questa_common.py +54 -25
  18. opencos/tools/questa_fe.py +0 -14
  19. opencos/tools/questa_fse.py +0 -14
  20. opencos/tools/riviera.py +104 -25
  21. opencos/tools/slang.py +12 -9
  22. opencos/tools/slang_yosys.py +0 -6
  23. opencos/tools/surelog.py +11 -8
  24. opencos/tools/tabbycad_yosys.py +1 -7
  25. opencos/tools/verilator.py +18 -11
  26. opencos/tools/vivado.py +92 -25
  27. opencos/tools/yosys.py +8 -5
  28. opencos/util.py +14 -5
  29. opencos/utils/str_helpers.py +4 -1
  30. {opencos_eda-0.3.12.dist-info → opencos_eda-0.3.14.dist-info}/METADATA +1 -2
  31. {opencos_eda-0.3.12.dist-info → opencos_eda-0.3.14.dist-info}/RECORD +36 -36
  32. {opencos_eda-0.3.12.dist-info → opencos_eda-0.3.14.dist-info}/WHEEL +0 -0
  33. {opencos_eda-0.3.12.dist-info → opencos_eda-0.3.14.dist-info}/entry_points.txt +0 -0
  34. {opencos_eda-0.3.12.dist-info → opencos_eda-0.3.14.dist-info}/licenses/LICENSE +0 -0
  35. {opencos_eda-0.3.12.dist-info → opencos_eda-0.3.14.dist-info}/licenses/LICENSE.spdx +0 -0
  36. {opencos_eda-0.3.12.dist-info → opencos_eda-0.3.14.dist-info}/top_level.txt +0 -0
opencos/commands/flist.py CHANGED
@@ -6,11 +6,13 @@ Intended to be overriden by Tool based classes (such as CommandFListVivado, etc)
6
6
  # pylint: disable=too-many-statements
7
7
 
8
8
  import os
9
+ import shlex
9
10
 
10
- from opencos import util
11
- from opencos.eda_base import CommandDesign
11
+ from opencos import util, eda_config
12
+ from opencos.eda_base import CommandDesign, Tool
12
13
  from opencos.utils.str_helpers import strip_all_quotes
13
14
  from opencos.commands.sim import parameters_dict_get_command_list
15
+ from opencos.utils.str_helpers import strip_outer_quotes
14
16
 
15
17
  class CommandFList(CommandDesign):
16
18
  '''Base class command handler for: eda flist ...'''
@@ -19,29 +21,41 @@ class CommandFList(CommandDesign):
19
21
 
20
22
  def __init__(self, config: dict):
21
23
  CommandDesign.__init__(self, config=config)
22
- self.args.update({
23
- 'eda-dir' : 'eda.flist', # user can specify eda-dir if files are generated.
24
+
25
+ # If there's no tool attached, then we'll assume this flist is being created
26
+ # to run in `eda`, not some vendor tool.
27
+ self.flist_has_tool = isinstance(self, Tool)
28
+
29
+ self.flist_args = {
24
30
  'out' : "flist.out",
25
31
  'emit-define' : True,
26
32
  'emit-parameter' : True,
27
33
  'emit-incdir' : True,
34
+ 'emit-plusargs' : True,
28
35
  'emit-v' : True,
29
36
  'emit-sv' : True,
30
37
  'emit-vhd' : True,
31
38
  'emit-cpp' : True,
32
39
  'emit-non-sources' : True, # as comments, from DEPS 'reqs'
40
+ 'emit-eda-args' : not self.flist_has_tool, # no Tool means flist for eda.
33
41
  'prefix-define' : "+define+",
34
42
  'prefix-parameter' : "-G",
35
43
  'prefix-incdir' : "+incdir+",
44
+ 'prefix-plusargs' : "+",
36
45
  'prefix-v' : "",
37
46
  'prefix-sv' : "",
38
47
  'prefix-vhd' : "",
39
48
  'prefix-cpp' : "",
40
49
  'prefix-non-sources' : "", # as comments anyway.
41
- # TODO(simon): make the define quoting work like the paths quoting
50
+ # NOTE - the defaults are for creating an flist that is suitable for 'eda', which for
51
+ # defines means: optionally single-quote the entire thing, double-quote string values
52
+ # only.
53
+ # Tool classes should override if they want to set safe-mode-defines=True.
54
+ # Tool classes may also avoid these args entirely in their derived CommandFList class
55
+ 'safe-mode-defines' : not self.flist_has_tool, # no Tool
42
56
  'bracket-quote-define': False,
43
57
  'single-quote-define': False,
44
- 'quote-define' : True,
58
+ 'quote-define' : self.flist_has_tool, # Tool, this is NOT for eda.
45
59
  'equal-define' : True,
46
60
  'escape-define-value': False,
47
61
  'quote-define-value' : False,
@@ -55,7 +69,14 @@ class CommandFList(CommandDesign):
55
69
 
56
70
  # ex: eda flist --print-to-stdout --emit-rel-path --quiet <target>
57
71
  'emit-rel-path' : False,
72
+ }
73
+
74
+
75
+ self.args.update({
76
+ 'eda-dir' : 'eda.flist', # user can specify eda-dir if files are generated.
58
77
  })
78
+ self.args.update(self.flist_args)
79
+
59
80
  self.args_help.update({
60
81
  'print-to-stdout': "do not save file, print to stdout",
61
82
  })
@@ -75,14 +96,15 @@ class CommandFList(CommandDesign):
75
96
  def get_flist_dict(self) -> dict:
76
97
  '''Returns dict of some internal class member vars, ignores args
77
98
 
78
- Useful for an extneral caller to get details about this CommandDesign child
99
+ Useful for an external caller to get details about this CommandDesign child
79
100
  object without generating a .f file, or having to know specifics about the
80
101
  class
81
102
  '''
82
103
  self.command_safe_set_tool_defines() # (Command.command_safe_set_tool_defines)
83
104
 
84
105
  ret = {}
85
- for key in ['files_sv', 'files_v', 'files_vhd', 'defines', 'incdirs']:
106
+ for key in ['files_sv', 'files_v', 'files_vhd', 'defines', 'incdirs',
107
+ 'parameters', 'unprocessed-plusargs']:
86
108
  # These keys must exist, all are lists, defines is a dict
87
109
  x = getattr(self, key, None)
88
110
  if isinstance(x, (dict, list)):
@@ -91,6 +113,134 @@ class CommandFList(CommandDesign):
91
113
  ret[key] = x
92
114
  return ret
93
115
 
116
+ def get_flist_defines_list(self) -> list:
117
+ '''Returns formatted list of str for known defines'''
118
+
119
+ ret = []
120
+ prefix = strip_all_quotes(self.args['prefix-define'])
121
+ for d, value in self.defines.items():
122
+
123
+ if value is None:
124
+ ret.append(prefix + d)
125
+ continue
126
+
127
+ # else, value exists:
128
+ safe_mode_guard_str_value = bool(
129
+ self.args['safe-mode-defines'] and isinstance(value, str) and ' ' in value
130
+ )
131
+
132
+ if self.args['bracket-quote-define']:
133
+ qd1 = "{"
134
+ qd2 = "}"
135
+ elif self.args['single-quote-define']:
136
+ qd1 = "'"
137
+ qd2 = "'"
138
+ elif self.args['quote-define']:
139
+ qd1 = '"'
140
+ qd2 = '"'
141
+ else:
142
+ qd1 = ''
143
+ qd2 = ''
144
+
145
+ if self.args['equal-define']:
146
+ ed1 = '='
147
+ else:
148
+ ed1 = ' '
149
+
150
+ if self.args['escape-define-value']:
151
+ value = value.replace('\\', '\\\\').replace('"', '\\"')
152
+ if self.args['quote-define-value']:
153
+ value = '"' + value + '"'
154
+ if safe_mode_guard_str_value:
155
+ value = strip_outer_quotes(value.strip('\n'))
156
+ value = '"' + value + '"'
157
+
158
+ if self.args['quote-define'] and value.startswith('"') and value.endswith('"'):
159
+ # If you wanted your define to look like:
160
+ # +define+"NAME=VALUE", but VALUE also has double quotes wrapping it,
161
+ # it's unlikely to work so we'll optimistically so escape the " wrapping value.
162
+ # If you have additional " in the middle of the value, good luck.
163
+ value = '\\"' + value[1:-1] + '\\"'
164
+
165
+ newline = prefix + qd1 + f"{d}{ed1}{value}" + qd2
166
+
167
+ if safe_mode_guard_str_value:
168
+ # wrap the entire thing with single-quotes, so it survives as a single
169
+ # token in an eda dot-f file:
170
+ newline = shlex.quote(newline)
171
+
172
+ ret.append(newline)
173
+
174
+ return ret
175
+
176
+ def get_flist_plusargs_list(self) -> list:
177
+ '''Returns formatted list of str for unprocessed plusargs
178
+
179
+ Tool based classes can override if they also want to query their own
180
+ processed plusargs, such as CommandSim.args[sim-plusargs']
181
+ '''
182
+ ret = []
183
+ for x in self.args.get('unprocessed-plusargs', []) + self.args.get('sim-plusargs', []):
184
+ if self.args['prefix-plusargs']:
185
+ if x.startswith('+'):
186
+ x = x[1:] # strip leading +
187
+ x = self.args['prefix-plusargs'] + x
188
+ ret.append(x)
189
+ return ret
190
+
191
+ def get_flist_parameter_list(self) -> list:
192
+ '''Returns formatted list of str for parameters'''
193
+ prefix = strip_all_quotes(self.args['prefix-parameter'])
194
+ return parameters_dict_get_command_list(
195
+ params=self.parameters, arg_prefix=prefix, for_flist=True
196
+ )
197
+
198
+ def get_flist_eda_args_list(self) -> list:
199
+ '''Returns list of eda args for an eda-capable flist
200
+
201
+ - This will NOT add any util based args (--color | --no-color, --debug, etc)
202
+ - This will NOT add any -f/--input-file args (those are already resolved)
203
+
204
+ - This WILL add --env-file args
205
+ - This WILL add --config-yml args that were not default value
206
+
207
+ Not intended to be overriden by Tool based command classes.
208
+ '''
209
+ ret = []
210
+
211
+ # --env-file(s), if used:
212
+ for env_file in util.env_files_loaded:
213
+ ret.append(f'--env-file={env_file}')
214
+
215
+ # --config-yml, if non-default:
216
+ ret.extend(eda_config.get_config_yml_args_for_flist())
217
+
218
+ # EDA args, but not the flist specific args, and only those that were modified.
219
+ for arg, _ in self.modified_args.items():
220
+
221
+ if arg in self.flist_args:
222
+ # do not emit flist command args
223
+ continue
224
+
225
+ value = self.args[arg]
226
+ if isinstance(value, bool):
227
+ if value:
228
+ ret.append(f'--{arg}')
229
+ else:
230
+ ret.append(f'--no-{arg}')
231
+ else:
232
+ ret.append(f'--{arg}={value}')
233
+ return ret
234
+
235
+ def get_additional_flist_args_list(self) -> list:
236
+ '''Derived classes may override, to output additional args in the flist'''
237
+ return []
238
+
239
+ def get_additional_flist_files_list(self) -> list:
240
+ '''Derived classes may override, to output additional files in the flist'''
241
+ return []
242
+
243
+
94
244
  def do_it(self) -> None:
95
245
  '''do_it() is the main entry point for creating the flist(),
96
246
 
@@ -105,6 +255,30 @@ class CommandFList(CommandDesign):
105
255
  self.write_eda_config_and_args()
106
256
  return
107
257
 
258
+ if self.config['tool']:
259
+ tool_string = f' (with --tool={self.config["tool"]})'
260
+ else:
261
+ tool_string = ''
262
+
263
+ # if config['tool'] is set, but self.flist_has_tool is False, we're likely using
264
+ # this default handler CommandFList and the Tool class hasn't defined what they
265
+ # do. In this case, simply warn that this will emit a non-tool specific default flist
266
+ # intended for use by `eda`:
267
+ if self.config['tool'] and not self.flist_has_tool:
268
+ util.warning(f'For command="flist"{tool_string}, there is no tool',
269
+ 'specific handler for producing an flist. The default eda flist will',
270
+ 'be emitted')
271
+ # If this happens, you'll likely want the Tool based defines (that were never set
272
+ # by Tool.set_tool_defines(self) b/c we have no Tool class.
273
+ # TODO(drew): This is only a best-effort, we could create a derived Tool object and
274
+ # instead call obj.set_tool_defines(), and update self.defines instead?
275
+ _tool_config = self.config.get('tools', {}).get(self.config['tool'], {})
276
+ self.defines.update(
277
+ _tool_config.get('defines', {})
278
+ )
279
+
280
+
281
+
108
282
  # check if we're overwriting the output flist file.
109
283
  if self.args['print-to-stdout']:
110
284
  pass
@@ -153,41 +327,33 @@ class CommandFList(CommandDesign):
153
327
  f = os.path.relpath(f)
154
328
  print('## ' + prefix + pq1 + f + pq2, file=fo)
155
329
 
330
+ if self.args['emit-eda-args']:
331
+ for newline in self.get_flist_eda_args_list():
332
+ print(newline, file=fo)
333
+
334
+ defines_lines = self.get_flist_defines_list()
335
+ if not self.args['emit-define'] and defines_lines:
336
+ util.warning(f'Command "flist"{tool_string}, has defines present but they were not',
337
+ f'included in the output flist: {defines_lines}')
338
+
339
+ parameter_lines = self.get_flist_parameter_list()
340
+ if not self.args['emit-parameter'] and parameter_lines:
341
+ util.warning(f'Command "flist"{tool_string}, has parameters present but they were not',
342
+ f'included in the output flist: {parameter_lines}')
343
+
344
+ plusarg_lines = self.get_flist_plusargs_list()
345
+ if not self.args['emit-plusargs'] and plusarg_lines:
346
+ util.warning(f'Command "flist"{tool_string}, has plusargs present but they were not',
347
+ f'included in the output flist: {plusarg_lines}')
348
+
156
349
  if self.args['emit-define']:
157
- prefix = strip_all_quotes(self.args['prefix-define'])
158
- for d, value in self.defines.items():
159
- if value is None:
160
- newline = prefix + d
161
- else:
162
- if self.args['bracket-quote-define']:
163
- qd1 = "{"
164
- qd2 = "}"
165
- elif self.args['single-quote-define']:
166
- qd1 = "'"
167
- qd2 = "'"
168
- elif self.args['quote-define']:
169
- qd1 = '"'
170
- qd2 = '"' # rename this one when things calm down
171
- else:
172
- qd1 = ''
173
- qd2 = ''
174
- if self.args['equal-define']:
175
- ed1 = '='
176
- else:
177
- ed1 = ' '
178
- if self.args['escape-define-value']:
179
- value = value.replace('\\', '\\\\').replace('"', '\\"')
180
- if self.args['quote-define-value']:
181
- value =f'"{value}"'
182
- newline = prefix + qd1 + f"{d}{ed1}{value}" + qd2
350
+ for newline in defines_lines:
183
351
  print(newline, file=fo)
184
352
 
185
353
  if self.args['emit-parameter']:
186
- prefix = strip_all_quotes(self.args['prefix-parameter'])
187
- for item in parameters_dict_get_command_list(
188
- params=self.parameters, arg_prefix=prefix
189
- ):
190
- print(item, file=fo)
354
+ for newline in parameter_lines:
355
+ print(newline, file=fo)
356
+
191
357
 
192
358
  if self.args['emit-incdir']:
193
359
  prefix = strip_all_quotes(self.args['prefix-incdir'])
@@ -195,12 +361,24 @@ class CommandFList(CommandDesign):
195
361
  if self.args['emit-rel-path']:
196
362
  i = os.path.relpath(i)
197
363
  print(prefix + pq1 + i + pq2, file=fo)
364
+
365
+ if self.args['emit-plusargs']:
366
+ for newline in plusarg_lines:
367
+ print(newline, file=fo)
368
+
369
+
370
+ # Hook for derived classes to optionally print additional custom args, prior to
371
+ # any files:
372
+ for newline in self.get_additional_flist_args_list():
373
+ print(newline, file=fo)
374
+
198
375
  if self.args['emit-v']:
199
376
  prefix = strip_all_quotes(self.args['prefix-v'])
200
377
  for f in self.files_v:
201
378
  if self.args['emit-rel-path']:
202
379
  f = os.path.relpath(f)
203
380
  print(prefix + pq1 + f + pq2, file=fo)
381
+
204
382
  if self.args['emit-sv']:
205
383
  prefix = strip_all_quotes(self.args['prefix-sv'])
206
384
  for f in self.files_sv:
@@ -220,6 +398,11 @@ class CommandFList(CommandDesign):
220
398
  f = os.path.relpath(f)
221
399
  print(prefix + pq1 + f + pq2, file=fo)
222
400
 
401
+ # Hook for derived classes to optionally print additional flist items after
402
+ # any files:
403
+ for newline in self.get_additional_flist_files_list():
404
+ print(newline, file=fo)
405
+
223
406
  if self.args['print-to-stdout']:
224
407
  print() # don't need to close fo (None)
225
408
  else:
opencos/commands/multi.py CHANGED
@@ -354,9 +354,11 @@ class CommandMulti(CommandParallel):
354
354
 
355
355
  if self.args['print-targets']:
356
356
  util.info('Multi print-targets (will not run jobs): -->')
357
- for t in self.targets:
357
+ print_targets = [t[0] for t in self.targets]
358
+ print_targets.sort()
359
+ for x in print_targets:
358
360
  # t = tuple of (target:str, tool:str), we just want the target.
359
- print(f' {t[0]}')
361
+ print(f' {x}')
360
362
  else:
361
363
  util.debug("Multi: converting list of targets into list of jobs")
362
364
  self.jobs = []
opencos/commands/sim.py CHANGED
@@ -12,15 +12,34 @@ Note that CommandSim is also a base class for opencos.commands.elab.CommandElab.
12
12
  # pylint: disable=too-many-arguments,too-many-positional-arguments
13
13
 
14
14
  import os
15
+ import shlex
16
+
17
+ from pathlib import Path
15
18
 
16
19
  from opencos import util, export_helper
17
20
  from opencos.eda_base import CommandDesign, Tool
18
21
  from opencos.utils import status_constants
19
22
 
20
- from opencos.utils.str_helpers import sanitize_defines_for_sh, strip_outer_quotes
23
+ from opencos.utils.str_helpers import strip_outer_quotes
24
+
25
+ def parameters_dict_get_command_list(
26
+ params: dict, arg_prefix: str = '-G',
27
+ hier_delimiter: str = '.',
28
+ top_hier_str: str = '',
29
+ for_flist: bool = False
30
+ ) -> list:
31
+ '''Given dict of parameters, returns a command list.
32
+
33
+ Some simulators will set ALL parameters with Name=Value at all levels of hierarchy,
34
+ so to avoid that and only set top level parameters, but still allow package or
35
+ hierarchy parameters to be set, you can use:
21
36
 
22
- def parameters_dict_get_command_list(params: dict, arg_prefix: str = '-G') -> list:
23
- '''Given dict of parameters, returns a command list'''
37
+ hier_delimiter='/'
38
+ top_hier_str=f'/{TOP_MODULE_NAME}/'
39
+
40
+ Which will, if a parameter Name does not contain the heir delimiter, the Name
41
+ will be prepended with top_hier_str.
42
+ '''
24
43
 
25
44
  ret_list = []
26
45
  if ' ' in arg_prefix:
@@ -34,13 +53,29 @@ def parameters_dict_get_command_list(params: dict, arg_prefix: str = '-G') -> li
34
53
  if not isinstance(v, (int, str)):
35
54
  util.warning(f'parameter {k} has value: {v}, parameters must be int/string types')
36
55
 
56
+ if top_hier_str and hier_delimiter not in k:
57
+ k = f'{top_hier_str}{k}'
58
+
37
59
  ret_list.extend(arg_list_prefix)
38
60
  if isinstance(v, int):
39
61
  ret_list.append(f'{arg_str_prefix}{k}={v}')
40
62
  else: # string
63
+ # Note if for_flist=True, then we have to take extra care on what gets
64
+ # double-quotes " and \".
41
65
  v = strip_outer_quotes(v.strip('\n'))
42
66
  v = '"' + v + '"'
43
- ret_list.append(f'{arg_str_prefix}{k}={sanitize_defines_for_sh(v)}')
67
+
68
+ if for_flist:
69
+ # do not wrap with " unless there are spaces, if there are spaces
70
+ # wrap the entire thing with single quotes (bash)
71
+ if ' ' in v:
72
+ ret_list.append(shlex.quote(f'{arg_str_prefix}{k}={v}'))
73
+ else:
74
+ ret_list.append(f'{arg_str_prefix}{k}={v}')
75
+ else:
76
+ # This is for a tool, so simply guard single quotes:
77
+ v = v.replace("'", "\\" + "'")
78
+ ret_list.append(f'{arg_str_prefix}{k}={v}')
44
79
  return ret_list
45
80
 
46
81
 
@@ -51,6 +86,7 @@ class CommandSim(CommandDesign): # pylint: disable=too-many-public-methods
51
86
  error_on_no_files_or_targets = True
52
87
  error_on_missing_top = True
53
88
  tool_config = {} # Children with Tool parent classes will set on Tool constructor.
89
+ library_map_supported = False
54
90
 
55
91
  command_name = 'sim'
56
92
 
@@ -79,6 +115,10 @@ class CommandSim(CommandDesign): # pylint: disable=too-many-public-methods
79
115
  # not used by other simulators, so these can go in DEPS files for custom things
80
116
  # like -CFLAGS -O0, etc.
81
117
  'verilate-args': [],
118
+ 'ext-defines-sv-fname': '',
119
+ 'license-queue': False,
120
+ 'library-map': [],
121
+ 'add-top-library': [],
82
122
  })
83
123
  self.args_help.update({
84
124
  'pre-sim-tcl': (
@@ -124,6 +164,26 @@ class CommandSim(CommandDesign): # pylint: disable=too-many-public-methods
124
164
  'uvm-version': (
125
165
  'Used if --uvm is set, for example --uvm-version=1.2'
126
166
  ),
167
+ 'ext-defines-sv-fname': (
168
+ 'If set to empty str, --ext-defines-sv-fname="", applies defines to tool via args.'
169
+ ' If set to non-emtpy str as a .sv file name, such as'
170
+ ' --ext-defines-sv-fname=_ext_defines.sv, saves defines to a file name in the'
171
+ ' work-dif, and adds this file to the SV/Verilog file list ahead of other files.'
172
+ ),
173
+ 'license-queue': (
174
+ 'Set to enable env vars LICENSE_QUEUE=1 which has effects on certain simulators'
175
+ ),
176
+ 'library-map': (
177
+ 'list arg (can set multiple) with values LibName:Path, only supported'
178
+ ' by certain simulation tools.'
179
+ ' The Path search is relative to the tool exe, one level above the tool exe,'
180
+ ' or can be absolute path.'
181
+ ),
182
+ 'add-top-library': (
183
+ 'Add additional top levels, for example if you added the "unisim" library'
184
+ ' you may need to --add-top-library=unisim.glbl for it to work with Xilinx'
185
+ ' based projects that require "glbl" to be present at simulation $root scope.'
186
+ ),
127
187
 
128
188
  })
129
189
 
@@ -131,11 +191,29 @@ class CommandSim(CommandDesign): # pylint: disable=too-many-public-methods
131
191
  'uvm-version': { 'choices': ['1.2'] },
132
192
  })
133
193
 
194
+ # only used if self.library_map_supported=True:
195
+ self.library_map = {} # lib-name: path-to.lib dict from self.args['library-map']
196
+
134
197
 
135
198
 
136
- def process_parameters_get_list(self, arg_prefix: str = '-G') -> list:
137
- '''Returns list (suitable command list for shell or for tool) from self.parameters'''
138
- return parameters_dict_get_command_list(params=self.parameters, arg_prefix=arg_prefix)
199
+ def process_parameters_get_list(
200
+ self, arg_prefix: str = '-G', **kwargs
201
+ ) -> list:
202
+ '''Returns list (suitable command list for shell or for tool) from self.parameters
203
+
204
+ Some simulators will set ALL parameters with Name=Value at all levels of hierarchy,
205
+ so to avoid that and only set top level parameters, but still allow package or
206
+ hierarchy parameters to be set, you can use args (in kwargs):
207
+
208
+ hier_delimiter='/'
209
+ top_hier_str=f'/{TOP_MODULE_NAME}/'
210
+
211
+ Which will, if a parameter Name does not contain the heir delimiter, the Name
212
+ will be prepended with top_hier_str.
213
+ '''
214
+ return parameters_dict_get_command_list(
215
+ params=self.parameters, arg_prefix=arg_prefix, **kwargs
216
+ )
139
217
 
140
218
  def process_plusarg(self, plusarg: str, pwd: str = os.getcwd()) -> None:
141
219
  '''Override for CommandDesign.process_plusarg(..)'''
@@ -159,6 +237,8 @@ class CommandSim(CommandDesign): # pylint: disable=too-many-public-methods
159
237
  if self.stop_process_tokens_before_do_it():
160
238
  return unparsed
161
239
 
240
+ self.handle_arg_license_queue()
241
+
162
242
  # add defines for this job type
163
243
  if self.args['lint'] or self.args['stop-after-elaborate']:
164
244
  self.args['lint'] = True
@@ -364,7 +444,7 @@ class CommandSim(CommandDesign): # pylint: disable=too-many-public-methods
364
444
  cmds2 = self.get_simulate_command_lists()
365
445
  cmds3 = self.get_post_simulate_command_lists()
366
446
  '''
367
- return
447
+ self.update_library_map()
368
448
 
369
449
 
370
450
  def check_logs_for_errors( # pylint: disable=dangerous-default-value,too-many-locals,too-many-branches
@@ -572,3 +652,107 @@ class CommandSim(CommandDesign): # pylint: disable=too-many-public-methods
572
652
  search_paths=self.args['work-dir'], file_extension='vcd',
573
653
  typ='waveform', description='Simulation Waveform VCD (Value Change Dump) file'
574
654
  )
655
+
656
+
657
+ def create_ext_defines_sv(self) -> None:
658
+ '''Creates _ext_defines.sv in the work-dir, adds to front of self.files_sv'''
659
+
660
+ if not self.defines:
661
+ return
662
+
663
+ if not self.args['ext-defines-sv-fname']:
664
+ return
665
+
666
+ fpath = os.path.join(self.args['work-dir'], self.args['ext-defines-sv-fname'])
667
+ fpath_abs = os.path.abspath(fpath)
668
+ if fpath_abs in self.files:
669
+ util.warning(f'{fpath} already exists, will overwrite. Consider changing the value',
670
+ f'of arg: --ext-defines-sv-fname={self.args["ext-defines-sv-fname"]}')
671
+ if not fpath.endswith('.sv') and not fpath.endswith('.v'):
672
+ util.error(f'--ext-defines-sv-fname={self.args["ext-defines-sv-fname"]} must end with',
673
+ '.v or .sv')
674
+ return
675
+
676
+ ifdef_guard = os.path.split(self.args['ext-defines-sv-fname'])[1].upper()
677
+ ifdef_guard = ifdef_guard.replace('.', '__').replace('/', '__')
678
+ with open(fpath, 'w', encoding='utf-8') as f:
679
+ f.write(f'`ifndef {ifdef_guard}\n')
680
+ f.write(f'`define {ifdef_guard}\n')
681
+ f.write('\n')
682
+ for key, value in self.defines.items():
683
+ if value is None:
684
+ f.write(f'`define {key}\n')
685
+ else:
686
+ # if the value v is a double-quoted string, such as v='"hi"' that needs
687
+ # to be preserved.
688
+ f.write(f'`define {key} {value}\n')
689
+
690
+ f.write('\n')
691
+ f.write(f'`endif // {ifdef_guard}\n')
692
+
693
+ self.files_sv.insert(0, fpath_abs)
694
+ self.files[fpath_abs] = True
695
+ self.files_caller_info[fpath_abs] = (
696
+ 'opencos.commands.sim::CommandSim::create_ext_defines_sv'
697
+ )
698
+ util.info(f'command "sim": {fpath} created for defines: {list(self.defines.keys())}')
699
+
700
+
701
+ def handle_arg_license_queue(self) -> None:
702
+ '''Handles self.args['license-queue'] (bool) to set env vars'''
703
+ if not self.args['license-queue']:
704
+ return
705
+
706
+ if 'LICENSE_QUEUE' not in os.environ:
707
+ os.environ['LICENSE_QUEUE'] = '1'
708
+
709
+ def update_library_map(self, base_search_paths: list | None = None) -> None:
710
+ '''
711
+ Returns None, uses self.args['library-map'] to update:
712
+
713
+ - self.library_map (dict)
714
+ - self.args['sim-library']
715
+ '''
716
+ if not self.args['library-map']:
717
+ return
718
+
719
+ if not self.library_map_supported:
720
+ tool = self.config.get('tool', None)
721
+ util.warning(f'Command: sim, --tool={tool}, args for --library-map are not supported:',
722
+ f'{self.args["library-map"]}')
723
+ return
724
+
725
+ for libmap in self.args['library-map']:
726
+ parts = libmap.split(':')
727
+ lib_name = parts[0]
728
+ lib_path = Path(':'.join(parts[1:]))
729
+ lib_abs_path = None
730
+
731
+ if not lib_name or not lib_path:
732
+ util.warning(f'library-map={libmap} could not parse {lib_name=} or {lib_path=}')
733
+ continue
734
+
735
+ if lib_name in self.library_map:
736
+ util.warning(
737
+ f'We have already mapped "{lib_name}" to {self.library_map[lib_name]}'
738
+ )
739
+
740
+ # We have to go looking for lib_path though
741
+ search_files = [lib_path]
742
+ if base_search_paths and isinstance(base_search_paths, list):
743
+ for x in base_search_paths:
744
+ search_files.append(Path(x) / lib_path)
745
+ for x in search_files:
746
+ if x.exists():
747
+ lib_abs_path = x.resolve()
748
+ util.info(f'library-map={libmap} found "{lib_name}" in: {lib_abs_path}')
749
+ break
750
+ util.debug(f'library-map={libmap} searching for "{lib_name}" in: {x}')
751
+
752
+
753
+ if lib_abs_path:
754
+ self.library_map[lib_name] = lib_abs_path
755
+ if lib_name not in self.args['sim-library']:
756
+ self.args['sim-library'].append(lib_name)
757
+ else:
758
+ util.warning(f'library-map={libmap}, for "{lib_name}" could not find {lib_path=}')
opencos/deps/deps_file.py CHANGED
@@ -124,6 +124,7 @@ def get_all_targets( # pylint: disable=dangerous-default-value,too-many-locals,t
124
124
  targets = list(targets)
125
125
  if not targets and error_on_empty_return:
126
126
  error(f'get_all_targets: {base_path=} {filter_using_multi=} returned no targets')
127
+ targets.sort()
127
128
  return targets
128
129
 
129
130
  targets = set()
@@ -144,7 +145,9 @@ def get_all_targets( # pylint: disable=dangerous-default-value,too-many-locals,t
144
145
 
145
146
  if not targets and error_on_empty_return:
146
147
  error(f'get_all_targets: {base_path=} {dirs=} {filter_str=} returned no targets')
147
- return list(targets)
148
+ targets = list(targets)
149
+ targets.sort()
150
+ return targets
148
151
 
149
152
 
150
153
  def deps_target_get_deps_list(
opencos/deps_schema.py CHANGED
@@ -336,10 +336,10 @@ FILE_SIMPLIFIED = Schema(
336
336
  {
337
337
  Optional('args'): [str],
338
338
  Optional('defines'): {
339
- Optional(str): Or(type(None), str),
339
+ Optional(str): Or(type(None), int, str),
340
340
  },
341
341
  Optional('plusargs'): {
342
- Optional(str): Or(type(None), str),
342
+ Optional(str): Or(type(None), int, str),
343
343
  },
344
344
  Optional('parameters'): {
345
345
  Optional(str): str,