opencos-eda 0.3.12__py3-none-any.whl → 0.3.13__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 +121 -7
  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 +28 -4
  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 +16 -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 +49 -25
  18. opencos/tools/questa_fe.py +0 -14
  19. opencos/tools/questa_fse.py +0 -14
  20. opencos/tools/riviera.py +54 -24
  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 +13 -11
  26. opencos/tools/vivado.py +49 -18
  27. opencos/tools/yosys.py +1 -3
  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.13.dist-info}/METADATA +1 -2
  31. {opencos_eda-0.3.12.dist-info → opencos_eda-0.3.13.dist-info}/RECORD +36 -36
  32. {opencos_eda-0.3.12.dist-info → opencos_eda-0.3.13.dist-info}/WHEEL +0 -0
  33. {opencos_eda-0.3.12.dist-info → opencos_eda-0.3.13.dist-info}/entry_points.txt +0 -0
  34. {opencos_eda-0.3.12.dist-info → opencos_eda-0.3.13.dist-info}/licenses/LICENSE +0 -0
  35. {opencos_eda-0.3.12.dist-info → opencos_eda-0.3.13.dist-info}/licenses/LICENSE.spdx +0 -0
  36. {opencos_eda-0.3.12.dist-info → opencos_eda-0.3.13.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,32 @@ 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
15
16
 
16
17
  from opencos import util, export_helper
17
18
  from opencos.eda_base import CommandDesign, Tool
18
19
  from opencos.utils import status_constants
19
20
 
20
- from opencos.utils.str_helpers import sanitize_defines_for_sh, strip_outer_quotes
21
+ from opencos.utils.str_helpers import strip_outer_quotes
21
22
 
22
- def parameters_dict_get_command_list(params: dict, arg_prefix: str = '-G') -> list:
23
- '''Given dict of parameters, returns a command list'''
23
+ def parameters_dict_get_command_list(
24
+ params: dict, arg_prefix: str = '-G',
25
+ hier_delimiter: str = '.',
26
+ top_hier_str: str = '',
27
+ for_flist: bool = False
28
+ ) -> list:
29
+ '''Given dict of parameters, returns a command list.
30
+
31
+ Some simulators will set ALL parameters with Name=Value at all levels of hierarchy,
32
+ so to avoid that and only set top level parameters, but still allow package or
33
+ hierarchy parameters to be set, you can use:
34
+
35
+ hier_delimiter='/'
36
+ top_hier_str=f'/{TOP_MODULE_NAME}/'
37
+
38
+ Which will, if a parameter Name does not contain the heir delimiter, the Name
39
+ will be prepended with top_hier_str.
40
+ '''
24
41
 
25
42
  ret_list = []
26
43
  if ' ' in arg_prefix:
@@ -34,13 +51,29 @@ def parameters_dict_get_command_list(params: dict, arg_prefix: str = '-G') -> li
34
51
  if not isinstance(v, (int, str)):
35
52
  util.warning(f'parameter {k} has value: {v}, parameters must be int/string types')
36
53
 
54
+ if top_hier_str and hier_delimiter not in k:
55
+ k = f'{top_hier_str}{k}'
56
+
37
57
  ret_list.extend(arg_list_prefix)
38
58
  if isinstance(v, int):
39
59
  ret_list.append(f'{arg_str_prefix}{k}={v}')
40
60
  else: # string
61
+ # Note if for_flist=True, then we have to take extra care on what gets
62
+ # double-quotes " and \".
41
63
  v = strip_outer_quotes(v.strip('\n'))
42
64
  v = '"' + v + '"'
43
- ret_list.append(f'{arg_str_prefix}{k}={sanitize_defines_for_sh(v)}')
65
+
66
+ if for_flist:
67
+ # do not wrap with " unless there are spaces, if there are spaces
68
+ # wrap the entire thing with single quotes (bash)
69
+ if ' ' in v:
70
+ ret_list.append(shlex.quote(f'{arg_str_prefix}{k}={v}'))
71
+ else:
72
+ ret_list.append(f'{arg_str_prefix}{k}={v}')
73
+ else:
74
+ # This is for a tool, so simply guard single quotes:
75
+ v = v.replace("'", "\\" + "'")
76
+ ret_list.append(f'{arg_str_prefix}{k}={v}')
44
77
  return ret_list
45
78
 
46
79
 
@@ -79,6 +112,8 @@ class CommandSim(CommandDesign): # pylint: disable=too-many-public-methods
79
112
  # not used by other simulators, so these can go in DEPS files for custom things
80
113
  # like -CFLAGS -O0, etc.
81
114
  'verilate-args': [],
115
+ 'ext-defines-sv-fname': '',
116
+ 'license-queue': False,
82
117
  })
