opencos-eda 0.2.42__tar.gz → 0.2.44__tar.gz
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_eda-0.2.42/opencos_eda.egg-info → opencos_eda-0.2.44}/PKG-INFO +1 -1
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/commands/export.py +0 -1
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/commands/flist.py +4 -1
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/deps_helpers.py +2 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/eda.py +4 -3
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/eda_base.py +54 -34
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/eda_config.py +1 -1
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/eda_config_defaults.yml +6 -2
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/export_helper.py +6 -4
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/files.py +1 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tests/helpers.py +2 -2
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tests/test_eda_synth.py +63 -2
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tools/iverilog.py +1 -1
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tools/riviera.py +1 -1
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tools/slang.py +1 -1
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tools/surelog.py +1 -1
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tools/verilator.py +5 -1
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tools/vivado.py +22 -4
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tools/yosys.py +32 -13
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/util.py +37 -6
- {opencos_eda-0.2.42 → opencos_eda-0.2.44/opencos_eda.egg-info}/PKG-INFO +1 -1
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/pyproject.toml +1 -1
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/LICENSE +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/LICENSE.spdx +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/README.md +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/__init__.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/_version.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/_waves_pkg.sv +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/commands/__init__.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/commands/build.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/commands/elab.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/commands/multi.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/commands/open.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/commands/proj.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/commands/sim.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/commands/sweep.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/commands/synth.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/commands/targets.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/commands/upload.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/commands/waves.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/deps_schema.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/eda_config_max_verilator_waivers.yml +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/eda_config_reduced.yml +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/eda_deps_bash_completion.bash +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/eda_extract_targets.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/eda_tool_helper.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/export_json_convert.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/names.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/oc_cli.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/pcie.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/peakrdl_cleanup.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/seed.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tests/__init__.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tests/custom_config.yml +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tests/deps_files/command_order/DEPS.yml +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tests/deps_files/error_msgs/DEPS.yml +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tests/deps_files/iverilog_test/DEPS.yml +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tests/deps_files/no_deps_here/DEPS.yml +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tests/deps_files/non_sv_reqs/DEPS.yml +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tests/deps_files/tags_with_tools/DEPS.yml +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tests/deps_files/test_err_fatal/DEPS.yml +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tests/test_build.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tests/test_deps_helpers.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tests/test_deps_schema.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tests/test_eda.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tests/test_eda_elab.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tests/test_oc_cli.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tests/test_tools.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tools/__init__.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tools/invio.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tools/invio_helpers.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tools/invio_yosys.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tools/modelsim_ase.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tools/questa.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tools/slang_yosys.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos/tools/tabbycad_yosys.py +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos_eda.egg-info/SOURCES.txt +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos_eda.egg-info/dependency_links.txt +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos_eda.egg-info/entry_points.txt +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos_eda.egg-info/requires.txt +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/opencos_eda.egg-info/top_level.txt +0 -0
- {opencos_eda-0.2.42 → opencos_eda-0.2.44}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opencos-eda
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.44
|
|
4
4
|
Summary: A simple Python package for wrapping RTL simuliatons and synthesis
|
|
5
5
|
Author-email: Simon Sabato <simon@cognichip.ai>, Drew Ranck <drew@cognichip.ai>
|
|
6
6
|
Project-URL: Homepage, https://github.com/cognichip/opencos
|
|
@@ -37,7 +37,6 @@ class CommandExport(CommandDesign):
|
|
|
37
37
|
self, tokens: list, process_all: bool = True, pwd: str = os.getcwd()
|
|
38
38
|
) -> list:
|
|
39
39
|
|
|
40
|
-
self.defines['OC_EXPORT'] = None
|
|
41
40
|
unparsed = CommandDesign.process_tokens(
|
|
42
41
|
self, tokens=tokens, process_all=process_all, pwd=pwd
|
|
43
42
|
)
|
|
@@ -47,11 +47,14 @@ class CommandFList(CommandDesign):
|
|
|
47
47
|
'no-quote-path' : False,
|
|
48
48
|
'build-script' : "", # we don't want this to error either
|
|
49
49
|
|
|
50
|
-
'print-to-stdout': False,
|
|
50
|
+
'print-to-stdout': False,
|
|
51
51
|
|
|
52
52
|
# ex: eda flist --print-to-stdout --emit-rel-path --quiet <target>
|
|
53
53
|
'emit-rel-path' : False,
|
|
54
54
|
})
|
|
55
|
+
self.args_help.update({
|
|
56
|
+
'print-to-stdout': "do not save file, print to stdout",
|
|
57
|
+
})
|
|
55
58
|
|
|
56
59
|
def process_tokens(
|
|
57
60
|
self, tokens: list , process_all: bool = True, pwd: str = os.getcwd()
|
|
@@ -320,9 +320,10 @@ def process_tokens(tokens: list, original_args: list, config: dict, interactive=
|
|
|
320
320
|
for value in unparsed:
|
|
321
321
|
if value in config['DEFAULT_HANDLERS'].keys():
|
|
322
322
|
command = value
|
|
323
|
-
if value in config['
|
|
323
|
+
if not parsed.tool and value in config['command_tool_is_optional']:
|
|
324
|
+
# only do this if --tool was not set.
|
|
324
325
|
run_auto_tool_setup = False
|
|
325
|
-
unparsed.remove(value)
|
|
326
|
+
unparsed.remove(value) # remove command (flist, export, targets, etc)
|
|
326
327
|
break
|
|
327
328
|
|
|
328
329
|
if not interactive:
|
|
@@ -361,7 +362,7 @@ def process_tokens(tokens: list, original_args: list, config: dict, interactive=
|
|
|
361
362
|
util.debug(f'{type(sco)=}')
|
|
362
363
|
if not parsed.tool and \
|
|
363
364
|
command not in config.get('command_determines_tool', []) and \
|
|
364
|
-
command not in config.get('
|
|
365
|
+
command not in config.get('command_tool_is_optional', []):
|
|
365
366
|
use_tool = which_tool(command, config)
|
|
366
367
|
util.info(f"--tool not specified, using default for {command=}: {use_tool}")
|
|
367
368
|
|
|
@@ -686,6 +686,7 @@ class CommandDesign(Command):
|
|
|
686
686
|
self.files_sv = []
|
|
687
687
|
self.files_vhd = []
|
|
688
688
|
self.files_cpp = []
|
|
689
|
+
self.files_sdc = []
|
|
689
690
|
self.files_non_source = []
|
|
690
691
|
self.files_caller_info = {}
|
|
691
692
|
self.dep_shell_commands = [] # each list entry is a {}
|
|
@@ -696,6 +697,7 @@ class CommandDesign(Command):
|
|
|
696
697
|
|
|
697
698
|
self.cached_deps = {} # key = abspath of DEPS markup file, has sub-dicts for 'data' and 'line_numbers'.
|
|
698
699
|
self.targets_dict = {} # key = targets that we've already processed in DEPS files
|
|
700
|
+
self.last_added_source_file_inferred_top = ''
|
|
699
701
|
|
|
700
702
|
def run_dep_commands(self):
|
|
701
703
|
# Run any shell@ commands from DEPS files
|
|
@@ -759,7 +761,8 @@ class CommandDesign(Command):
|
|
|
759
761
|
self.files.pop(key)
|
|
760
762
|
self.files[new_key] = True
|
|
761
763
|
|
|
762
|
-
my_file_lists_list = [self.files_v, self.files_sv, self.files_vhd, self.files_cpp
|
|
764
|
+
my_file_lists_list = [self.files_v, self.files_sv, self.files_vhd, self.files_cpp,
|
|
765
|
+
self.files_sdc]
|
|
763
766
|
for my_file_list in my_file_lists_list:
|
|
764
767
|
for iter,value in enumerate(my_file_list):
|
|
765
768
|
if value and type(value) is str and value.startswith(self._work_dir_add_srcs_path_string):
|
|
@@ -1028,6 +1031,7 @@ class CommandDesign(Command):
|
|
|
1028
1031
|
sv_file_ext_list = known_file_ext_dict.get('systemverilog', [])
|
|
1029
1032
|
vhdl_file_ext_list = known_file_ext_dict.get('vhdl', [])
|
|
1030
1033
|
cpp_file_ext_list = known_file_ext_dict.get('cpp', [])
|
|
1034
|
+
sdc_file_ext_list = known_file_ext_dict.get('synth_constraints', [])
|
|
1031
1035
|
|
|
1032
1036
|
if forced_extension:
|
|
1033
1037
|
# If forced_extension='systemverilog', then use the first known extension for
|
|
@@ -1036,6 +1040,11 @@ class CommandDesign(Command):
|
|
|
1036
1040
|
file_ext = known_file_ext_dict.get(forced_extension, [''])[0]
|
|
1037
1041
|
util.debug(f"{forced_extension=} for {filename=} as type '{file_ext}'")
|
|
1038
1042
|
|
|
1043
|
+
|
|
1044
|
+
if not add_to_non_sources and \
|
|
1045
|
+
file_ext in known_file_ext_dict.get('inferred_top', []):
|
|
1046
|
+
self.last_added_source_file_inferred_top = file_abspath
|
|
1047
|
+
|
|
1039
1048
|
if add_to_non_sources:
|
|
1040
1049
|
self.files_non_source.append(file_abspath)
|
|
1041
1050
|
util.debug("Added non-source file file %s as %s" % (filename, file_abspath))
|
|
@@ -1051,6 +1060,9 @@ class CommandDesign(Command):
|
|
|
1051
1060
|
elif file_ext in cpp_file_ext_list:
|
|
1052
1061
|
self.files_cpp.append(file_abspath)
|
|
1053
1062
|
util.debug("Added C++ file %s as %s" % (filename, file_abspath))
|
|
1063
|
+
elif file_ext in sdc_file_ext_list:
|
|
1064
|
+
self.files_sdc.append(file_abspath)
|
|
1065
|
+
util.debug("Added SDC file %s as %s" % (filename, file_abspath))
|
|
1054
1066
|
else:
|
|
1055
1067
|
# unknown file extension. In these cases we link the file to the working directory
|
|
1056
1068
|
# so it is available (for example, a .mem file that is expected to exist with relative path)
|
|
@@ -1114,8 +1126,8 @@ class CommandDesign(Command):
|
|
|
1114
1126
|
|
|
1115
1127
|
# by this point hopefully this is a target ... is it a simple filename?
|
|
1116
1128
|
remove_list = []
|
|
1117
|
-
|
|
1118
|
-
|
|
1129
|
+
last_potential_top_file = ('', '') # (top, fpath)
|
|
1130
|
+
last_potential_top_target = ('', '') # (top, path/to/full-target-name)
|
|
1119
1131
|
last_potential_top_isfile = False
|
|
1120
1132
|
caller_info = ''
|
|
1121
1133
|
for token in unparsed:
|
|
@@ -1131,21 +1143,13 @@ class CommandDesign(Command):
|
|
|
1131
1143
|
else:
|
|
1132
1144
|
self.add_file(filename=fpath, caller_info=caller_info,
|
|
1133
1145
|
forced_extension=forced_extension)
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
if not self.args['top'] and \
|
|
1142
|
-
file_ext and \
|
|
1143
|
-
file_ext in known_file_ext_dict.get('inferred_top', []):
|
|
1144
|
-
# if we haven't yet been given a top, or inferred one, we take the last one we get
|
|
1145
|
-
# from a raw list of RTL file names (from args or command line tokens)
|
|
1146
|
-
last_potential_top_path = file_abspath
|
|
1147
|
-
last_potential_top = self.get_top_name(file_abspath)
|
|
1148
|
-
last_potential_top_isfile = True
|
|
1146
|
+
if not self.args['top']:
|
|
1147
|
+
known_file_ext_dict = self.config.get('file_extensions', {})
|
|
1148
|
+
if forced_extension:
|
|
1149
|
+
file_ext = known_file_ext_dict.get(forced_extension, [''])[0]
|
|
1150
|
+
if file_ext in known_file_ext_dict.get('inferred_top', []):
|
|
1151
|
+
# last cmd line arg was a filename that could have inferred top.
|
|
1152
|
+
last_potential_top_isfile = True
|
|
1149
1153
|
|
|
1150
1154
|
remove_list.append(token)
|
|
1151
1155
|
continue # done with token, consume it, we added the file.
|
|
@@ -1158,12 +1162,9 @@ class CommandDesign(Command):
|
|
|
1158
1162
|
|
|
1159
1163
|
util.debug(f'Calling self.resolve_target on {target_name=}')
|
|
1160
1164
|
if self.resolve_target(target_name, caller_info=caller_info):
|
|
1161
|
-
if self.args['top']
|
|
1162
|
-
#
|
|
1163
|
-
|
|
1164
|
-
# from a target name
|
|
1165
|
-
last_potential_top = self.get_top_name(target_name)
|
|
1166
|
-
last_potential_top_path = target_name
|
|
1165
|
+
if not self.args['top']:
|
|
1166
|
+
# last cmd line arg was a target that we'll likely use for inferred top.
|
|
1167
|
+
last_potential_top_target = (self.get_top_name(target_name), target_name)
|
|
1167
1168
|
last_potential_top_isfile = False
|
|
1168
1169
|
|
|
1169
1170
|
remove_list.append(token)
|
|
@@ -1176,16 +1177,35 @@ class CommandDesign(Command):
|
|
|
1176
1177
|
if process_all and len(unparsed) > 0:
|
|
1177
1178
|
self.error(f"Didn't understand command remaining tokens {unparsed=} in CommandDesign")
|
|
1178
1179
|
|
|
1179
|
-
# handle a missing self.args['top'] with last filepath
|
|
1180
|
-
if self.args.get('top', '')
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1180
|
+
# handle a missing self.args['top'] with last filepath or last target:
|
|
1181
|
+
if not self.args.get('top', ''):
|
|
1182
|
+
if not last_potential_top_isfile and last_potential_top_target[0]:
|
|
1183
|
+
# If we have a target name from DEPS, prefer to use that.
|
|
1184
|
+
self.args['top'], self.args['top-path'] = last_potential_top_target
|
|
1185
|
+
util.info("--top not specified, inferred from target:",
|
|
1186
|
+
f"{self.args['top']} ({self.args['top-path']})")
|
|
1187
|
+
|
|
1188
|
+
else:
|
|
1189
|
+
best_top_fname = self.last_added_source_file_inferred_top
|
|
1190
|
+
if best_top_fname:
|
|
1191
|
+
last_potential_top_file = (self.get_top_name(best_top_fname), best_top_fname)
|
|
1192
|
+
|
|
1193
|
+
if not self.args['top'] and last_potential_top_file[0]:
|
|
1194
|
+
# If we don't have a target name, and no top name yet, then go looking for the
|
|
1195
|
+
# module name in the final source file added.
|
|
1196
|
+
self.args['top-path'] = last_potential_top_file[1] # from tuple: (top, fpath)
|
|
1197
|
+
self.args['top'] = util.get_inferred_top_module_name(
|
|
1198
|
+
module_guess=last_potential_top_file[0],
|
|
1199
|
+
module_fpath=last_potential_top_file[1]
|
|
1200
|
+
)
|
|
1201
|
+
if self.args['top']:
|
|
1202
|
+
util.info("--top not specified, inferred from final source file:",
|
|
1203
|
+
f"{self.args['top']} ({self.args['top-path']})")
|
|
1204
|
+
# If top wasn't set, and we're using the final command-line 'arg' filename
|
|
1205
|
+
# (not from DEPS.yml) we need to override self.target if that was set. Otherwise
|
|
1206
|
+
# it won't save to the correct work-dir:
|
|
1207
|
+
self.target = self.args['top']
|
|
1208
|
+
|
|
1189
1209
|
if self.error_on_missing_top and not self.args.get('top', ''):
|
|
1190
1210
|
self.error(f"Did not get a --top or DEPS top, required to run command",
|
|
1191
1211
|
f"'{self.command_name}' for tool={self.args.get('tool', None)}")
|
|
@@ -60,6 +60,9 @@ file_extensions:
|
|
|
60
60
|
- .vhdl
|
|
61
61
|
cpp:
|
|
62
62
|
- .cpp
|
|
63
|
+
synth_constraints:
|
|
64
|
+
- .sdc
|
|
65
|
+
- .xdc
|
|
63
66
|
|
|
64
67
|
inferred_top:
|
|
65
68
|
# file extensions that we can infer "top" module from, if --top omitted.
|
|
@@ -72,8 +75,9 @@ command_determines_tool:
|
|
|
72
75
|
# eda commands that will self-determine the tool to use
|
|
73
76
|
- waves
|
|
74
77
|
|
|
75
|
-
|
|
76
|
-
# eda commands that
|
|
78
|
+
command_tool_is_optional:
|
|
79
|
+
# eda commands that may not need to use a tool at all, will skip auto_tools_order if --tool=None (default)
|
|
80
|
+
- flist
|
|
77
81
|
- export
|
|
78
82
|
- targets
|
|
79
83
|
|
|
@@ -352,23 +352,25 @@ class ExportHelper:
|
|
|
352
352
|
}
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
-
|
|
355
|
+
|
|
356
|
+
if deps_file_args:
|
|
356
357
|
data[self.target]['args'] = deps_file_args.copy()
|
|
357
358
|
|
|
358
359
|
if self.args.get('top', None):
|
|
359
360
|
data[self.target]['top'] = self.args['top']
|
|
360
361
|
|
|
361
|
-
if
|
|
362
|
+
if self.cmd_design_obj.defines:
|
|
362
363
|
data[self.target]['defines'] = self.cmd_design_obj.defines.copy()
|
|
363
364
|
for define in _remove_DEPS_yml_defines:
|
|
364
365
|
# Remove defines keys for OC_ROOT and OC_SEED. Change OC_SEED to _ORIG_OC_SEED
|
|
365
366
|
if define in data[self.target]['defines']:
|
|
366
367
|
data[self.target]['defines'].pop(define)
|
|
367
368
|
|
|
368
|
-
|
|
369
|
+
reqs_fullpath_list = self.included_files + self.cmd_design_obj.files_non_source
|
|
370
|
+
if reqs_fullpath_list:
|
|
369
371
|
# Need to strip path information from non-source files:
|
|
370
372
|
data[self.target]['reqs'] = list()
|
|
371
|
-
for fullpath in
|
|
373
|
+
for fullpath in reqs_fullpath_list:
|
|
372
374
|
filename = os.path.split(fullpath)[1]
|
|
373
375
|
data[self.target]['reqs'].append(filename)
|
|
374
376
|
|
|
@@ -19,7 +19,7 @@ def can_run_eda_command(*commands, config: dict) -> bool:
|
|
|
19
19
|
if not handler:
|
|
20
20
|
return False
|
|
21
21
|
if handler and getattr(handler, 'CHECK_REQUIRES', []):
|
|
22
|
-
if not all(
|
|
22
|
+
if not all(issubclass(handler, x) for x in getattr(handler, 'CHECK_REQUIRES', [])):
|
|
23
23
|
return False
|
|
24
24
|
runnable.append(True)
|
|
25
25
|
return runnable and all(runnable)
|
|
@@ -128,7 +128,7 @@ class Helpers:
|
|
|
128
128
|
'''Changes directory to self.DEFAULT_DIR and removes eda.work, eda.export paths'''
|
|
129
129
|
chdir_remove_work_dir('', self.DEFAULT_DIR)
|
|
130
130
|
|
|
131
|
-
def log_it(self, command_str:str, logfile=None, use_eda_wrap=True):
|
|
131
|
+
def log_it(self, command_str:str, logfile=None, use_eda_wrap=True) -> int:
|
|
132
132
|
'''Replacement for calling eda.main or eda_wrap, when you want an internal logfile
|
|
133
133
|
|
|
134
134
|
Usage:
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
'''pytests for: eda [multi|tools-multi] synth [args] <target(s)>'''
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
+
import shutil
|
|
5
|
+
|
|
4
6
|
import pytest
|
|
5
7
|
|
|
6
8
|
from opencos import eda, eda_tool_helper
|
|
7
9
|
from opencos.tests import helpers
|
|
10
|
+
from opencos.tests.helpers import Helpers
|
|
8
11
|
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
THISPATH = os.path.dirname(__file__)
|
|
11
14
|
|
|
12
15
|
def chdir_remove_work_dir(relpath):
|
|
13
16
|
'''Changes dir to relpath, removes the work directories (eda.work, eda.export*)'''
|
|
14
|
-
return helpers.chdir_remove_work_dir(
|
|
17
|
+
return helpers.chdir_remove_work_dir(THISPATH, relpath)
|
|
15
18
|
|
|
16
19
|
# Figure out what tools the system has available, without calling eda.main(..)
|
|
17
20
|
config, tools_loaded = eda_tool_helper.get_config_and_tools_loaded()
|
|
@@ -75,3 +78,61 @@ class Tests:
|
|
|
75
78
|
rc = eda.main(*cmdlist)
|
|
76
79
|
print(f'{rc=}')
|
|
77
80
|
assert rc == 0
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def vivado_has_xpms() -> bool:
|
|
84
|
+
'''Returns True if Vivado is installed and has visibility to XPMs'''
|
|
85
|
+
if 'vivado' not in tools_loaded:
|
|
86
|
+
return False
|
|
87
|
+
vivado_exe = shutil.which('vivado')
|
|
88
|
+
vivado_bin_path, _ = os.path.split(vivado_exe)
|
|
89
|
+
vivado_base_path, _ = os.path.split(vivado_bin_path) # strip bin/vivado
|
|
90
|
+
|
|
91
|
+
return os.path.exists(os.path.join(vivado_base_path, 'data', 'ip', 'xpm'))
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@pytest.mark.skipif(
|
|
95
|
+
'slang_yosys' not in tools_loaded, reason="requires slang_yosys for synth"
|
|
96
|
+
)
|
|
97
|
+
class TestsSlangYosys(Helpers):
|
|
98
|
+
'''Tests that require tool=slang_yosys to be available'''
|
|
99
|
+
|
|
100
|
+
def test_sdc_file(self):
|
|
101
|
+
'''Test for 'eda synth' on oclib_fifo_with_sdc
|
|
102
|
+
|
|
103
|
+
This does not use the actual .sdc file, but it also shouldn't fail with
|
|
104
|
+
that file in the 'deps' list (CommandDesign should track it correctly)
|
|
105
|
+
'''
|
|
106
|
+
chdir_remove_work_dir('deps_files/test_sdc_files')
|
|
107
|
+
cmd_str = 'synth --tool=slang_yosys oclib_fifo_with_sdc'
|
|
108
|
+
rc = self.log_it(cmd_str, use_eda_wrap=False)
|
|
109
|
+
assert rc == 0
|
|
110
|
+
|
|
111
|
+
@pytest.mark.skipif('vivado' not in tools_loaded, reason="requires vivado")
|
|
112
|
+
@pytest.mark.skipif(not vivado_has_xpms(), reason="requires install to have XPMs")
|
|
113
|
+
class TestsVivado(Helpers):
|
|
114
|
+
'''Tests that require tool=vivado with XPMs available for synthesis'''
|
|
115
|
+
|
|
116
|
+
def test_sdc_file(self):
|
|
117
|
+
'''Test for 'eda synth' on oclib_fifo_with_sdc
|
|
118
|
+
|
|
119
|
+
And check that the .sdc file was used and not the default generated .xdc
|
|
120
|
+
file
|
|
121
|
+
'''
|
|
122
|
+
chdir_remove_work_dir('deps_files/test_sdc_files')
|
|
123
|
+
cmd_str = 'synth --tool=vivado oclib_fifo_with_sdc'
|
|
124
|
+
rc = self.log_it(cmd_str, use_eda_wrap=False)
|
|
125
|
+
assert rc == 0
|
|
126
|
+
|
|
127
|
+
# apparently this doesn't get saved in the eda.log. That's not great,
|
|
128
|
+
# but we can check the <target>.synth.log file.
|
|
129
|
+
synth_log = os.path.join(
|
|
130
|
+
THISPATH, 'deps_files', 'test_sdc_files',
|
|
131
|
+
'eda.work', 'oclib_fifo_with_sdc.synth', 'oclib_fifo.synth.log'
|
|
132
|
+
)
|
|
133
|
+
sdc_lines = self.get_log_lines_with('.sdc', logfile=synth_log)
|
|
134
|
+
assert sdc_lines
|
|
135
|
+
for sdc_line in sdc_lines:
|
|
136
|
+
assert 'oclib_fifo_vivado.sdc' in sdc_line
|
|
137
|
+
assert 'test_sdc_files' in sdc_line
|
|
138
|
+
assert 'eda.work' not in sdc_line
|
|
@@ -37,7 +37,7 @@ class ToolIverilog(Tool):
|
|
|
37
37
|
iverilog_version_ret = subprocess.run(
|
|
38
38
|
[self.iverilog_exe, '-v'], capture_output=True, check=False
|
|
39
39
|
)
|
|
40
|
-
lines = iverilog_version_ret.stdout.decode("utf-8").split('\n')
|
|
40
|
+
lines = iverilog_version_ret.stdout.decode("utf-8", errors="replace").split('\n')
|
|
41
41
|
words = lines[0].split() # 'Icarus Verilog version 13.0 (devel) (s20221226-568-g62727e8b2)'
|
|
42
42
|
version = words[3]
|
|
43
43
|
util.debug(f'{iverilog_path=} {lines[0]=}')
|
|
@@ -36,7 +36,7 @@ class ToolRiviera(ToolModelsimAse):
|
|
|
36
36
|
capture_output=True,
|
|
37
37
|
check=False
|
|
38
38
|
)
|
|
39
|
-
stdout = version_ret.stdout.decode('utf-8').rstrip()
|
|
39
|
+
stdout = version_ret.stdout.decode('utf-8', errors='replace').rstrip()
|
|
40
40
|
|
|
41
41
|
# Expect:
|
|
42
42
|
# Aldec, Inc. Riviera-PRO version 2025.04.139.9738 built for Linux64 on May 30, 2025
|
|
@@ -44,7 +44,7 @@ class ToolSlang(Tool):
|
|
|
44
44
|
capture_output=True,
|
|
45
45
|
check=False
|
|
46
46
|
)
|
|
47
|
-
stdout = version_ret.stdout.decode('utf-8')
|
|
47
|
+
stdout = version_ret.stdout.decode('utf-8', errors='replace')
|
|
48
48
|
util.debug(f'{path=} {version_ret=}')
|
|
49
49
|
words = stdout.split() # slang version 8.0.6+b4a74b00
|
|
50
50
|
if len(words) < 3:
|
|
@@ -33,7 +33,7 @@ class ToolSurelog(Tool):
|
|
|
33
33
|
version_ret = subprocess.run(
|
|
34
34
|
[self.surelog_exe, '--version'], capture_output=True, check=False
|
|
35
35
|
)
|
|
36
|
-
stdout = version_ret.stdout.decode('utf-8')
|
|
36
|
+
stdout = version_ret.stdout.decode('utf-8', errors='replace')
|
|
37
37
|
util.debug(f'{path=} {version_ret=}')
|
|
38
38
|
words = stdout.split() # VERSION: 1.84 (first line)
|
|
39
39
|
if len(words) < 2:
|
|
@@ -49,7 +49,7 @@ class ToolVerilator(Tool):
|
|
|
49
49
|
capture_output=True,
|
|
50
50
|
check=False
|
|
51
51
|
)
|
|
52
|
-
stdout = version_ret.stdout.decode('utf-8')
|
|
52
|
+
stdout = version_ret.stdout.decode('utf-8', errors='replace')
|
|
53
53
|
util.debug(f'{path=} {version_ret=}')
|
|
54
54
|
words = stdout.split() # 'Verilator 5.027 devel rev v5.026-92-g403a197e2
|
|
55
55
|
if len(words) < 1:
|
|
@@ -166,6 +166,10 @@ class VerilatorSim(CommandSim, ToolVerilator):
|
|
|
166
166
|
# don't run this if we're stopping before/after compile/elab
|
|
167
167
|
return
|
|
168
168
|
|
|
169
|
+
if not os.path.isfile(os.path.join(self.args['work-dir'], 'obj_dir', 'sim.exe')):
|
|
170
|
+
self.error('Verilated executable obj_dir/sim.exe does not exist')
|
|
171
|
+
return
|
|
172
|
+
|
|
169
173
|
# Note that this is not returning a pass/fail bash return code,
|
|
170
174
|
# so we will likely have to log-scrape to deterimine pass/fail.
|
|
171
175
|
# Also, if we ran in cc-mode, we will not get the "R e p o r t: Verilator"
|
|
@@ -70,7 +70,7 @@ class ToolVivado(Tool):
|
|
|
70
70
|
#try:
|
|
71
71
|
# # Get version from vivado -version, or xsim --version:
|
|
72
72
|
# vivado_ret = subprocess.run(['vivado', '-version'], capture_output=True)
|
|
73
|
-
# lines = vivado_ret.stdout.decode('utf-8').split('\n')
|
|
73
|
+
# lines = vivado_ret.stdout.decode('utf-8', errors='replace').split('\n')
|
|
74
74
|
# words = lines[0].split() # vivado v2024.2.1 (64-bit)
|
|
75
75
|
# version = words[1][1:] # 2024.2.1
|
|
76
76
|
# self._VERSION = version
|
|
@@ -388,9 +388,14 @@ class CommandSynthVivado(CommandSynth, ToolVivado):
|
|
|
388
388
|
self.exec(self.args['work-dir'], command_list)
|
|
389
389
|
|
|
390
390
|
|
|
391
|
-
def write_tcl_file(
|
|
391
|
+
def write_tcl_file( # pylint: disable=too-many-locals,too-many-branches
|
|
392
|
+
self, tcl_file: str
|
|
393
|
+
) -> None:
|
|
392
394
|
'''Writes synthesis capable Vivado tcl file to filepath 'tcl_file'.'''
|
|
393
395
|
|
|
396
|
+
# TODO(drew): This method needs to be broken up to avoid the pylint
|
|
397
|
+
# waivers.
|
|
398
|
+
|
|
394
399
|
v = self.get_vivado_tcl_verbose_arg()
|
|
395
400
|
|
|
396
401
|
defines = ""
|
|
@@ -416,9 +421,12 @@ class CommandSynthVivado(CommandSynth, ToolVivado):
|
|
|
416
421
|
part = self.args['part']
|
|
417
422
|
top = self.args['top']
|
|
418
423
|
|
|
424
|
+
default_xdc = False
|
|
419
425
|
if self.args['xdc'] != "":
|
|
420
|
-
default_xdc = False
|
|
421
426
|
xdc_file = os.path.abspath(self.args['xdc'])
|
|
427
|
+
elif self.files_sdc:
|
|
428
|
+
# Use files from DEPS target or command line.
|
|
429
|
+
xdc_file = ''
|
|
422
430
|
else:
|
|
423
431
|
default_xdc = True
|
|
424
432
|
xdc_file = os.path.abspath(os.path.join(self.args['work-dir'],
|
|
@@ -427,7 +435,17 @@ class CommandSynthVivado(CommandSynth, ToolVivado):
|
|
|
427
435
|
|
|
428
436
|
tcl_lines += [
|
|
429
437
|
f"create_fileset -constrset constraints_1 {v}",
|
|
430
|
-
|
|
438
|
+
]
|
|
439
|
+
for _file in self.files_sdc:
|
|
440
|
+
# NOTE - sdc files cannot (yet) be attached to other modules.
|
|
441
|
+
tcl_lines += [
|
|
442
|
+
f"add_files -fileset constraints_1 {_file} {v}",
|
|
443
|
+
]
|
|
444
|
+
if xdc_file:
|
|
445
|
+
tcl_lines += [
|
|
446
|
+
f"add_files -fileset constraints_1 {xdc_file} {v}",
|
|
447
|
+
]
|
|
448
|
+
tcl_lines += [
|
|
431
449
|
"# FIRST PASS -- auto_detect_xpm",
|
|
432
450
|
"synth_design -rtl -rtl_skip_ip -rtl_skip_constraints -no_timing_driven -no_iobuf " \
|
|
433
451
|
+ f"-top {top} {incdirs} {defines} {v}",
|
|
@@ -46,7 +46,7 @@ class ToolYosys(Tool):
|
|
|
46
46
|
[self.sta_exe, '-version'], capture_output=True, check=False
|
|
47
47
|
)
|
|
48
48
|
util.debug(f'{self.yosys_exe} {sta_version_ret=}')
|
|
49
|
-
sta_ver = sta_version_ret.stdout.decode('utf-8').split()[0]
|
|
49
|
+
sta_ver = sta_version_ret.stdout.decode('utf-8', errors='replace').split()[0]
|
|
50
50
|
if sta_ver:
|
|
51
51
|
self.sta_version = sta_ver
|
|
52
52
|
|
|
@@ -56,7 +56,7 @@ class ToolYosys(Tool):
|
|
|
56
56
|
util.debug(f'{self.yosys_exe} {version_ret=}')
|
|
57
57
|
|
|
58
58
|
# Yosys 0.48 (git sha1 aaa534749, clang++ 14.0.0-1ubuntu1.1 -fPIC -O3)
|
|
59
|
-
words = version_ret.stdout.decode('utf-8').split()
|
|
59
|
+
words = version_ret.stdout.decode('utf-8', errors='replace').split()
|
|
60
60
|
|
|
61
61
|
if len(words) < 2:
|
|
62
62
|
self.error(f'{self.yosys_exe} --version: returned unexpected str {version_ret=}')
|
|
@@ -93,16 +93,27 @@ class CommonSynthYosys(CommandSynth, ToolYosys):
|
|
|
93
93
|
'yosys-blackbox': [], # list of modules that yosys will blackbox.
|
|
94
94
|
})
|
|
95
95
|
self.args_help.update({
|
|
96
|
-
'sta':
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
'sta': (
|
|
97
|
+
'After running Yosys, run "sta" with --liberty-file.'
|
|
98
|
+
' sta can be installed via: https://github.com/The-OpenROAD-Project/OpenSTA'
|
|
99
|
+
),
|
|
100
|
+
'sdc-file': (
|
|
101
|
+
'.sdc file to use with --sta, if not present will use auto constraints.'
|
|
102
|
+
' Note you can have .sdc files in "deps" of DEPS.yml targets.'
|
|
103
|
+
),
|
|
104
|
+
'liberty-file': (
|
|
105
|
+
'Single liberty file for synthesis and sta,'
|
|
106
|
+
' for example: github/OpenSTA/examples/nangate45_slow.lib.gz'
|
|
107
|
+
),
|
|
101
108
|
'yosys-synth': 'The synth command provided to Yosys, see: yosys help.',
|
|
102
|
-
'yosys-pre-synth':
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
109
|
+
'yosys-pre-synth': (
|
|
110
|
+
'Yosys commands performed prior to running "synth"'
|
|
111
|
+
' (or eda arg value for --yosys-synth)'
|
|
112
|
+
),
|
|
113
|
+
'yosys-blackbox': (
|
|
114
|
+
'List of modules that yosys will blackbox, likely will need these'
|
|
115
|
+
' in Verilog-2001 for yosys to read outside of slang and synth'
|
|
116
|
+
),
|
|
106
117
|
})
|
|
107
118
|
|
|
108
119
|
self.yosys_out_dir = ''
|
|
@@ -193,6 +204,9 @@ class CommonSynthYosys(CommandSynth, ToolYosys):
|
|
|
193
204
|
# Need to create sta.f:
|
|
194
205
|
if self.args['sdc-file']:
|
|
195
206
|
sdc_path = self.args['sdc-file']
|
|
207
|
+
elif self.files_sdc:
|
|
208
|
+
# Use files from DEPS target or command line.
|
|
209
|
+
sdc_path = ''
|
|
196
210
|
else:
|
|
197
211
|
# Need to create sdc.f:
|
|
198
212
|
sdc_path = 'sdc.f'
|
|
@@ -204,9 +218,14 @@ class CommonSynthYosys(CommandSynth, ToolYosys):
|
|
|
204
218
|
'read_liberty ' + self.args['liberty-file'],
|
|
205
219
|
'read_verilog ' + self.yosys_v_path,
|
|
206
220
|
'link_design ' + self.args['top'],
|
|
207
|
-
'read_sdc ' + sdc_path,
|
|
208
|
-
'report_checks',
|
|
209
221
|
]
|
|
222
|
+
for _file in self.files_sdc:
|
|
223
|
+
lines.append('read_sdc ' + _file)
|
|
224
|
+
if sdc_path:
|
|
225
|
+
lines.append('read_sdc ' + sdc_path)
|
|
226
|
+
|
|
227
|
+
lines.append('report_checks')
|
|
228
|
+
|
|
210
229
|
f.write('\n'.join(lines))
|
|
211
230
|
|
|
212
231
|
return util.ShellCommandList(
|
|
@@ -33,11 +33,10 @@ args = {
|
|
|
33
33
|
'errors' : 0,
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
def strip_all_quotes(s:str):
|
|
36
|
+
def strip_all_quotes(s: str) -> str:
|
|
38
37
|
return s.replace("'", '').replace('"', '')
|
|
39
38
|
|
|
40
|
-
def strip_outer_quotes(s:str):
|
|
39
|
+
def strip_outer_quotes(s: str) -> str:
|
|
41
40
|
ret = str(s)
|
|
42
41
|
while (ret.startswith("'") and ret.endswith("'")) or \
|
|
43
42
|
(ret.startswith('"') and ret.endswith('"')):
|
|
@@ -635,6 +634,38 @@ def write_eda_config_and_args(dirpath : str, filename='eda_output_config.yml', c
|
|
|
635
634
|
yaml_safe_writer(data=data, filepath=fullpath)
|
|
636
635
|
|
|
637
636
|
|
|
637
|
+
def get_inferred_top_module_name(module_guess: str, module_fpath: str) -> str:
|
|
638
|
+
'''Returns the best guess as the 'top' module name name, given a fpath where
|
|
639
|
+
|
|
640
|
+
module_fpath = /some/path/to/module_guess.[v|sv]
|
|
641
|
+
|
|
642
|
+
Use the module_guess if it exists in the file as: module <module_guess>
|
|
643
|
+
Otherwise use the last observed: module <best_guess>
|
|
644
|
+
Otherwise use blank str
|
|
645
|
+
'''
|
|
646
|
+
|
|
647
|
+
best_guess = ''
|
|
648
|
+
if not os.path.isfile(module_fpath):
|
|
649
|
+
return ''
|
|
650
|
+
with open(module_fpath, encoding='utf-8') as f:
|
|
651
|
+
for line in f.readlines():
|
|
652
|
+
line = line.strip()
|
|
653
|
+
if line.startswith('module '):
|
|
654
|
+
parts = line.split()
|
|
655
|
+
module_name = parts[1]
|
|
656
|
+
rstrip_nonword_pattern = r'\W+$'
|
|
657
|
+
module_name = re.sub(rstrip_nonword_pattern, '', module_name)
|
|
658
|
+
if bool(re.fullmatch(r'^\w+$', module_name)):
|
|
659
|
+
if module_name == module_guess:
|
|
660
|
+
return module_guess
|
|
661
|
+
elif module_name:
|
|
662
|
+
best_guess = module_name
|
|
663
|
+
if best_guess:
|
|
664
|
+
return best_guess
|
|
665
|
+
else:
|
|
666
|
+
return ''
|
|
667
|
+
|
|
668
|
+
|
|
638
669
|
def subprocess_run(work_dir, command_list, fake:bool=False, shell=False) -> int:
|
|
639
670
|
''' Run command_list in the foreground, with preference to use bash if shell=True.'''
|
|
640
671
|
|
|
@@ -700,7 +731,7 @@ def subprocess_run_background(work_dir, command_list, background=True, fake:bool
|
|
|
700
731
|
stderr = ''
|
|
701
732
|
with open(tee_fpath, 'w') as f:
|
|
702
733
|
for line in iter(proc.stdout.readline, b''):
|
|
703
|
-
line = line.rstrip().decode("utf-8")
|
|
734
|
+
line = line.rstrip().decode("utf-8", errors="replace")
|
|
704
735
|
if not background:
|
|
705
736
|
print(line)
|
|
706
737
|
f.write(line + '\n')
|
|
@@ -717,8 +748,8 @@ def subprocess_run_background(work_dir, command_list, background=True, fake:bool
|
|
|
717
748
|
stdout, stderr = proc.communicate()
|
|
718
749
|
rc = proc.returncode
|
|
719
750
|
|
|
720
|
-
stdout = stdout.decode('utf-8') if stdout else ""
|
|
721
|
-
stderr = stderr.decode('utf-8') if stderr else ""
|
|
751
|
+
stdout = stdout.decode('utf-8', errors="replace") if stdout else ""
|
|
752
|
+
stderr = stderr.decode('utf-8', errors="replace") if stderr else ""
|
|
722
753
|
debug(f"shell_run_background: {rc=}")
|
|
723
754
|
if stdout:
|
|
724
755
|
for lineno, line in enumerate(stdout.strip().split('\n')):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opencos-eda
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.44
|
|
4
4
|
Summary: A simple Python package for wrapping RTL simuliatons and synthesis
|
|
5
5
|
Author-email: Simon Sabato <simon@cognichip.ai>, Drew Ranck <drew@cognichip.ai>
|
|
6
6
|
Project-URL: Homepage, https://github.com/cognichip/opencos
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|