opencos-eda 0.2.55__py3-none-any.whl → 0.2.57__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opencos/commands/flist.py +10 -0
- opencos/commands/lec.py +4 -0
- opencos/commands/multi.py +4 -0
- opencos/commands/sim.py +31 -0
- opencos/deps/defaults.py +2 -0
- opencos/deps/deps_processor.py +39 -1
- opencos/deps_schema.py +22 -0
- opencos/eda.py +13 -1
- opencos/eda_base.py +108 -10
- opencos/eda_config.py +37 -2
- opencos/eda_config_defaults.yml +1 -0
- opencos/export_helper.py +20 -6
- opencos/hw/oc_cli.py +1 -1
- opencos/names.py +4 -1
- opencos/tests/helpers.py +9 -0
- opencos/tests/test_eda_synth.py +12 -0
- opencos/tools/cocotb.py +25 -0
- opencos/tools/invio_helpers.py +25 -4
- opencos/tools/iverilog.py +5 -0
- opencos/tools/modelsim_ase.py +52 -6
- opencos/tools/quartus.py +32 -6
- opencos/tools/riviera.py +17 -3
- opencos/tools/slang.py +5 -0
- opencos/tools/slang_yosys.py +13 -1
- opencos/tools/surelog.py +5 -0
- opencos/tools/verilator.py +5 -0
- opencos/tools/vivado.py +15 -2
- opencos/tools/yosys.py +50 -20
- opencos/utils/vsim_helper.py +1 -2
- {opencos_eda-0.2.55.dist-info → opencos_eda-0.2.57.dist-info}/METADATA +1 -1
- {opencos_eda-0.2.55.dist-info → opencos_eda-0.2.57.dist-info}/RECORD +36 -36
- {opencos_eda-0.2.55.dist-info → opencos_eda-0.2.57.dist-info}/WHEEL +0 -0
- {opencos_eda-0.2.55.dist-info → opencos_eda-0.2.57.dist-info}/entry_points.txt +0 -0
- {opencos_eda-0.2.55.dist-info → opencos_eda-0.2.57.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.2.55.dist-info → opencos_eda-0.2.57.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.2.55.dist-info → opencos_eda-0.2.57.dist-info}/top_level.txt +0 -0
opencos/commands/flist.py
CHANGED
|
@@ -10,6 +10,7 @@ import os
|
|
|
10
10
|
from opencos import util
|
|
11
11
|
from opencos.eda_base import CommandDesign
|
|
12
12
|
from opencos.utils.str_helpers import strip_all_quotes
|
|
13
|
+
from opencos.commands.sim import parameters_dict_get_command_list
|
|
13
14
|
|
|
14
15
|
class CommandFList(CommandDesign):
|
|
15
16
|
'''Base class command handler for: eda flist ...'''
|
|
@@ -22,6 +23,7 @@ class CommandFList(CommandDesign):
|
|
|
22
23
|
'eda-dir' : 'eda.flist', # user can specify eda-dir if files are generated.
|
|
23
24
|
'out' : "flist.out",
|
|
24
25
|
'emit-define' : True,
|
|
26
|
+
'emit-parameter' : True,
|
|
25
27
|
'emit-incdir' : True,
|
|
26
28
|
'emit-v' : True,
|
|
27
29
|
'emit-sv' : True,
|
|
@@ -29,6 +31,7 @@ class CommandFList(CommandDesign):
|
|
|
29
31
|
'emit-cpp' : True,
|
|
30
32
|
'emit-non-sources' : True, # as comments, from DEPS 'reqs'
|
|
31
33
|
'prefix-define' : "+define+",
|
|
34
|
+
'prefix-parameter' : "-G",
|
|
32
35
|
'prefix-incdir' : "+incdir+",
|
|
33
36
|
'prefix-v' : "",
|
|
34
37
|
'prefix-sv' : "",
|
|
@@ -179,6 +182,13 @@ class CommandFList(CommandDesign):
|
|
|
179
182
|
newline = prefix + qd1 + f"{d}{ed1}{value}" + qd2
|
|
180
183
|
print(newline, file=fo)
|
|
181
184
|
|
|
185
|
+
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)
|
|
191
|
+
|
|
182
192
|
if self.args['emit-incdir']:
|
|
183
193
|
prefix = strip_all_quotes(self.args['prefix-incdir'])
|
|
184
194
|
for i in self.incdirs:
|
opencos/commands/lec.py
CHANGED
|
@@ -27,6 +27,7 @@ class CommandLec(CommandDesign):
|
|
|
27
27
|
self.args.update({
|
|
28
28
|
'designs': [],
|
|
29
29
|
'synth': True,
|
|
30
|
+
'flatten-all': True,
|
|
30
31
|
})
|
|
31
32
|
self.args_help.update({
|
|
32
33
|
'designs': (
|
|
@@ -34,6 +35,9 @@ class CommandLec(CommandDesign):
|
|
|
34
35
|
' use this arg twice'
|
|
35
36
|
),
|
|
36
37
|
'synth': 'run synthesis on the two designs prior to running LEC',
|
|
38
|
+
'flatten-all': (
|
|
39
|
+
'arg passed to "synth" if run with --synth, to disable use --no-flatten-all'
|
|
40
|
+
),
|
|
37
41
|
})
|
|
38
42
|
|
|
39
43
|
self.synth_design_verilog_fpaths = ['', '']
|
opencos/commands/multi.py
CHANGED
|
@@ -327,6 +327,10 @@ class CommandMulti(CommandParallel):
|
|
|
327
327
|
|
|
328
328
|
if parsed.fail_if_no_targets and len(self.targets) == 0:
|
|
329
329
|
self.error(f'Multi: --fail-if-no-targets set, and {self.targets=}')
|
|
330
|
+
if not all_multi_tools:
|
|
331
|
+
possible_tools = self.all_handler_commands.get(command, [])
|
|
332
|
+
self.error(f'Multi: no tools to run for {command=}, available tools: {possible_tools}')
|
|
333
|
+
|
|
330
334
|
util.info("Multi: About to run: ", end="")
|
|
331
335
|
|
|
332
336
|
def get_pretty_targets_tuple_as_list(l:list):
|
opencos/commands/sim.py
CHANGED
|
@@ -17,6 +17,33 @@ from opencos import util, export_helper
|
|
|
17
17
|
from opencos.eda_base import CommandDesign, Tool
|
|
18
18
|
from opencos.utils import status_constants
|
|
19
19
|
|
|
20
|
+
from opencos.utils.str_helpers import sanitize_defines_for_sh, strip_outer_quotes
|
|
21
|
+
|
|
22
|
+
def parameters_dict_get_command_list(params: dict, arg_prefix: str = '-G') -> list:
|
|
23
|
+
'''Given dict of parameters, returns a command list'''
|
|
24
|
+
|
|
25
|
+
ret_list = []
|
|
26
|
+
if ' ' in arg_prefix:
|
|
27
|
+
arg_list_prefix = arg_prefix.split()
|
|
28
|
+
arg_str_prefix = ''
|
|
29
|
+
else:
|
|
30
|
+
arg_list_prefix = []
|
|
31
|
+
arg_str_prefix = arg_prefix
|
|
32
|
+
|
|
33
|
+
for k,v in params.items():
|
|
34
|
+
if not isinstance(v, (int, str)):
|
|
35
|
+
util.warning(f'parameter {k} has value: {v}, parameters must be int/string types')
|
|
36
|
+
|
|
37
|
+
ret_list.extend(arg_list_prefix)
|
|
38
|
+
if isinstance(v, int):
|
|
39
|
+
ret_list.append(f'{arg_str_prefix}{k}={v}')
|
|
40
|
+
else: # string
|
|
41
|
+
v = strip_outer_quotes(v.strip('\n'))
|
|
42
|
+
v = '"' + v + '"'
|
|
43
|
+
ret_list.append(f'{arg_str_prefix}{k}={sanitize_defines_for_sh(v)}')
|
|
44
|
+
return ret_list
|
|
45
|
+
|
|
46
|
+
|
|
20
47
|
class CommandSim(CommandDesign):
|
|
21
48
|
'''Base class command handler for: eda sim ...'''
|
|
22
49
|
|
|
@@ -80,6 +107,10 @@ class CommandSim(CommandDesign):
|
|
|
80
107
|
|
|
81
108
|
self.args['verilate-args'] = []
|
|
82
109
|
|
|
110
|
+
def process_parameters_get_list(self, arg_prefix: str = '-G') -> list:
|
|
111
|
+
'''Returns list (suitable command list for shell or for tool) from self.parameters'''
|
|
112
|
+
return parameters_dict_get_command_list(params=self.parameters, arg_prefix=arg_prefix)
|
|
113
|
+
|
|
83
114
|
def process_plusarg(self, plusarg: str, pwd: str = os.getcwd()) -> None:
|
|
84
115
|
'''Override for CommandDesign.process_plusarg(..)'''
|
|
85
116
|
maybe_plusarg = CommandDesign.process_plusarg(self, plusarg, pwd)
|
opencos/deps/defaults.py
CHANGED
|
@@ -31,6 +31,7 @@ KNOWN_EDA_COMMANDS = set([
|
|
|
31
31
|
SUPPORTED_TARGET_TABLE_KEYS = set([
|
|
32
32
|
'args',
|
|
33
33
|
'defines',
|
|
34
|
+
'parameters',
|
|
34
35
|
'incdirs',
|
|
35
36
|
'top',
|
|
36
37
|
'deps',
|
|
@@ -52,6 +53,7 @@ SUPPORTED_TAG_KEYS = set([
|
|
|
52
53
|
'deps',
|
|
53
54
|
'reqs',
|
|
54
55
|
'defines',
|
|
56
|
+
'parameters',
|
|
55
57
|
'incdirs',
|
|
56
58
|
'replace-config-tools',
|
|
57
59
|
'additive-config-tools',
|
opencos/deps/deps_processor.py
CHANGED
|
@@ -91,6 +91,22 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
91
91
|
v = v.replace('%SEED%', str(self.args.get('seed', 1)))
|
|
92
92
|
self.command_design_ref.process_plusarg(f'+define+{k}={v}')
|
|
93
93
|
|
|
94
|
+
|
|
95
|
+
def apply_parameters(self, parameters_dict: dict):
|
|
96
|
+
'''Given parameters_dict, applies them to our self.command_design_ref obj'''
|
|
97
|
+
if not isinstance(parameters_dict, dict):
|
|
98
|
+
self.error(f"{parameters_dict=} is not type dict, can't apply defines,",
|
|
99
|
+
f"in {self.caller_info}")
|
|
100
|
+
for k,v in parameters_dict.items():
|
|
101
|
+
if v is None or v == '' or not isinstance(v, (int, str, bool)):
|
|
102
|
+
warning(f'parameter {k} has value: {v}, parameters must be bool/int/string types',
|
|
103
|
+
f'from {self.caller_info}')
|
|
104
|
+
else:
|
|
105
|
+
self.command_design_ref.set_parameter(
|
|
106
|
+
name=k, value=v, caller_info=self.caller_info
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
94
110
|
def apply_incdirs(self, incdirs_list:list):
|
|
95
111
|
'''Given incdirs_list, applies them to our self.command_design_ref obj'''
|
|
96
112
|
if not isinstance(incdirs_list, (str, list)):
|
|
@@ -252,6 +268,8 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
252
268
|
remaining_deps_list += self.process_tags()
|
|
253
269
|
elif key == 'defines':
|
|
254
270
|
self.process_defines()
|
|
271
|
+
elif key == 'parameters':
|
|
272
|
+
self.process_parameters()
|
|
255
273
|
elif key == 'incdirs':
|
|
256
274
|
self.process_incdirs()
|
|
257
275
|
elif key == 'top':
|
|
@@ -426,6 +444,9 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
426
444
|
# apply defines:
|
|
427
445
|
self.apply_defines(value.get('defines', {}))
|
|
428
446
|
|
|
447
|
+
elif key == 'parameters':
|
|
448
|
+
self.apply_parameters(value.get('parameters', {}))
|
|
449
|
+
|
|
429
450
|
elif key == 'incdirs':
|
|
430
451
|
# apply incdirs:
|
|
431
452
|
self.apply_incdirs(value.get('incdirs', []))
|
|
@@ -517,6 +538,22 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
517
538
|
|
|
518
539
|
self.apply_defines(entry_defines)
|
|
519
540
|
|
|
541
|
+
def process_parameters(self):
|
|
542
|
+
'''Returns None, applies parameters (dict, if any) from self.deps_entry to
|
|
543
|
+
self.command_design_ref.'''
|
|
544
|
+
|
|
545
|
+
# Parameters:
|
|
546
|
+
# apply command specific parameters, with higher priority than the a
|
|
547
|
+
# deps_entry['sim']['parameters'] entry,
|
|
548
|
+
# do this with dict1.update(dict2):
|
|
549
|
+
entry_parameters = {}
|
|
550
|
+
entry_parameters.update(self.deps_entry.get('parameters', {}))
|
|
551
|
+
entry_parameters.update(self.entry_eda_command.get('parameters', {}))
|
|
552
|
+
assert isinstance(entry_parameters, dict), \
|
|
553
|
+
f'{entry_parameters=} for in {self.caller_info} must be a dict'
|
|
554
|
+
|
|
555
|
+
self.apply_parameters(entry_parameters)
|
|
556
|
+
|
|
520
557
|
def process_incdirs(self) -> None:
|
|
521
558
|
'''Returns None, applies incdirs (dict, if any) from self.deps_entry to
|
|
522
559
|
self.command_design_ref.'''
|
|
@@ -700,7 +737,8 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
700
737
|
deps_targets_to_resolve.append(command_tuple)
|
|
701
738
|
|
|
702
739
|
|
|
703
|
-
elif isinstance(dep, str) and
|
|
740
|
+
elif isinstance(dep, str) and \
|
|
741
|
+
any(dep.startswith(x) for x in ['+define+', '+incdir+']):
|
|
704
742
|
# Note: we still support +define+ and +incdir in the deps list.
|
|
705
743
|
# check for compile-time Verilog style plusarg, which are supported under targets
|
|
706
744
|
# These are not run-time Verilog style plusargs comsumable from within the .sv:
|
opencos/deps_schema.py
CHANGED
|
@@ -11,6 +11,9 @@ my_target_name:
|
|
|
11
11
|
defines: <---- defines, optional table
|
|
12
12
|
USER_DEFINE_VALUE: 12
|
|
13
13
|
|
|
14
|
+
parameters: <---- parameters, optional table
|
|
15
|
+
USER_PARAM_VALUE: 12
|
|
16
|
+
|
|
14
17
|
incdirs: <---- incdirs, optional array (or string)
|
|
15
18
|
- ./
|
|
16
19
|
|
|
@@ -32,6 +35,9 @@ my_target_name:
|
|
|
32
35
|
defines: <---- defines, optional table of (key: str Or null)
|
|
33
36
|
USER_DEFINE_VALUE: 12
|
|
34
37
|
|
|
38
|
+
parameters: <---- parameters, optional table
|
|
39
|
+
USER_PARAM_VALUE: 12
|
|
40
|
+
|
|
35
41
|
incdirs: <---- incdirs, optional array of strings
|
|
36
42
|
- ./
|
|
37
43
|
|
|
@@ -55,6 +61,7 @@ METADATA: <---- (optional) unstructured table
|
|
|
55
61
|
|
|
56
62
|
my_target_name:
|
|
57
63
|
defines: <---- defines, optional table
|
|
64
|
+
parameters: <---- parameters, optional table
|
|
58
65
|
incdirs: <---- incdirs, optional array (or string)
|
|
59
66
|
top: tb <---- top, optional string
|
|
60
67
|
deps: <---- TARGET_DEPS_CONTENTS schema
|
|
@@ -105,6 +112,8 @@ my_target_name:
|
|
|
105
112
|
to the current target for this named eda command.
|
|
106
113
|
defines: <---- optional table, defines to be applied to the current
|
|
107
114
|
target for this named eda command.
|
|
115
|
+
parameters: <---- optional table, parameters to be applied to the current
|
|
116
|
+
target to for this named eda command.
|
|
108
117
|
incdirs: <---- optional array (or string) incdirs to be applied to
|
|
109
118
|
the current target for this named eda command.
|
|
110
119
|
tags: <---- tags, optional table using TARGET_TAGS_TABLE schema.
|
|
@@ -204,6 +213,9 @@ TARGET_EDA_COMMAND_ENTRY_TABLE = {
|
|
|
204
213
|
Optional('defines'): {
|
|
205
214
|
Optional(str): Or(str, int, type(None)),
|
|
206
215
|
},
|
|
216
|
+
Optional('parameters'): {
|
|
217
|
+
Optional(str): Or(str, int),
|
|
218
|
+
},
|
|
207
219
|
Optional('incdirs'): ARRAY_OR_SPACE_SEPARATED_STRING,
|
|
208
220
|
}
|
|
209
221
|
|
|
@@ -226,6 +238,9 @@ TARGET_TAGS_TABLE = {
|
|
|
226
238
|
Optional('defines'): {
|
|
227
239
|
Optional(str): Or(str, int, type(None)),
|
|
228
240
|
},
|
|
241
|
+
Optional('parameters'): {
|
|
242
|
+
Optional(str): Or(str, int),
|
|
243
|
+
},
|
|
229
244
|
Optional('incdirs'): ARRAY_OR_SPACE_SEPARATED_STRING,
|
|
230
245
|
Optional('replace-config-tools'): dict,
|
|
231
246
|
Optional('additive-config-tools'): dict,
|
|
@@ -244,6 +259,10 @@ TARGET_CONTENTS = Or(
|
|
|
244
259
|
Optional('defines'): {
|
|
245
260
|
Optional(str): Or(str, int, type(None)),
|
|
246
261
|
},
|
|
262
|
+
# parameters: table of key-value
|
|
263
|
+
Optional('parameters'): {
|
|
264
|
+
Optional(str): Or(str, int),
|
|
265
|
+
},
|
|
247
266
|
# incdirs: array
|
|
248
267
|
Optional('incdirs'): ARRAY_OR_SPACE_SEPARATED_STRING,
|
|
249
268
|
# top: string
|
|
@@ -302,6 +321,9 @@ FILE_SIMPLIFIED = Schema(
|
|
|
302
321
|
Optional('defines'): {
|
|
303
322
|
Optional(str): Or(type(None), str),
|
|
304
323
|
},
|
|
324
|
+
Optional('parameters'): {
|
|
325
|
+
Optional(str): str,
|
|
326
|
+
},
|
|
305
327
|
Optional('incdirs'): [str],
|
|
306
328
|
Optional('top'): str,
|
|
307
329
|
Optional('deps'): [str],
|
opencos/eda.py
CHANGED
|
@@ -49,6 +49,9 @@ def init_config(
|
|
|
49
49
|
|
|
50
50
|
# For key DEFAULT_HANDLERS, we'll update config['command_handler'] with
|
|
51
51
|
# the actual class using importlib (via opencos.util)
|
|
52
|
+
|
|
53
|
+
eda_config.tool_try_add_to_path(tool)
|
|
54
|
+
|
|
52
55
|
config['command_handler'] = {}
|
|
53
56
|
for _cmd, str_class in config['DEFAULT_HANDLERS'].items():
|
|
54
57
|
cls = util.import_class_from_string(str_class)
|
|
@@ -329,7 +332,7 @@ def tool_setup(tool: str, config: dict, quiet: bool = False, auto_setup: bool =
|
|
|
329
332
|
config['tools_loaded'].add(tool)
|
|
330
333
|
|
|
331
334
|
|
|
332
|
-
def process_tokens( # pylint: disable=too-many-branches,too-many-statements,too-many-locals
|
|
335
|
+
def process_tokens( # pylint: disable=too-many-branches,too-many-statements,too-many-locals,too-many-return-statements
|
|
333
336
|
tokens: list, original_args: list, config: dict, is_interactive=False
|
|
334
337
|
) -> int:
|
|
335
338
|
'''Returns bash/sh style return code int (0 pass, non-zero fail).
|
|
@@ -416,6 +419,15 @@ def process_tokens( # pylint: disable=too-many-branches,too-many-statements,too-
|
|
|
416
419
|
return 2
|
|
417
420
|
|
|
418
421
|
sco = config['command_handler'][command](config=config) # sub command object
|
|
422
|
+
if not parsed.tool:
|
|
423
|
+
# then what was the auto selected tool?
|
|
424
|
+
sco_tool = getattr(sco, '_TOOL', '')
|
|
425
|
+
if sco_tool in config['auto_tools_order'][0] and \
|
|
426
|
+
config['auto_tools_order'][0][sco_tool].get('disable-auto', False):
|
|
427
|
+
util.error(f'Cannot use tool={sco_tool} without arg --tool, it cannot be selected',
|
|
428
|
+
'automatically')
|
|
429
|
+
return status_constants.EDA_COMMAND_OR_ARGS_ERROR
|
|
430
|
+
|
|
419
431
|
util.debug(f'{command=}')
|
|
420
432
|
util.debug(f'{sco.config=}')
|
|
421
433
|
util.debug(f'{type(sco)=}')
|
opencos/eda_base.py
CHANGED
|
@@ -831,7 +831,33 @@ class Command: # pylint: disable=too-many-public-methods
|
|
|
831
831
|
lines.append(indent_me(f" --{k:20} : string : {vstr:12}{khelp}"))
|
|
832
832
|
else:
|
|
833
833
|
lines.append(indent_me(f" --{k:20} : <unknown> : {vstr:12}{khelp}"))
|
|
834
|
+
|
|
835
|
+
lines.append('')
|
|
836
|
+
lines.append(indent_me((
|
|
837
|
+
" -G<parameterName>=<value> "
|
|
838
|
+
" Add parameter to top level, support bit/int/string types only."
|
|
839
|
+
" Example: -GDEPTH=8 (DEPTH treated as SV int/integer)."
|
|
840
|
+
" -GENABLE=1 (ENABLED treated as SV bit/int/integer)."
|
|
841
|
+
" -GName=eda (Name treated as SV string \"eda\")."
|
|
842
|
+
)))
|
|
843
|
+
lines.append(indent_me((
|
|
844
|
+
" +define+<defineName> "
|
|
845
|
+
" Add define w/out value to tool ahead of SV sources"
|
|
846
|
+
" Example: +define+SIM_SPEEDUP"
|
|
847
|
+
)))
|
|
848
|
+
lines.append(indent_me((
|
|
849
|
+
" +define+<defineName>=<value> "
|
|
850
|
+
" Add define w/ value to tool ahead of SV sources"
|
|
851
|
+
" Example: +define+TECH_LIB=2 +define+FULL_NAME=\"E D A\""
|
|
852
|
+
)))
|
|
853
|
+
lines.append(indent_me((
|
|
854
|
+
" +incdir+<path> "
|
|
855
|
+
" Add path (absolute or relative) for include directories"
|
|
856
|
+
" for SystemVerilog `include \"<some-file>.svh\""
|
|
857
|
+
" Example: +incdir+../lib"
|
|
858
|
+
)))
|
|
834
859
|
lines.append('')
|
|
860
|
+
|
|
835
861
|
for line in lines:
|
|
836
862
|
print(line)
|
|
837
863
|
|
|
@@ -922,6 +948,7 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
922
948
|
),
|
|
923
949
|
})
|
|
924
950
|
self.defines = {}
|
|
951
|
+
self.parameters = {}
|
|
925
952
|
self.incdirs = []
|
|
926
953
|
self.files = {}
|
|
927
954
|
self.files_v = []
|
|
@@ -1086,10 +1113,72 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1086
1113
|
# TODO(drew): Use the helper method in util for this instead to peek in file contents?
|
|
1087
1114
|
return os.path.splitext(os.path.basename(name))[0]
|
|
1088
1115
|
|
|
1116
|
+
def set_parameter(
|
|
1117
|
+
self, name: str, value, caller_info: str = '(CLI)',
|
|
1118
|
+
wrap_str_double_quotes: bool = True
|
|
1119
|
+
) -> None:
|
|
1120
|
+
'''Safe wrapper for setting a parameter (can only set once, from CLI or DEPS)'''
|
|
1121
|
+
|
|
1122
|
+
if name in self.parameters:
|
|
1123
|
+
util.debug("Parameter not set because it is already modified",
|
|
1124
|
+
f"(via -G<Name>=<Value>): {name}={value}; orig value",
|
|
1125
|
+
f'{self.parameters[name]} from {caller_info}')
|
|
1126
|
+
else:
|
|
1127
|
+
if isinstance(value, bool):
|
|
1128
|
+
value = int(value)
|
|
1129
|
+
elif isinstance(value, str):
|
|
1130
|
+
value = strip_outer_quotes(value.strip('\n'))
|
|
1131
|
+
if wrap_str_double_quotes:
|
|
1132
|
+
value = '"' + value + '"'
|
|
1133
|
+
self.parameters[name] = value
|
|
1134
|
+
util.debug(f"Parameter (via -G<Name>=<Value>): {name}={value}",
|
|
1135
|
+
f'from {caller_info}')
|
|
1136
|
+
|
|
1137
|
+
|
|
1138
|
+
def process_parameter_arg(
|
|
1139
|
+
self, text: str, pwd: str = os.getcwd()
|
|
1140
|
+
) -> None:
|
|
1141
|
+
'''Retuns None, parses -G<Name>=<Value> adds to internal self.parameters.'''
|
|
1142
|
+
|
|
1143
|
+
# Deal with raw CLI/bash/powershell argparser, strip all outer quotes.
|
|
1144
|
+
text = strip_outer_quotes(text)
|
|
1145
|
+
if not pwd:
|
|
1146
|
+
pwd = ''
|
|
1147
|
+
|
|
1148
|
+
if not text.startswith('-G'):
|
|
1149
|
+
self.error(f"Didn't understand -G parameter arg: '{text}'",
|
|
1150
|
+
error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
|
|
1151
|
+
return
|
|
1152
|
+
|
|
1153
|
+
text = text[2:] # strip leading -G
|
|
1154
|
+
m = re.match(r'^(\w+)$', text)
|
|
1155
|
+
if m:
|
|
1156
|
+
k = m.group(1)
|
|
1157
|
+
util.warning(f"Parameter {k} has no value and will not be applied")
|
|
1158
|
+
return
|
|
1159
|
+
m = re.match(r'^(\w+)\=(\S+)$', text)
|
|
1160
|
+
if not m:
|
|
1161
|
+
m = re.match(r'^(\w+)\=(\"[^\"]*\")$', text)
|
|
1162
|
+
if m:
|
|
1163
|
+
k = m.group(1)
|
|
1164
|
+
v = m.group(2)
|
|
1165
|
+
# since this is coming from a CLI string, we have to guess at types,
|
|
1166
|
+
# for int or bool (not str) and convert bool to int:
|
|
1167
|
+
if not isinstance(v, (int, str)):
|
|
1168
|
+
self.error(f"Didn't understand -G parameter arg: name={k} value={v}",
|
|
1169
|
+
f"value must be int/str type, from: '{text}'",
|
|
1170
|
+
error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
|
|
1171
|
+
return
|
|
1172
|
+
try:
|
|
1173
|
+
v = int(v)
|
|
1174
|
+
except ValueError:
|
|
1175
|
+
pass
|
|
1176
|
+
self.set_parameter(k, v)
|
|
1177
|
+
|
|
1089
1178
|
|
|
1090
1179
|
def process_plusarg( # pylint: disable=too-many-branches
|
|
1091
1180
|
self, plusarg: str, pwd: str = os.getcwd()
|
|
1092
|
-
) ->
|
|
1181
|
+
) -> str:
|
|
1093
1182
|
'''Retuns str, parses a +define+, +incdir+, +key=value str; adds to internal.
|
|
1094
1183
|
|
|
1095
1184
|
Adds to self.defines, self.incdirs,
|
|
@@ -1112,7 +1201,7 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1112
1201
|
k = m.group(1)
|
|
1113
1202
|
self.defines[k] = None
|
|
1114
1203
|
util.debug(f"Defined {k}")
|
|
1115
|
-
return
|
|
1204
|
+
return ''
|
|
1116
1205
|
m = re.match(r'^(\w+)\=(\S+)$', plusarg)
|
|
1117
1206
|
if not m:
|
|
1118
1207
|
m = re.match(r'^(\w+)\=(\"[^\"]*\")$', plusarg)
|
|
@@ -1126,10 +1215,10 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1126
1215
|
v = v.replace('%SEED%', str(self.args.get('seed', 1)))
|
|
1127
1216
|
self.defines[k] = v
|
|
1128
1217
|
util.debug(f"Defined {k}={v}")
|
|
1129
|
-
return
|
|
1218
|
+
return ''
|
|
1130
1219
|
self.error(f"Didn't understand +define+: '{plusarg}'",
|
|
1131
1220
|
error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
|
|
1132
|
-
return
|
|
1221
|
+
return ''
|
|
1133
1222
|
|
|
1134
1223
|
if plusarg.startswith('+incdir+'):
|
|
1135
1224
|
plusarg = plusarg[len('+incdir+'):]
|
|
@@ -1139,16 +1228,16 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1139
1228
|
if incdir not in self.incdirs:
|
|
1140
1229
|
self.incdirs.append(os.path.abspath(incdir))
|
|
1141
1230
|
util.debug(f"Added include dir '{os.path.abspath(incdir)}'")
|
|
1142
|
-
return
|
|
1231
|
+
return ''
|
|
1143
1232
|
self.error(f"Didn't understand +incdir+: '{plusarg}'",
|
|
1144
1233
|
error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
|
|
1145
|
-
return
|
|
1234
|
+
return ''
|
|
1146
1235
|
|
|
1147
1236
|
# remaining plusargs as stored in self.args['unprocessed-plusargs'] (list)
|
|
1148
1237
|
if plusarg.startswith('+'):
|
|
1149
1238
|
if not self.config.get('bare_plusarg_supported', False):
|
|
1150
1239
|
self.error(f"bare plusarg(s) are not supported: {plusarg}'")
|
|
1151
|
-
return
|
|
1240
|
+
return ''
|
|
1152
1241
|
if plusarg not in self.args['unprocessed-plusargs']:
|
|
1153
1242
|
self.args['unprocessed-plusargs'].append(plusarg)
|
|
1154
1243
|
# For anything added to unprocessed-plusarg, we have to return it, to let
|
|
@@ -1157,7 +1246,7 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1157
1246
|
|
|
1158
1247
|
self.error(f"Didn't understand +plusarg: '{plusarg}'",
|
|
1159
1248
|
error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
|
|
1160
|
-
return
|
|
1249
|
+
return ''
|
|
1161
1250
|
|
|
1162
1251
|
|
|
1163
1252
|
def append_shell_commands(self, cmds : list) -> None:
|
|
@@ -1439,8 +1528,8 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1439
1528
|
unparsed = Command.process_tokens(self, tokens, process_all=False, pwd=pwd)
|
|
1440
1529
|
util.debug(f'CommandDesign - after Command.process_tokens(..) {unparsed=}')
|
|
1441
1530
|
|
|
1442
|
-
# deal with +define
|
|
1443
|
-
# walk the list, remove all items after we're done.
|
|
1531
|
+
# deal with +define+, +incdir+, +(plusargName)+, or -GParameterName=Value:
|
|
1532
|
+
# consume it and remove from unparsed, walk the list, remove all items after we're done.
|
|
1444
1533
|
remove_list = []
|
|
1445
1534
|
for token in unparsed:
|
|
1446
1535
|
# Since this is a raw argparser, we may have args that come from shlex.quote(token),
|
|
@@ -1454,6 +1543,15 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1454
1543
|
plusarg = strip_outer_quotes(token)
|
|
1455
1544
|
self.process_plusarg(plusarg, pwd=pwd)
|
|
1456
1545
|
remove_list.append(token)
|
|
1546
|
+
continue
|
|
1547
|
+
|
|
1548
|
+
# Parameters in -G<word>=<something>
|
|
1549
|
+
m = re.match(r"^\'?\-G\w+\=.+", token)
|
|
1550
|
+
if m:
|
|
1551
|
+
# Copy and strip all outer ' or " on the text:
|
|
1552
|
+
param = strip_outer_quotes(token)
|
|
1553
|
+
self.process_parameter_arg(param, pwd=pwd)
|
|
1554
|
+
remove_list.append(token)
|
|
1457
1555
|
|
|
1458
1556
|
for x in remove_list:
|
|
1459
1557
|
unparsed.remove(x)
|
opencos/eda_config.py
CHANGED
|
@@ -47,7 +47,7 @@ class Defaults:
|
|
|
47
47
|
'exe', 'handlers',
|
|
48
48
|
'requires_env', 'requires_py', 'requires_cmd', 'requires_in_exe_path',
|
|
49
49
|
'requires_vsim_helper', 'requires_vscode_extension',
|
|
50
|
-
'disable-tools-multi',
|
|
50
|
+
'disable-tools-multi', 'disable-auto',
|
|
51
51
|
])
|
|
52
52
|
supported_config_tool_keys = set([
|
|
53
53
|
'defines',
|
|
@@ -158,6 +158,9 @@ def update_config_auto_tool_order_for_tool(tool: str, config: dict) -> str:
|
|
|
158
158
|
|
|
159
159
|
user_exe = shutil.which(user_exe)
|
|
160
160
|
|
|
161
|
+
# try adding to $PATH if in form --tool=/path/to/exe
|
|
162
|
+
tool_try_add_to_path(tool)
|
|
163
|
+
|
|
161
164
|
if tool not in config['auto_tools_order'][0]:
|
|
162
165
|
return tool
|
|
163
166
|
if not user_exe:
|
|
@@ -305,7 +308,8 @@ def write_eda_config_and_args(
|
|
|
305
308
|
fullpath = os.path.join(dirpath, filename)
|
|
306
309
|
data = {}
|
|
307
310
|
for x in ['command_name', 'config', 'target', 'args', 'modified_args', 'defines',
|
|
308
|
-
'incdirs', 'files_v', 'files_sv', 'files_vhd'
|
|
311
|
+
'incdirs', 'files_v', 'files_sv', 'files_vhd', 'files_cpp', 'files_sdc',
|
|
312
|
+
'files_non_source']:
|
|
309
313
|
# Use deep copy b/c otherwise these are references to opencos.eda.
|
|
310
314
|
data[x] = copy.deepcopy(getattr(command_obj_ref, x, ''))
|
|
311
315
|
|
|
@@ -321,3 +325,34 @@ def write_eda_config_and_args(
|
|
|
321
325
|
data['config'][k] = str(v)
|
|
322
326
|
|
|
323
327
|
yaml_safe_writer(data=data, filepath=fullpath)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def tool_try_add_to_path(tool: str) -> None:
|
|
331
|
+
'''Since we support --tool=<name>=/path/to/bin/exe, attempt to prepend $PATH
|
|
332
|
+
|
|
333
|
+
with this information for this tool (which will nicely affect all subprocesses,
|
|
334
|
+
but not wreck our original shell).'''
|
|
335
|
+
|
|
336
|
+
if not tool or '=' not in tool:
|
|
337
|
+
return
|
|
338
|
+
|
|
339
|
+
name, exe = tool.split('=')
|
|
340
|
+
if os.path.isdir(name):
|
|
341
|
+
# Someone passes us --tool=<name>=/path/to/bin/ (did not have exe)
|
|
342
|
+
path = name
|
|
343
|
+
else:
|
|
344
|
+
# Someone passes us --tool=<name>=/path/to/bin/exe, remove the exe.
|
|
345
|
+
path, _ = os.path.split(exe)
|
|
346
|
+
if not path:
|
|
347
|
+
return
|
|
348
|
+
|
|
349
|
+
path = os.path.abspath(path)
|
|
350
|
+
if os.path.isdir(path):
|
|
351
|
+
paths = os.environ['PATH'].split(':')
|
|
352
|
+
if path not in paths:
|
|
353
|
+
os.environ['PATH'] = path + ':' + os.environ['PATH']
|
|
354
|
+
util.info(f'--tool={tool} has path information, prepending PATH with: {path}')
|
|
355
|
+
else:
|
|
356
|
+
util.info(f'--tool={tool} has path information, but {path} already in $PATH')
|
|
357
|
+
if exe and os.path.isfile(exe):
|
|
358
|
+
util.info(f'--tool={tool} has path information, using exe {shutil.which(exe)}')
|
opencos/eda_config_defaults.yml
CHANGED
|
@@ -516,6 +516,7 @@ auto_tools_order:
|
|
|
516
516
|
sim: opencos.tools.iverilog.CommandSimIverilog
|
|
517
517
|
|
|
518
518
|
cocotb:
|
|
519
|
+
disable-auto: True # do not allow this to run `eda sim` with --tool not set
|
|
519
520
|
exe: python
|
|
520
521
|
requires_cmd:
|
|
521
522
|
- python -c "import cocotb; print(cocotb.__version__)"
|
opencos/export_helper.py
CHANGED
|
@@ -286,7 +286,11 @@ def get_list_sv_included_files(
|
|
|
286
286
|
# If we have more than N levels of `include hunting, then rethink this.
|
|
287
287
|
# For example, some codebases would do their file dependencies as `include
|
|
288
288
|
# as part of their header guards, which could be ~100 levels of nesting.
|
|
289
|
-
|
|
289
|
+
|
|
290
|
+
# make a copy of keys so we don't alter during traversal of the dict:
|
|
291
|
+
fnames = list(sv_included_files_dict.keys())
|
|
292
|
+
for fname in fnames:
|
|
293
|
+
traversed = sv_included_files_dict[fname]
|
|
290
294
|
if not traversed:
|
|
291
295
|
included_files_list = find_sv_included_files_within_file(
|
|
292
296
|
filename=fname,
|
|
@@ -387,6 +391,13 @@ class ExportHelper:
|
|
|
387
391
|
paths.
|
|
388
392
|
'''
|
|
389
393
|
|
|
394
|
+
# We'll copy files_sv and files_v later, along with discovered included files,
|
|
395
|
+
# need to copy any others:
|
|
396
|
+
remaing_files_to_cp = []
|
|
397
|
+
for x in self.cmd_design_obj.files.keys():
|
|
398
|
+
if x not in self.cmd_design_obj.files_v + self.cmd_design_obj.files_sv:
|
|
399
|
+
remaing_files_to_cp.append(x)
|
|
400
|
+
|
|
390
401
|
# Also sets our list of included files.
|
|
391
402
|
self.included_files = get_list_sv_included_files(
|
|
392
403
|
all_src_files=self.cmd_design_obj.files_sv + self.cmd_design_obj.files_v,
|
|
@@ -398,7 +409,8 @@ class ExportHelper:
|
|
|
398
409
|
|
|
399
410
|
info(f"export_helper: {self.target=} included files {self.included_files=}")
|
|
400
411
|
|
|
401
|
-
|
|
412
|
+
|
|
413
|
+
for filename in remaing_files_to_cp:
|
|
402
414
|
dst = os.path.join(self.out_dir, os.path.split(filename)[1])
|
|
403
415
|
if not os.path.exists(dst):
|
|
404
416
|
shutil.copy(src=filename, dst=dst)
|
|
@@ -415,11 +427,13 @@ class ExportHelper:
|
|
|
415
427
|
|
|
416
428
|
info(f'export_helper: Creating DEPS.yml for {self.target=} in {self.out_dir=}')
|
|
417
429
|
|
|
418
|
-
# Need to strip path information from our files_sv and files_v
|
|
430
|
+
# Need to strip path information from our files_sv and files_v
|
|
431
|
+
# (and all source files: cpp, sdc, etc; but skip the non-source files):
|
|
419
432
|
deps_files = []
|
|
420
|
-
for fullpath in self.cmd_design_obj.
|
|
421
|
-
|
|
422
|
-
|
|
433
|
+
for fullpath in self.cmd_design_obj.files.keys():
|
|
434
|
+
if fullpath not in self.cmd_design_obj.files_non_source:
|
|
435
|
+
filename = os.path.split(fullpath)[1]
|
|
436
|
+
deps_files.append(filename)
|
|
423
437
|
|
|
424
438
|
|
|
425
439
|
data = {
|
opencos/hw/oc_cli.py
CHANGED
|
@@ -2508,7 +2508,7 @@ name_tables = { 'OC_VENDOR' : {},
|
|
|
2508
2508
|
}
|
|
2509
2509
|
|
|
2510
2510
|
def parse_names():
|
|
2511
|
-
name_tables.update(opencos_names.
|
|
2511
|
+
name_tables.update(opencos_names.NAMES) #update our global name_table from names.py
|
|
2512
2512
|
|
|
2513
2513
|
def lookup_table_string(table, index):
|
|
2514
2514
|
if isinstance(table, str) and table in name_tables:
|
opencos/names.py
CHANGED
|
@@ -13,11 +13,12 @@ just needs to set a couple of these statically.
|
|
|
13
13
|
all values in this file are in hex.
|
|
14
14
|
'''
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
NAMES = {
|
|
17
17
|
|
|
18
18
|
'OC_VENDOR': {
|
|
19
19
|
0: "None",
|
|
20
20
|
1: "Xilinx",
|
|
21
|
+
2: "Altera",
|
|
21
22
|
},
|
|
22
23
|
|
|
23
24
|
'OC_BOARD': {
|
|
@@ -28,11 +29,13 @@ table = {
|
|
|
28
29
|
4: "U55N",
|
|
29
30
|
5: "U50C",
|
|
30
31
|
6: "PYNQ-Z2",
|
|
32
|
+
7: "AG3C_DEVKIT",
|
|
31
33
|
},
|
|
32
34
|
|
|
33
35
|
'OC_LIBRARY': {
|
|
34
36
|
0: "None",
|
|
35
37
|
1: "Ultrascale+",
|
|
38
|
+
2: "Agilex3"
|
|
36
39
|
},
|
|
37
40
|
|
|
38
41
|
'PLL_TYPES': {
|