83
118
  self.args_help.update({
84
119
  'pre-sim-tcl': (
@@ -124,6 +159,15 @@ class CommandSim(CommandDesign): # pylint: disable=too-many-public-methods
124
159
  'uvm-version': (
125
160
  'Used if --uvm is set, for example --uvm-version=1.2'
126
161
  ),
162
+ 'ext-defines-sv-fname': (
163
+ 'If set to empty str, --ext-defines-sv-fname="", applies defines to tool via args.'
164
+ ' If set to non-emtpy str as a .sv file name, such as'
165
+ ' --ext-defines-sv-fname=_ext_defines.sv, saves defines to a file name in the'
166
+ ' work-dif, and adds this file to the SV/Verilog file list ahead of other files.'
167
+ ),
168
+ 'license-queue': (
169
+ 'Set to enable env vars LICENSE_QUEUE=1 which has effects on certain simulators'
170
+ ),
127
171
 
128
172
  })
129
173
 
@@ -133,9 +177,24 @@ class CommandSim(CommandDesign): # pylint: disable=too-many-public-methods
133
177
 
134
178
 
135
179
 
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)
180
+ def process_parameters_get_list(
181
+ self, arg_prefix: str = '-G', **kwargs
182
+ ) -> list:
183
+ '''Returns list (suitable command list for shell or for tool) from self.parameters
184
+
185
+ Some simulators will set ALL parameters with Name=Value at all levels of hierarchy,
186
+ so to avoid that and only set top level parameters, but still allow package or
187
+ hierarchy parameters to be set, you can use args (in kwargs):
188
+
189
+ hier_delimiter='/'
190
+ top_hier_str=f'/{TOP_MODULE_NAME}/'
191
+
192
+ Which will, if a parameter Name does not contain the heir delimiter, the Name
193
+ will be prepended with top_hier_str.
194
+ '''
195
+ return parameters_dict_get_command_list(
196
+ params=self.parameters, arg_prefix=arg_prefix, **kwargs
197
+ )
139
198
 
140
199
  def process_plusarg(self, plusarg: str, pwd: str = os.getcwd()) -> None:
141
200
  '''Override for CommandDesign.process_plusarg(..)'''
@@ -159,6 +218,8 @@ class CommandSim(CommandDesign): # pylint: disable=too-many-public-methods
159
218
  if self.stop_process_tokens_before_do_it():
160
219
  return unparsed
161
220
 
221
+ self.handle_arg_license_queue()
222
+
162
223
  # add defines for this job type
163
224
  if self.args['lint'] or self.args['stop-after-elaborate']:
164
225
  self.args['lint'] = True
@@ -572,3 +633,56 @@ class CommandSim(CommandDesign): # pylint: disable=too-many-public-methods
572
633
  search_paths=self.args['work-dir'], file_extension='vcd',
573
634
  typ='waveform', description='Simulation Waveform VCD (Value Change Dump) file'
574
635
  )
