opencos-eda 0.2.49__tar.gz → 0.2.50__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.49/opencos_eda.egg-info → opencos_eda-0.2.50}/PKG-INFO +1 -1
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/multi.py +1 -1
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/sim.py +5 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/eda_base.py +15 -2
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/eda_config.py +5 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/eda_config_defaults.yml +36 -4
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/test_eda_elab.py +2 -1
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/test_tools.py +1 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/modelsim_ase.py +22 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/questa.py +5 -3
- opencos_eda-0.2.50/opencos/tools/questa_fse.py +57 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/slang.py +8 -2
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/verilator.py +25 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/vivado.py +33 -26
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/util.py +158 -6
- {opencos_eda-0.2.49 → opencos_eda-0.2.50/opencos_eda.egg-info}/PKG-INFO +1 -1
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos_eda.egg-info/SOURCES.txt +1 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/pyproject.toml +1 -1
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/LICENSE +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/LICENSE.spdx +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/README.md +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/__init__.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/_version.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/_waves_pkg.sv +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/__init__.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/build.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/elab.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/export.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/flist.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/lec.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/open.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/proj.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/shell.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/sweep.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/synth.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/targets.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/upload.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/waves.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/deps/__init__.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/deps/defaults.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/deps/deps_commands.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/deps/deps_file.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/deps/deps_processor.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/deps_schema.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/eda.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/eda_config_max_verilator_waivers.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/eda_config_reduced.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/eda_deps_bash_completion.bash +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/eda_extract_targets.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/eda_tool_helper.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/export_helper.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/export_json_convert.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/files.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/hw/__init__.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/hw/oc_cli.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/hw/pcie.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/names.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/peakrdl_cleanup.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/seed.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/__init__.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/custom_config.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/deps_files/command_order/DEPS.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/deps_files/error_msgs/DEPS.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/deps_files/iverilog_test/DEPS.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/deps_files/no_deps_here/DEPS.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/deps_files/non_sv_reqs/DEPS.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/deps_files/tags_with_tools/DEPS.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/deps_files/test_err_fatal/DEPS.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/helpers.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/test_build.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/test_deps_helpers.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/test_deps_schema.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/test_eda.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/test_eda_synth.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/test_oc_cli.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/__init__.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/invio.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/invio_helpers.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/invio_yosys.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/iverilog.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/riviera.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/slang_yosys.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/surelog.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/tabbycad_yosys.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/yosys.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/utils/__init__.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/utils/markup_helpers.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/utils/str_helpers.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/utils/subprocess_helpers.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos_eda.egg-info/dependency_links.txt +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos_eda.egg-info/entry_points.txt +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos_eda.egg-info/requires.txt +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos_eda.egg-info/top_level.txt +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.50}/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.50
|
|
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
|
|
@@ -590,7 +590,7 @@ class CommandToolsMulti(CommandMulti):
|
|
|
590
590
|
def multi_which_tools(self, command):
|
|
591
591
|
'''Overrides CommandMulti.multi_which_tool(command), return a list of all
|
|
592
592
|
possible tools that can run this command'''
|
|
593
|
-
if self.tools is None or
|
|
593
|
+
if self.tools is None or not self.tools:
|
|
594
594
|
# wasn't set via arg --tools, so use all if possible for this command.
|
|
595
595
|
which_tools = self.all_handler_commands.get(command, [])
|
|
596
596
|
else:
|
|
@@ -177,6 +177,11 @@ class CommandSim(CommandDesign):
|
|
|
177
177
|
filename=log_fname, bad_strings=bad_strings, must_strings=must_strings,
|
|
178
178
|
use_bad_strings=use_bad_strings, use_must_strings=use_must_strings
|
|
179
179
|
)
|
|
180
|
+
if log_fname:
|
|
181
|
+
self.artifacts_add(
|
|
182
|
+
name=os.path.join(self.args['work-dir'], log_fname),
|
|
183
|
+
typ='text', description='Simulator stdout/stderr log file'
|
|
184
|
+
)
|
|
180
185
|
|
|
181
186
|
def do_export(self) -> None:
|
|
182
187
|
'''CommandSim helper for handling args --export*
|
|
@@ -257,10 +257,15 @@ class Command:
|
|
|
257
257
|
if self.args['work-dir']:
|
|
258
258
|
if not self.errors_log_f:
|
|
259
259
|
try:
|
|
260
|
+
fullpath = os.path.join(self.args['work-dir'], 'eda.errors.log')
|
|
260
261
|
self.errors_log_f = open( # pylint: disable=consider-using-with
|
|
261
|
-
|
|
262
|
-
encoding='utf-8'
|
|
262
|
+
fullpath, 'w', encoding='utf-8'
|
|
263
263
|
)
|
|
264
|
+
util.artifacts.add(
|
|
265
|
+
name=fullpath,
|
|
266
|
+
typ='text', description='EDA reported errors'
|
|
267
|
+
)
|
|
268
|
+
|
|
264
269
|
except FileNotFoundError:
|
|
265
270
|
pass
|
|
266
271
|
if self.errors_log_f:
|
|
@@ -374,8 +379,16 @@ class Command:
|
|
|
374
379
|
if self.args['keep']:
|
|
375
380
|
open(keep_file, 'w', encoding='utf-8').close() # pylint: disable=consider-using-with
|
|
376
381
|
util.debug(f'create_work_dir: created {keep_file=}')
|
|
382
|
+
|
|
383
|
+
# Set the util.artifacts path with our work-dir:
|
|
384
|
+
util.artifacts.set_artifacts_json_dir(self.args['work-dir'])
|
|
385
|
+
|
|
377
386
|
return self.args['work-dir']
|
|
378
387
|
|
|
388
|
+
def artifacts_add(self, name: str, typ: str, description: str) -> None:
|
|
389
|
+
'''Adds a file to util.artifacts, derived classes may override'''
|
|
390
|
+
util.artifacts.add(name=name, typ=typ, description=description)
|
|
391
|
+
|
|
379
392
|
|
|
380
393
|
def exec(self, work_dir: str, command_list: list, background: bool = False,
|
|
381
394
|
stop_on_error: bool = True, quiet: bool = False, tee_fpath: str = '',
|
|
@@ -308,6 +308,11 @@ def write_eda_config_and_args(
|
|
|
308
308
|
# Use deep copy b/c otherwise these are references to opencos.eda.
|
|
309
309
|
data[x] = copy.deepcopy(getattr(command_obj_ref, x, ''))
|
|
310
310
|
|
|
311
|
+
# copy util.args
|
|
312
|
+
data['util'] = {
|
|
313
|
+
'args': util.args
|
|
314
|
+
}
|
|
315
|
+
|
|
311
316
|
# fix some burried class references in command_obj_ref.config,
|
|
312
317
|
# otherwise we won't be able to safe load this yaml, so cast as str repr.
|
|
313
318
|
for k, v in getattr(command_obj_ref, 'config', {}).items():
|
|
@@ -230,17 +230,39 @@ tools:
|
|
|
230
230
|
compile-args: |
|
|
231
231
|
-sv -svinputport=net -lint
|
|
232
232
|
compile-waivers:
|
|
233
|
-
- 2275 # 2275 - Existing package '
|
|
234
|
-
- 2555 # 2555 - assignment to input port
|
|
233
|
+
- 2275 # 2275 - Existing package 'myname_pkg' will be overwritten.
|
|
234
|
+
- 2555 # 2555 - assignment to input port myname
|
|
235
235
|
- 2583 # 2583 - [SVCHK] - Extra checking for conflicts with always_comb and
|
|
236
236
|
# always_latch variables is done at vopt time.
|
|
237
237
|
simulate-waivers:
|
|
238
|
-
- 3009 # 3009: [TSCALE] - Module '
|
|
238
|
+
- 3009 # 3009: [TSCALE] - Module 'myname' does not have a timeunit/timeprecision
|
|
239
239
|
# specification in effect, but other modules do.
|
|
240
240
|
simulate-waves-args: |
|
|
241
241
|
+acc
|
|
242
242
|
|
|
243
243
|
|
|
244
|
+
questa_fse:
|
|
245
|
+
defines:
|
|
246
|
+
OC_TOOL_QUESTA_FSE: 1
|
|
247
|
+
log-bad-strings:
|
|
248
|
+
- "Error:"
|
|
249
|
+
log-must-strings:
|
|
250
|
+
- " vsim "
|
|
251
|
+
- "Errors: 0"
|
|
252
|
+
compile-args: |
|
|
253
|
+
-sv -svinputport=net -lint
|
|
254
|
+
compile-waivers:
|
|
255
|
+
- 2275 # 2275 - Existing package 'myname_pkg' will be overwritten.
|
|
256
|
+
- 2555 # 2555 - assignment to input port myname
|
|
257
|
+
- 2583 # 2583 - [SVCHK] - Extra checking for conflicts with always_comb and
|
|
258
|
+
# always_latch variables is done at vopt time.
|
|
259
|
+
simulate-waivers:
|
|
260
|
+
- 3009 # 3009: [TSCALE] - Module 'myname' does not have a timeunit/timeprecision
|
|
261
|
+
# specification in effect, but other modules do.
|
|
262
|
+
simulate-waves-args: |
|
|
263
|
+
-voptargs=+acc=bcgnprst
|
|
264
|
+
|
|
265
|
+
|
|
244
266
|
iverilog:
|
|
245
267
|
log-bad-strings:
|
|
246
268
|
- "Error:"
|
|
@@ -303,7 +325,7 @@ auto_tools_order:
|
|
|
303
325
|
# TODO(drew): surelog is disabled from `eda tools-multi`. It is still
|
|
304
326
|
# enabled for `eda [multi] elab --tool surelog`. It does not support
|
|
305
327
|
# type comparisons:
|
|
306
|
-
# if type(
|
|
328
|
+
# if type(myname) == type(othername)
|
|
307
329
|
# [ERR:UH0700] ... Unsupported expression
|
|
308
330
|
# modelsim_ase also doesn't, but it won't fail elab, whereas surelog does.
|
|
309
331
|
surelog:
|
|
@@ -404,6 +426,16 @@ auto_tools_order:
|
|
|
404
426
|
elab: opencos.tools.modelsim_ase.CommandElabModelsimAse
|
|
405
427
|
sim: opencos.tools.modelsim_ase.CommandSimModelsimAse
|
|
406
428
|
|
|
429
|
+
questa_fse: # free student edition, works similar to modelsim_ase
|
|
430
|
+
exe: vsim
|
|
431
|
+
requires_cmd:
|
|
432
|
+
- vsim -version
|
|
433
|
+
requires_in_exe_path:
|
|
434
|
+
- questa_fse
|
|
435
|
+
handlers:
|
|
436
|
+
elab: opencos.tools.questa_fse.CommandElabQuestaFse
|
|
437
|
+
sim: opencos.tools.questa_fse.CommandSimQuestaFse
|
|
438
|
+
|
|
407
439
|
iverilog:
|
|
408
440
|
exe: iverilog
|
|
409
441
|
handlers:
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
''' opencos.tools.modelsim_ase - Used by opencos.eda for sim/elab commands w/ --tool=modelsim_ase.
|
|
2
2
|
|
|
3
3
|
Contains classes for ToolModelsimAse, CommandSimModelsimAse, CommandElabModelsimAse.
|
|
4
|
+
|
|
5
|
+
Note that this is for 32-bit Modelsim Student Edition. Consider using --tool=questa_fse instead.
|
|
4
6
|
'''
|
|
5
7
|
|
|
6
8
|
# pylint: disable=R0801 # (duplicate code in derived classes, such as if-condition return.)
|
|
@@ -151,6 +153,9 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
|
|
|
151
153
|
]):
|
|
152
154
|
vlog_dot_f_lines += ['-suppress', str(waiver)]
|
|
153
155
|
|
|
156
|
+
if self.args['gui'] or self.args['waves']:
|
|
157
|
+
vlog_dot_f_lines += self.tool_config.get('compile-waves-args', '').split()
|
|
158
|
+
|
|
154
159
|
vlog_dot_f_fname = filename
|
|
155
160
|
vlog_dot_f_fpath = os.path.join(self.args['work-dir'], vlog_dot_f_fname)
|
|
156
161
|
|
|
@@ -203,6 +208,10 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
|
|
|
203
208
|
voptargs_str = ""
|
|
204
209
|
if self.args['gui'] or self.args['waves']:
|
|
205
210
|
voptargs_str = self.tool_config.get('simulate-waves-args', '+acc')
|
|
211
|
+
util.artifacts.add_extension(
|
|
212
|
+
search_paths=self.args['work-dir'], file_extension='wlf',
|
|
213
|
+
typ='waveform', description='Modelsim/Questa Waveform WLF (Wave Log Format) file'
|
|
214
|
+
)
|
|
206
215
|
|
|
207
216
|
# TODO(drew): support self.args['sim_libary', 'elab-args', sim-args'] (3 lists)
|
|
208
217
|
# to add to vsim_one_liner.
|
|
@@ -331,6 +340,19 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
|
|
|
331
340
|
return ' '.join(vsim_suppress_list)
|
|
332
341
|
|
|
333
342
|
|
|
343
|
+
def artifacts_add(self, name: str, typ: str, description: str) -> None:
|
|
344
|
+
'''Override from Command.artifacts_add, so we can catch known file
|
|
345
|
+
|
|
346
|
+
names to make their typ/description better, such as CommandSim using
|
|
347
|
+
sim.log
|
|
348
|
+
'''
|
|
349
|
+
_, leafname = os.path.split(name)
|
|
350
|
+
if leafname == 'sim.log':
|
|
351
|
+
description = 'Modelsim/Questa Transcript log file'
|
|
352
|
+
|
|
353
|
+
super().artifacts_add(name=name, typ=typ, description=description)
|
|
354
|
+
|
|
355
|
+
|
|
334
356
|
class CommandElabModelsimAse(CommandSimModelsimAse):
|
|
335
357
|
'''CommandElabModelsimAse is a command handler for: eda elab --tool=modelsim_ase'''
|
|
336
358
|
|
|
@@ -40,9 +40,11 @@ class ToolQuesta(Tool):
|
|
|
40
40
|
self.error(f"{self._EXE} not in path, need to setup",
|
|
41
41
|
"(i.e. source /opt/intelFPGA_pro/23.4/settings64.sh")
|
|
42
42
|
util.debug(f"{path=}")
|
|
43
|
-
if self._EXE.endswith('qrun') and
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
if self._EXE.endswith('qrun') and \
|
|
44
|
+
any(x in path for x in ('modelsim_ase', 'questa_fse')):
|
|
45
|
+
util.warning(f"{self._EXE=} Questa path is for starter edition",
|
|
46
|
+
"(modelsim_ase, questa_fse), consider using --tool=modelsim_ase",
|
|
47
|
+
"or --tool=questa_fse")
|
|
46
48
|
else:
|
|
47
49
|
self.sim_exe = path
|
|
48
50
|
self.sim_exe_base_path, _ = os.path.split(path)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
''' opencos.tools.questa_fse - Used by opencos.eda for sim/elab commands w/ --tool=questa_fse.
|
|
2
|
+
|
|
3
|
+
Contains classes for CommandSimQuestaFse, CommandElabQuestaFse.
|
|
4
|
+
'''
|
|
5
|
+
|
|
6
|
+
# pylint: disable=R0801 # (duplicate code in derived classes, such as if-condition return.)
|
|
7
|
+
# pylint: disable=too-many-ancestors
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
from opencos.tools.modelsim_ase import CommandSimModelsimAse
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CommandSimQuestaFse(CommandSimModelsimAse):
|
|
15
|
+
'''CommandSimQuestaFse is a command handler for: eda sim --tool=questa_fse
|
|
16
|
+
|
|
17
|
+
Note this inherits 99% from CommandSimModelSimAse for command handling
|
|
18
|
+
'''
|
|
19
|
+
_TOOL = 'questa_fse'
|
|
20
|
+
_EXE = 'vsim'
|
|
21
|
+
|
|
22
|
+
def __init__(self, config: dict):
|
|
23
|
+
# this will setup with self._TOOL = modelsim_ase, which is not ideal so
|
|
24
|
+
# we have to repait it later.
|
|
25
|
+
CommandSimModelsimAse.__init__(self, config=config)
|
|
26
|
+
|
|
27
|
+
# repairs: override self._TOOL, and run get_versions() again.
|
|
28
|
+
self._TOOL = 'questa_fse'
|
|
29
|
+
|
|
30
|
+
self.shell_command = os.path.join(self.sim_exe_base_path, 'vsim')
|
|
31
|
+
self.starter_edition = True
|
|
32
|
+
self.args.update({
|
|
33
|
+
'tool': self._TOOL, # override
|
|
34
|
+
'gui': False,
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def set_tool_defines(self):
|
|
39
|
+
'''Override from questa.ToolQuesta'''
|
|
40
|
+
# Update any defines from config.tools.questa_fse:
|
|
41
|
+
self.defines.update(
|
|
42
|
+
self.tool_config.get(
|
|
43
|
+
'defines',
|
|
44
|
+
# defaults, if not set:
|
|
45
|
+
{
|
|
46
|
+
'OC_TOOL_QUESTA_FSE': 1
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class CommandElabQuestaFse(CommandSimQuestaFse):
|
|
53
|
+
'''CommandElabQuestaFse is a command handler for: eda elab --tool=questa_fse'''
|
|
54
|
+
|
|
55
|
+
def __init__(self, config:dict):
|
|
56
|
+
super().__init__(config)
|
|
57
|
+
self.args['stop-after-elaborate'] = True
|
|
@@ -191,14 +191,20 @@ class CommandElabSlang(CommandElab, ToolSlang):
|
|
|
191
191
|
|
|
192
192
|
return command_list
|
|
193
193
|
|
|
194
|
-
def _get_slang_json_args(self, command_exe:str) -> list:
|
|
194
|
+
def _get_slang_json_args(self, command_exe: str) -> list:
|
|
195
195
|
command_list = []
|
|
196
196
|
|
|
197
|
-
|
|
197
|
+
_, command_exe_leaf = os.path.split(command_exe)
|
|
198
|
+
if self.args.get('slang-json', False) and command_exe_leaf == 'slang':
|
|
198
199
|
for arg in self.all_json_args:
|
|
199
200
|
if arg not in command_list:
|
|
200
201
|
command_list.append(arg)
|
|
201
202
|
if arg == '--ast-json': # needs filename
|
|
202
203
|
command_list.append('slang.json')
|
|
204
|
+
util.artifacts.add(
|
|
205
|
+
name=os.path.join(self.args['work-dir'], 'slang.json'),
|
|
206
|
+
typ='json',
|
|
207
|
+
description='Abstract syntax tree from slang --ast-json'
|
|
208
|
+
)
|
|
203
209
|
|
|
204
210
|
return command_list
|
|
@@ -346,6 +346,16 @@ class VerilatorSim(CommandSim, ToolVerilator):
|
|
|
346
346
|
util.info(f'--waves arg present, no $dumpfile found, adding SV file: {file_to_add}')
|
|
347
347
|
self.add_file(file_to_add)
|
|
348
348
|
|
|
349
|
+
# register .vcd or .fst artifacts:
|
|
350
|
+
util.artifacts.add_extension(
|
|
351
|
+
search_paths=self.args['work-dir'], file_extension='fst',
|
|
352
|
+
typ='waveform', description='Simulation Waveform FST (Fast Signal Trace) file'
|
|
353
|
+
)
|
|
354
|
+
util.artifacts.add_extension(
|
|
355
|
+
search_paths=self.args['work-dir'], file_extension='vcd',
|
|
356
|
+
typ='waveform', description='Simulation Waveform VCD (Value Change Dump) file'
|
|
357
|
+
)
|
|
358
|
+
|
|
349
359
|
|
|
350
360
|
def _get_start_verilator_command_list(self, lint_only: bool = False) -> list:
|
|
351
361
|
|
|
@@ -447,6 +457,21 @@ class VerilatorSim(CommandSim, ToolVerilator):
|
|
|
447
457
|
return verilate_args
|
|
448
458
|
|
|
449
459
|
|
|
460
|
+
def artifacts_add(self, name: str, typ: str, description: str) -> None:
|
|
461
|
+
'''Override from Command.artifacts_add, so we can catch known file
|
|
462
|
+
|
|
463
|
+
names to make their typ/description better, such as CommandSim using
|
|
464
|
+
sim.log or compile.log
|
|
465
|
+
'''
|
|
466
|
+
_, leafname = os.path.split(name)
|
|
467
|
+
if leafname == 'sim.log':
|
|
468
|
+
description = 'Verilated executable log from stdout/stderr'
|
|
469
|
+
elif leafname == 'compile.log':
|
|
470
|
+
description = 'Verilator compile step log from verilator call'
|
|
471
|
+
|
|
472
|
+
super().artifacts_add(name=name, typ=typ, description=description)
|
|
473
|
+
|
|
474
|
+
|
|
450
475
|
class VerilatorElab(VerilatorSim):
|
|
451
476
|
'''VerilatorElab is a command handler for: eda elab --tool=verilator'''
|
|
452
477
|
|
|
@@ -63,29 +63,14 @@ class ToolVivado(Tool):
|
|
|
63
63
|
util.info("environment for XILINX_VIVADO is not set or doesn't match the vivado path:",
|
|
64
64
|
f"XILINX_VIVADO={xilinx_vivado} EXE PATH={self.vivado_exe}")
|
|
65
65
|
|
|
66
|
-
version
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
# words = lines[0].split() # vivado v2024.2.1 (64-bit)
|
|
75
|
-
# version = words[1][1:] # 2024.2.1
|
|
76
|
-
# self._VERSION = version
|
|
77
|
-
#except:
|
|
78
|
-
# pass
|
|
79
|
-
|
|
80
|
-
if not version:
|
|
81
|
-
# Get version based on install path name:
|
|
82
|
-
util.debug(f"vivado path = {self.vivado_exe}")
|
|
83
|
-
m = re.search(r'(\d\d\d\d)\.(\d)', self.vivado_exe)
|
|
84
|
-
if m:
|
|
85
|
-
version = m.group(1) + '.' + m.group(2)
|
|
86
|
-
self._VERSION = version
|
|
87
|
-
else:
|
|
88
|
-
self.error("Vivado path doesn't specificy version, expecting (dddd.d)")
|
|
66
|
+
# Get version based on install path name. Calling vivado -verison is too slow.
|
|
67
|
+
util.debug(f"vivado path = {self.vivado_exe}")
|
|
68
|
+
m = re.search(r'(\d\d\d\d)\.(\d)', self.vivado_exe)
|
|
69
|
+
if m:
|
|
70
|
+
version = m.group(1) + '.' + m.group(2)
|
|
71
|
+
self._VERSION = version
|
|
72
|
+
else:
|
|
73
|
+
self.error("Vivado path doesn't specificy version, expecting (dddd.d)")
|
|
89
74
|
|
|
90
75
|
if version:
|
|
91
76
|
numbers_list = version.split('.')
|
|
@@ -260,6 +245,14 @@ class CommandSimVivado(CommandSim, ToolVivado):
|
|
|
260
245
|
def get_simulate_command_lists(self, **kwargs) -> list:
|
|
261
246
|
# create TCL
|
|
262
247
|
tcl_name = os.path.abspath(os.path.join(self.args['work-dir'], self.args['tcl-file']))
|
|
248
|
+
|
|
249
|
+
if self.args['waves']:
|
|
250
|
+
util.artifacts.add_extension(
|
|
251
|
+
search_paths=self.args['work-dir'], file_extension='wdb',
|
|
252
|
+
typ='waveform', description='Vivado XSim Waveform WDB (Wave DataBase) file'
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
|
|
263
256
|
with open( tcl_name, 'w', encoding='utf-8' ) as fo:
|
|
264
257
|
if self.args['waves']:
|
|
265
258
|
if self.args['waves-start']:
|
|
@@ -345,6 +338,23 @@ class CommandSimVivado(CommandSim, ToolVivado):
|
|
|
345
338
|
self.files_v.insert(0, glbl_v)
|
|
346
339
|
|
|
347
340
|
|
|
341
|
+
def artifacts_add(self, name: str, typ: str, description: str) -> None:
|
|
342
|
+
'''Override from Command.artifacts_add, so we can catch known file
|
|
343
|
+
|
|
344
|
+
names to make their typ/description better, such as CommandSim using
|
|
345
|
+
sim.log or compile.log
|
|
346
|
+
'''
|
|
347
|
+
_, leafname = os.path.split(name)
|
|
348
|
+
if leafname == 'xsim.log':
|
|
349
|
+
description = 'Vivado XSim simulation step (3/3) log from stdout/stderr'
|
|
350
|
+
elif leafname == 'xelab.log':
|
|
351
|
+
description = 'Vivado XSim elaboration step (2/3) log from stdout/stderr'
|
|
352
|
+
elif leafname == 'xvlog.log':
|
|
353
|
+
description = 'Vivado XSim compile step (1/3) log from stdout/stderr'
|
|
354
|
+
|
|
355
|
+
super().artifacts_add(name=name, typ=typ, description=description)
|
|
356
|
+
|
|
357
|
+
|
|
348
358
|
class CommandElabVivado(CommandSimVivado):
|
|
349
359
|
'''CommandElabVivado is a command handler for: eda elab --tool=vivado, uses xvlog, xelab'''
|
|
350
360
|
def __init__(self, config: dict):
|
|
@@ -933,9 +943,6 @@ class CommandUploadVivado(CommandUpload, ToolVivado):
|
|
|
933
943
|
util.info("Upload done")
|
|
934
944
|
|
|
935
945
|
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
946
|
class CommandOpenVivado(CommandOpen, ToolVivado):
|
|
940
947
|
'''CommandOpenVivado command handler class used by: eda open --tool vivado'''
|
|
941
948
|
def __init__(self, config: dict):
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
'''opencos.util -- support global logging, argparser for printing (colors)'''
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import argparse
|
|
4
|
+
import atexit
|
|
5
5
|
import datetime
|
|
6
|
+
import json
|
|
6
7
|
import os
|
|
7
|
-
import
|
|
8
|
-
import atexit
|
|
8
|
+
import re
|
|
9
9
|
import shutil
|
|
10
|
+
import subprocess
|
|
11
|
+
import sys
|
|
12
|
+
import time
|
|
10
13
|
import traceback
|
|
11
|
-
import argparse
|
|
12
|
-
import re
|
|
13
14
|
|
|
15
|
+
from enum import Enum
|
|
16
|
+
from pathlib import Path
|
|
14
17
|
from importlib import import_module
|
|
15
18
|
|
|
16
19
|
global_exit_allowed = False # pylint: disable=invalid-name
|
|
@@ -26,6 +29,7 @@ args = { # pylint: disable=invalid-name
|
|
|
26
29
|
'fancy' : sys.stdout.isatty(),
|
|
27
30
|
'warnings' : 0,
|
|
28
31
|
'errors' : 0,
|
|
32
|
+
'artifacts-json': True,
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
class Colors:
|
|
@@ -70,6 +74,138 @@ def yellow_text(text: str) -> str:
|
|
|
70
74
|
return Colors.yellow + text + Colors.normal
|
|
71
75
|
return text
|
|
72
76
|
|
|
77
|
+
class ArtifactTypes(Enum):
|
|
78
|
+
'''Types that are allow-listed for artifacts.add* methods. If you don't use one of
|
|
79
|
+
these, you'll get a warning'''
|
|
80
|
+
TEXT = 0
|
|
81
|
+
JSON = 1
|
|
82
|
+
YAML = 2
|
|
83
|
+
WAVEFORM = 3
|
|
84
|
+
DOTF = 4
|
|
85
|
+
TCL = 5
|
|
86
|
+
SHELL = 6
|
|
87
|
+
|
|
88
|
+
class Artifacts:
|
|
89
|
+
'''Class to hold file artifacts, for logs generated by EDA, or other artifcats created
|
|
90
|
+
by a specific tool.
|
|
91
|
+
|
|
92
|
+
If enabled, will write out an artifacts.json file upon util.exit()
|
|
93
|
+
'''
|
|
94
|
+
|
|
95
|
+
data = {}
|
|
96
|
+
unresolved_data = {}
|
|
97
|
+
enabled = True
|
|
98
|
+
artifacts_json_filepath = os.path.join('eda.work', 'artifacts.json')
|
|
99
|
+
|
|
100
|
+
def _get_type_str(self, typ: str) -> (bool, str):
|
|
101
|
+
'''Gets the name from ArtifactTypes, and returns tuple of (True, str)
|
|
102
|
+
|
|
103
|
+
if the ArtifactTypes exists, else (False, str.upper())'''
|
|
104
|
+
ret = getattr(ArtifactTypes, typ.upper(), '')
|
|
105
|
+
if ret:
|
|
106
|
+
return True, ret.name.lower()
|
|
107
|
+
return False, typ.lower()
|
|
108
|
+
|
|
109
|
+
def add(self, name: str, typ: str, description: str = '') -> None:
|
|
110
|
+
'''Adds file to artifacts'''
|
|
111
|
+
|
|
112
|
+
if not self.enabled:
|
|
113
|
+
return # artifacts dumping disabled.
|
|
114
|
+
|
|
115
|
+
_, shortname = os.path.split(name)
|
|
116
|
+
if shortname in self.data:
|
|
117
|
+
return # artifact already registered
|
|
118
|
+
|
|
119
|
+
type_exists, type_str = self._get_type_str(typ)
|
|
120
|
+
if not type_exists:
|
|
121
|
+
warning(f'Unknown artifacts {typ=} add for {name=} {description=}')
|
|
122
|
+
|
|
123
|
+
self.data[shortname] = {
|
|
124
|
+
'name': name,
|
|
125
|
+
'type': type_str,
|
|
126
|
+
'size': 0, # will be filled in when JSON written upon exit.
|
|
127
|
+
'description': description,
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
def add_extension(
|
|
131
|
+
self, search_paths: list, file_extension: str, typ: str, description: str = ''
|
|
132
|
+
) -> None:
|
|
133
|
+
'''Adds a placeholder for file extensions to add to artifacts
|
|
134
|
+
|
|
135
|
+
For example, when saving a Simulation waves, we may know it's a .fst or .vcd file,
|
|
136
|
+
but we do not know what the testbench SV file has set in $dumpfile(filepath)
|
|
137
|
+
'''
|
|
138
|
+
type_exists, type_str = self._get_type_str(typ)
|
|
139
|
+
if not type_exists:
|
|
140
|
+
warning(f'Unknown artifacts {typ=} add_extension for {search_paths=}',
|
|
141
|
+
f'{file_extension=} {description=}')
|
|
142
|
+
|
|
143
|
+
if isinstance(search_paths, str):
|
|
144
|
+
sp = [search_paths]
|
|
145
|
+
else:
|
|
146
|
+
sp = search_paths
|
|
147
|
+
self.unresolved_data[file_extension] = {
|
|
148
|
+
'search_paths': sp,
|
|
149
|
+
'type': type_str,
|
|
150
|
+
'description': description
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
def set_artifacts_json_dir(self, dirpath: str) -> None:
|
|
154
|
+
'''Sets the artifacts_json_filepath'''
|
|
155
|
+
_, right = os.path.split(self.artifacts_json_filepath)
|
|
156
|
+
self.artifacts_json_filepath = os.path.join(dirpath, right)
|
|
157
|
+
|
|
158
|
+
def reset(self, enable: bool = True) -> None:
|
|
159
|
+
'''Clears internal data (dict). Called by util.process_tokens()'''
|
|
160
|
+
self.data = {}
|
|
161
|
+
self.unresolved_data = {}
|
|
162
|
+
self.enabled = enable or args['artifacts-json']
|
|
163
|
+
self.artifacts_json_filepath = os.path.join('eda.work', 'artifacts.json')
|
|
164
|
+
|
|
165
|
+
def _resolve_unresolved_data(self, ext: str) -> None:
|
|
166
|
+
'''Find files for this extension that were registered using add_extension(...)
|
|
167
|
+
|
|
168
|
+
adds to self.data'''
|
|
169
|
+
entry = self.unresolved_data[ext]
|
|
170
|
+
if not entry:
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
for search_path in entry['search_paths']:
|
|
174
|
+
p = Path(search_path)
|
|
175
|
+
for posix_filename in p.glob(f'*.{ext}'):
|
|
176
|
+
self.add(
|
|
177
|
+
name=str(posix_filename), typ=entry['type'], description=entry['description']
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
def write_json(self) -> None:
|
|
181
|
+
'''Write out the artifacts.json file, called by util.exit()'''
|
|
182
|
+
|
|
183
|
+
if not self.enabled:
|
|
184
|
+
return # artifacts dumping disabled.
|
|
185
|
+
|
|
186
|
+
if self.unresolved_data:
|
|
187
|
+
for ext in self.unresolved_data:
|
|
188
|
+
self._resolve_unresolved_data(ext)
|
|
189
|
+
|
|
190
|
+
if not self.data:
|
|
191
|
+
return # do nothing if we have no artifacts
|
|
192
|
+
|
|
193
|
+
# Update all file sizes:
|
|
194
|
+
for key, entry in self.data.items():
|
|
195
|
+
if os.path.isfile(entry['name']):
|
|
196
|
+
entry['size'] = os.path.getsize(entry['name'])
|
|
197
|
+
else:
|
|
198
|
+
# file doesn't exist, remove it from artifacts.
|
|
199
|
+
warning(f'Removing {key} ({entry["name"]}) from artifacts (file does not exist)')
|
|
200
|
+
del self.data[key]
|
|
201
|
+
|
|
202
|
+
with open(self.artifacts_json_filepath, 'w', encoding='utf-8') as f:
|
|
203
|
+
json.dump(self.data, f, indent=4)
|
|
204
|
+
info(f'Wrote artifacts JSON: {self.artifacts_json_filepath}')
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
artifacts = Artifacts()
|
|
208
|
+
|
|
73
209
|
|
|
74
210
|
class UtilLogger:
|
|
75
211
|
'''Class for the util.global_log'''
|
|
@@ -115,6 +251,8 @@ class UtilLogger:
|
|
|
115
251
|
debug(f"Opened logfile '{filename}' for writing")
|
|
116
252
|
self.filepath = filename
|
|
117
253
|
self.write_timestamp(f'start - {self.filepath}')
|
|
254
|
+
# add to global artifacts:
|
|
255
|
+
artifacts.add(name=filename, typ='text', description='EDA stdout logfile')
|
|
118
256
|
except Exception as e:
|
|
119
257
|
error(f"Error opening '{filename}' for writing, {e}")
|
|
120
258
|
self.clear()
|
|
@@ -213,6 +351,8 @@ def get_argparser() -> argparse.ArgumentParser:
|
|
|
213
351
|
parser.add_argument('--no-respawn', action='store_true',
|
|
214
352
|
help=('Legacy mode (default respawn disabled) for respawning eda.py'
|
|
215
353
|
' using $OC_ROOT/bin'))
|
|
354
|
+
parser.add_argument('--artifacts-json', **bool_action_kwargs,
|
|
355
|
+
help='Store a work-dir/artifacts.json file of known tool output files')
|
|
216
356
|
return parser
|
|
217
357
|
|
|
218
358
|
|
|
@@ -265,21 +405,29 @@ def process_tokens(tokens:list) -> (argparse.Namespace, list):
|
|
|
265
405
|
|
|
266
406
|
debug(f'util.process_tokens: {parsed=} {unparsed=} from {tokens=}')
|
|
267
407
|
|
|
408
|
+
# clear existing artifacts dicts (mostly for pytests repeatedly calling eda.main),
|
|
409
|
+
# set artifacts.enabled based on args['artifacts-json']
|
|
410
|
+
artifacts.reset(enable=parsed.artifacts_json)
|
|
411
|
+
|
|
268
412
|
if parsed.force_logfile:
|
|
269
413
|
start_log(parsed.force_logfile, force=True)
|
|
270
414
|
elif parsed.logfile:
|
|
271
415
|
start_log(parsed.logfile, force=False)
|
|
272
416
|
elif parsed.default_log and \
|
|
417
|
+
not any(x in unparsed for x in ('help', '-h', '--help')) and \
|
|
273
418
|
(parsed.force_logfile is None and parsed.logfile is None):
|
|
274
419
|
# Use a forced logfile in the eda.work/eda.log:
|
|
420
|
+
# avoid this if someone has --help arg not yet parsed.
|
|
275
421
|
start_log(global_log.default_log_filepath, force=True)
|
|
276
422
|
|
|
277
423
|
|
|
278
424
|
parsed_as_dict = vars(parsed)
|
|
279
425
|
for key,value in parsed_as_dict.items():
|
|
426
|
+
key = key.replace('_', '-')
|
|
280
427
|
if value is not None:
|
|
281
428
|
args[key] = value # only update with non-None values to our global 'args' dict
|
|
282
429
|
|
|
430
|
+
|
|
283
431
|
return parsed, unparsed
|
|
284
432
|
|
|
285
433
|
# ********************
|
|
@@ -500,6 +648,10 @@ def exit( # pylint: disable=redefined-builtin
|
|
|
500
648
|
error_code: int = 0, quiet: bool = False
|
|
501
649
|
) -> int:
|
|
502
650
|
'''sys.exit(int) wrapper, returns the error_code if global_exit_allowed=False'''
|
|
651
|
+
|
|
652
|
+
# Save out artifacts file:
|
|
653
|
+
artifacts.write_json()
|
|
654
|
+
|
|
503
655
|
if global_exit_allowed:
|
|
504
656
|
if not quiet:
|
|
505
657
|
info(f"Exiting with {args['warnings']} warnings, {args['errors']} errors")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opencos-eda
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.50
|
|
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
|
|
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
|