636
+
637
+
638
+ def create_ext_defines_sv(self) -> None:
639
+ '''Creates _ext_defines.sv in the work-dir, adds to front of self.files_sv'''
640
+
641
+ if not self.defines:
642
+ return
643
+
644
+ if not self.args['ext-defines-sv-fname']:
645
+ return
646
+
647
+ fpath = os.path.join(self.args['work-dir'], self.args['ext-defines-sv-fname'])
648
+ fpath_abs = os.path.abspath(fpath)
649
+ if fpath_abs in self.files:
650
+ util.warning(f'{fpath} already exists, will overwrite. Consider changing the value',
651
+ f'of arg: --ext-defines-sv-fname={self.args["ext-defines-sv-fname"]}')
652
+ if not fpath.endswith('.sv') and not fpath.endswith('.v'):
653
+ util.error(f'--ext-defines-sv-fname={self.args["ext-defines-sv-fname"]} must end with',
654
+ '.v or .sv')
655
+ return
656
+
657
+ ifdef_guard = os.path.split(self.args['ext-defines-sv-fname'])[1].upper()
658
+ ifdef_guard = ifdef_guard.replace('.', '__').replace('/', '__')
659
+ with open(fpath, 'w', encoding='utf-8') as f:
660
+ f.write(f'`ifndef {ifdef_guard}\n')
661
+ f.write(f'`define {ifdef_guard}\n')
662
+ f.write('\n')
663
+ for key, value in self.defines.items():
664
+ if value is None:
665
+ f.write(f'`define {key}\n')
666
+ else:
667
+ # if the value v is a double-quoted string, such as v='"hi"' that needs
668
+ # to be preserved.
669
+ f.write(f'`define {key} {value}\n')
670
+
671
+ f.write('\n')
672
+ f.write(f'`endif // {ifdef_guard}\n')
673
+
674
+ self.files_sv.insert(0, fpath_abs)
675
+ self.files[fpath_abs] = True
676
+ self.files_caller_info[fpath_abs] = (
677
+ 'opencos.commands.sim::CommandSim::create_ext_defines_sv'
678
+ )
679
+ util.info(f'command "sim": {fpath} created for defines: {list(self.defines.keys())}')
680
+
681
+
682
+ def handle_arg_license_queue(self) -> None:
683
+ '''Handles self.args['license-queue'] (bool) to set env vars'''
684
+ if not self.args['license-queue']:
685
+ return
686
+
687
+ if 'LICENSE_QUEUE' not in os.environ:
688
+ os.environ['LICENSE_QUEUE'] = '1'
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,
opencos/eda.py CHANGED
@@ -75,7 +75,7 @@ def init_config(
75
75
  config['command_handler'][_cmd] = cls
76
76
 
77
77
  config['auto_tools_found'] = {}
78
- config['tools_loaded'] = set()
78
+ config['tools_loaded'] = []
79
79
 
80
80
  # If this is a sub_command sitation, then use that for setting up tools, otherwise
81
81
  # use the command. If there's no command, blank str is fine will load all the tools, or
@@ -192,7 +192,7 @@ def auto_tool_setup( # pylint: disable=too-many-locals,too-many-branches,too-man
192
192
  '''Returns an updated config, uses config['auto_tools_order'] dict and
193
193
  config['tools'] dict, calls tool_setup(..)
194
194
 
195
- -- adds items to config['tools_loaded'] set
195
+ -- adds items to config['tools_loaded'] list
196
196
  -- updates config['command_handler'][command] with a Tool class
197
197
 
198
198
  Input arg tool can be in the form (for example):
@@ -210,7 +210,7 @@ def auto_tool_setup( # pylint: disable=too-many-locals,too-many-branches,too-man
210
210
  if 'auto_tools_found' not in config:
211
211
  config['auto_tools_found'] = {}
212
212
  if 'tools_loaded' not in config:
213
- config['tools_loaded'] = set()
213
+ config['tools_loaded'] = []
214
214
 
215
215
  util.debug(f'Calling auto_tool_setup for {tool=} {command=}')
216
216
 
@@ -223,12 +223,14 @@ def auto_tool_setup( # pylint: disable=too-many-locals,too-many-branches,too-man
223
223
 
224
224
  # Step 1 - Load tools (do not set config['command_handler'][command])
225
225
 
226
- for name, tool_cfg in config['tools'].items():
226
+ if tool:
227
+ # if called with tool=(some_name), then only load that tool (which is not
228
+ # this one). Also delete this entry from config['tools'] because we don't need it.
229
+ for name in list(config['tools'].keys()):
230
+ if tool != name:
231
+ del config['tools'][name]
227
232
 
228
- if tool and tool != name:
229
- # if called with tool=(some_name), then only load that tool (which is not
230
- # this one)
231
- continue
233
+ for name, tool_cfg in config['tools'].items():
232
234
 
233
235
  if name in config['auto_tools_found']:
234
236
  # we already loaded this tool.
@@ -326,7 +328,8 @@ def auto_tool_setup( # pylint: disable=too-many-locals,too-many-branches,too-man
326
328
  else:
327
329
  p = safe_shutil_which(exe_list[0])
328
330
  config['auto_tools_found'][name] = p # populate key-value pairs w/ first exe in list
329
- config['tools_loaded'].add(name)
331
+ if name not in config['tools_loaded']:
332
+ config['tools_loaded'].append(name)
330
333
  if not quiet:
331
334
  util.info(f"Detected {name} ({p})")
332
335
  else: