opencos-eda 0.2.35__py3-none-any.whl → 0.2.38__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/__init__.py +2 -0
- opencos/commands/targets.py +49 -0
- opencos/eda.py +39 -99
- opencos/eda_base.py +8 -3
- opencos/eda_config.py +20 -11
- opencos/eda_config_defaults.yml +28 -1
- opencos/eda_config_reduced.yml +0 -1
- opencos/eda_deps_bash_completion.bash +2 -2
- opencos/{eda_extract_deps_keys.py → eda_extract_targets.py} +37 -19
- opencos/export_helper.py +20 -19
- opencos/files.py +2 -2
- opencos/tests/helpers.py +7 -6
- opencos/tests/test_eda.py +22 -0
- opencos/tools/invio.py +4 -5
- opencos/tools/invio_yosys.py +28 -20
- opencos/tools/slang_yosys.py +10 -135
- opencos/tools/yosys.py +135 -0
- {opencos_eda-0.2.35.dist-info → opencos_eda-0.2.38.dist-info}/METADATA +1 -1
- {opencos_eda-0.2.35.dist-info → opencos_eda-0.2.38.dist-info}/RECORD +24 -23
- {opencos_eda-0.2.35.dist-info → opencos_eda-0.2.38.dist-info}/entry_points.txt +1 -1
- {opencos_eda-0.2.35.dist-info → opencos_eda-0.2.38.dist-info}/WHEEL +0 -0
- {opencos_eda-0.2.35.dist-info → opencos_eda-0.2.38.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.2.35.dist-info → opencos_eda-0.2.38.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.2.35.dist-info → opencos_eda-0.2.38.dist-info}/top_level.txt +0 -0
opencos/export_helper.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import shutil
|
|
2
3
|
import json
|
|
3
4
|
|
|
4
5
|
from opencos import util
|
|
@@ -75,7 +76,7 @@ def find_sv_included_files_within_file(filename:str,
|
|
|
75
76
|
known_incdir_paths:list,
|
|
76
77
|
warnings:bool=True,
|
|
77
78
|
modify_files_and_save_to_path=None,
|
|
78
|
-
|
|
79
|
+
unmodified_files_copy_to_path=None) -> list:
|
|
79
80
|
'''Given a filename (full path) and a list of known incdir paths, returns
|
|
80
81
|
a list of included files (full path).
|
|
81
82
|
|
|
@@ -83,8 +84,8 @@ def find_sv_included_files_within_file(filename:str,
|
|
|
83
84
|
to strip all path information on the `include "(path)" for example:
|
|
84
85
|
`include "foo.svh" -- no modifications
|
|
85
86
|
`include "../bar.svh" -- is modified to become `include "bar.svh"
|
|
86
|
-
(Optional)
|
|
87
|
-
to
|
|
87
|
+
(Optional) unmodified_files_copy_to_path (str: directory/path) if you wish
|
|
88
|
+
to copy unmodified files to this path.
|
|
88
89
|
'''
|
|
89
90
|
|
|
90
91
|
found_included_files = set()
|
|
@@ -97,7 +98,7 @@ def find_sv_included_files_within_file(filename:str,
|
|
|
97
98
|
|
|
98
99
|
filename_no_path = os.path.split(filename)[1]
|
|
99
100
|
|
|
100
|
-
debug(f'export_helper: {filename=} {modify_files_and_save_to_path=} {
|
|
101
|
+
debug(f'export_helper: {filename=} {modify_files_and_save_to_path=} {unmodified_files_copy_to_path=}')
|
|
101
102
|
|
|
102
103
|
with open(filename) as f:
|
|
103
104
|
|
|
@@ -153,13 +154,13 @@ def find_sv_included_files_within_file(filename:str,
|
|
|
153
154
|
else:
|
|
154
155
|
outf.write(line)
|
|
155
156
|
|
|
156
|
-
#
|
|
157
|
-
if len(modified_lines) == 0 and
|
|
158
|
-
if os.path.isdir(
|
|
159
|
-
dst = os.path.join(
|
|
157
|
+
# Copy unmodified files to some path.
|
|
158
|
+
if len(modified_lines) == 0 and unmodified_files_copy_to_path:
|
|
159
|
+
if os.path.isdir(unmodified_files_copy_to_path):
|
|
160
|
+
dst = os.path.join(unmodified_files_copy_to_path, filename_no_path)
|
|
160
161
|
if not os.path.exists(dst):
|
|
161
|
-
debug(f'export_helper:
|
|
162
|
-
|
|
162
|
+
debug(f'export_helper: Copied unmodified {filename=} to {dst=}')
|
|
163
|
+
shutil.copy(src=filename, dst=dst)
|
|
163
164
|
|
|
164
165
|
|
|
165
166
|
# Back to the list found_included_files that we observed within our filename, we
|
|
@@ -193,7 +194,7 @@ def find_sv_included_files_within_file(filename:str,
|
|
|
193
194
|
def get_list_sv_included_files(all_src_files:list, known_incdir_paths:list, target:str='',
|
|
194
195
|
warnings:bool=True,
|
|
195
196
|
modify_files_and_save_to_path=None,
|
|
196
|
-
|
|
197
|
+
unmodified_files_copy_to_path=None) -> list:
|
|
197
198
|
''' Given a list of all_src_files, and list of known_incdir_paths, returns a list
|
|
198
199
|
of all included files (fullpath). This is recurisve if an included file includes another file.
|
|
199
200
|
|
|
@@ -205,7 +206,7 @@ def get_list_sv_included_files(all_src_files:list, known_incdir_paths:list, targ
|
|
|
205
206
|
`include "foo.svh" -- no modifications
|
|
206
207
|
`include "../bar.svh" -- is modified to become `include "bar.svh"
|
|
207
208
|
Set to None (default) to disable.
|
|
208
|
-
|
|
209
|
+
unmodified_files_copy_to_path -- (str: directory/path) if you wish to copy unmodified
|
|
209
210
|
files to this path. Set to None (default) to disable.
|
|
210
211
|
'''
|
|
211
212
|
|
|
@@ -220,7 +221,7 @@ def get_list_sv_included_files(all_src_files:list, known_incdir_paths:list, targ
|
|
|
220
221
|
known_incdir_paths=known_incdir_paths,
|
|
221
222
|
warnings=warnings,
|
|
222
223
|
modify_files_and_save_to_path=modify_files_and_save_to_path,
|
|
223
|
-
|
|
224
|
+
unmodified_files_copy_to_path=unmodified_files_copy_to_path
|
|
224
225
|
)
|
|
225
226
|
|
|
226
227
|
for f in included_files_list:
|
|
@@ -239,7 +240,7 @@ def get_list_sv_included_files(all_src_files:list, known_incdir_paths:list, targ
|
|
|
239
240
|
known_incdir_paths=known_incdir_paths,
|
|
240
241
|
warnings=warnings,
|
|
241
242
|
modify_files_and_save_to_path=modify_files_and_save_to_path,
|
|
242
|
-
|
|
243
|
+
unmodified_files_copy_to_path=unmodified_files_copy_to_path
|
|
243
244
|
)
|
|
244
245
|
sv_included_files_dict[fname] = True # mark as traversed.
|
|
245
246
|
|
|
@@ -317,7 +318,7 @@ class ExportHelper:
|
|
|
317
318
|
known_incdir_paths=self.cmd_design_obj.incdirs,
|
|
318
319
|
target=self.target,
|
|
319
320
|
modify_files_and_save_to_path=self.out_dir,
|
|
320
|
-
|
|
321
|
+
unmodified_files_copy_to_path=self.out_dir
|
|
321
322
|
)
|
|
322
323
|
|
|
323
324
|
info(f"export_helper: {self.target=} included files {self.included_files=}")
|
|
@@ -325,7 +326,7 @@ class ExportHelper:
|
|
|
325
326
|
for filename in self.cmd_design_obj.files_non_source:
|
|
326
327
|
dst = os.path.join(self.out_dir, os.path.split(filename)[1])
|
|
327
328
|
if not os.path.exists(dst):
|
|
328
|
-
|
|
329
|
+
shutil.copy(src=filename, dst=dst)
|
|
329
330
|
|
|
330
331
|
def create_deps_yml_in_out_dir(self, deps_file_args:list=list()):
|
|
331
332
|
if not self.target:
|
|
@@ -449,11 +450,11 @@ class ExportHelper:
|
|
|
449
450
|
info(f'export_helper: Wrote {dst=}')
|
|
450
451
|
|
|
451
452
|
# If this was from an `export` command, and the self.out_dir != self.args['work-dir'], then
|
|
452
|
-
#
|
|
453
|
+
# copy the export.json to the work-dir:
|
|
453
454
|
if self.out_dir != self.args['work-dir']:
|
|
454
455
|
util.safe_mkdirs(base=self.args['work-dir'], new_dirs=['export'])
|
|
455
456
|
src = os.path.abspath(os.path.join(self.out_dir, 'export.json'))
|
|
456
457
|
dst = os.path.join(self.args['work-dir'], 'export', 'export.json')
|
|
457
458
|
if not os.path.exists(dst):
|
|
458
|
-
|
|
459
|
-
info(f'export_helper:
|
|
459
|
+
shutil.copy(src=filename, dst=dst)
|
|
460
|
+
info(f'export_helper: Copied {src=} to {dst=}')
|
opencos/files.py
CHANGED
|
@@ -29,7 +29,7 @@ ALL_FORCED_PREFIXES = set(list(FORCE_PREFIX_DICT.keys()))
|
|
|
29
29
|
|
|
30
30
|
def get_source_file(target:str) -> (bool, str, str):
|
|
31
31
|
'''Returns tuple: bool if file exists, filepath str, and optional forced file type str'''
|
|
32
|
-
if os.path.
|
|
32
|
+
if os.path.isfile(target):
|
|
33
33
|
# target exists as a file, return True w/ original target:
|
|
34
34
|
return True, target, ''
|
|
35
35
|
|
|
@@ -37,7 +37,7 @@ def get_source_file(target:str) -> (bool, str, str):
|
|
|
37
37
|
for p in ALL_FORCED_PREFIXES:
|
|
38
38
|
if p in target:
|
|
39
39
|
fpath = ''.join(target.split(p)) # essentially just removing the "sv@" or whatever it is
|
|
40
|
-
if os.path.
|
|
40
|
+
if os.path.isfile(fpath):
|
|
41
41
|
return True, fpath, FORCE_PREFIX_DICT.get(p)
|
|
42
42
|
|
|
43
43
|
# target or fpath didn't exist, return False with the original target:
|
opencos/tests/helpers.py
CHANGED
|
@@ -142,13 +142,14 @@ class Helpers:
|
|
|
142
142
|
# TODO(drew): There are some issues with log_it redirecting stdout from vivado
|
|
143
143
|
# and modelsim_ase. So this may not work for all tools, you may have to directly
|
|
144
144
|
# look at eda.work/{target}.sim/sim.log or xsim.log.
|
|
145
|
+
print(f'{os.getcwd()=}')
|
|
146
|
+
print(f'{command_str=}')
|
|
145
147
|
with open(logfile, 'w', encoding='utf-8') as f:
|
|
146
|
-
with redirect_stdout(f):
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
rc = eda.main(*(command_str.split()))
|
|
148
|
+
with redirect_stdout(f), redirect_stderr(f):
|
|
149
|
+
if use_eda_wrap:
|
|
150
|
+
rc = eda_wrap(*(command_str.split()))
|
|
151
|
+
else:
|
|
152
|
+
rc = eda.main(*(command_str.split()))
|
|
152
153
|
print(f'Wrote: {os.path.abspath(logfile)=}')
|
|
153
154
|
return rc
|
|
154
155
|
|
opencos/tests/test_eda.py
CHANGED
|
@@ -53,6 +53,28 @@ def test_args_sim_default_tool():
|
|
|
53
53
|
assert rc == 0
|
|
54
54
|
|
|
55
55
|
|
|
56
|
+
class TestTargets(Helpers):
|
|
57
|
+
'''Tests for: eda targets'''
|
|
58
|
+
|
|
59
|
+
DEFAULT_DIR = os.path.join(THISPATH, '..', '..', 'lib', 'tests')
|
|
60
|
+
|
|
61
|
+
def test_lib_tests__no_pattern(self):
|
|
62
|
+
'''Test that this works: eda targets'''
|
|
63
|
+
self.chdir()
|
|
64
|
+
rc = self.log_it('targets --debug', use_eda_wrap=False)
|
|
65
|
+
assert rc == 0
|
|
66
|
+
assert self.is_in_log('oclib_fifo_test')
|
|
67
|
+
assert self.is_in_log('oclib_rrarb_test')
|
|
68
|
+
|
|
69
|
+
def test_lib_tests__with_pattern(self):
|
|
70
|
+
'''Test that this works: eda targets oclib_fifo*test'''
|
|
71
|
+
self.chdir()
|
|
72
|
+
rc = self.log_it('targets oclib_fifo*test', use_eda_wrap=False)
|
|
73
|
+
assert rc == 0
|
|
74
|
+
assert self.is_in_log('oclib_fifo_test')
|
|
75
|
+
assert not self.is_in_log('oclib_rrarb_test')
|
|
76
|
+
|
|
77
|
+
|
|
56
78
|
@pytest.mark.skipif(
|
|
57
79
|
'verilator' not in tools_loaded, reason="requires verilator"
|
|
58
80
|
)
|
opencos/tools/invio.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
# pylint: disable=R0801 # (duplicate code in derived classes, such as if-condition return.)
|
|
4
4
|
|
|
5
|
-
import shutil
|
|
6
5
|
import importlib.util
|
|
7
6
|
|
|
8
7
|
from opencos import util
|
|
@@ -22,11 +21,11 @@ class ToolInvio(Tool):
|
|
|
22
21
|
if self._VERSION:
|
|
23
22
|
return self._VERSION
|
|
24
23
|
|
|
25
|
-
# We also have to make sure
|
|
26
|
-
invio_py_path = shutil.which('invio-py')
|
|
24
|
+
# We also have to make sure we can import invio within python.
|
|
27
25
|
spec = importlib.util.find_spec('invio')
|
|
28
|
-
if not spec
|
|
29
|
-
self.error('"invio
|
|
26
|
+
if not spec:
|
|
27
|
+
self.error('"invio" package not in python env')
|
|
28
|
+
|
|
30
29
|
|
|
31
30
|
return super().get_versions()
|
|
32
31
|
|
opencos/tools/invio_yosys.py
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
# pylint: disable=too-many-locals # TODO(drew): fix this later.
|
|
6
6
|
|
|
7
7
|
import os
|
|
8
|
-
import shutil
|
|
9
8
|
import importlib.util
|
|
10
9
|
|
|
11
10
|
from opencos import util
|
|
@@ -23,11 +22,10 @@ class ToolInvioYosys(ToolYosys):
|
|
|
23
22
|
if self._VERSION:
|
|
24
23
|
return self._VERSION
|
|
25
24
|
|
|
26
|
-
# We also have to make sure
|
|
27
|
-
invio_py_path = shutil.which('invio-py')
|
|
25
|
+
# We also have to make sure we can import invio within python.
|
|
28
26
|
spec = importlib.util.find_spec('invio')
|
|
29
|
-
if not spec
|
|
30
|
-
self.error('"invio
|
|
27
|
+
if not spec:
|
|
28
|
+
self.error('"invio" package not in python env')
|
|
31
29
|
|
|
32
30
|
# run ToolYosys.get_versions() to set up self.yosys_exe, and return the version
|
|
33
31
|
# str:
|
|
@@ -49,6 +47,9 @@ class CommandSynthInvioYosys(CommonSynthYosys, ToolInvioYosys):
|
|
|
49
47
|
self.args.update({
|
|
50
48
|
'invio-blackbox': [], # list of modules that invio/verific will blackbox.
|
|
51
49
|
})
|
|
50
|
+
self.args_help.update({
|
|
51
|
+
'invio-blackbox': 'List of modules that invio will blackbox prior to yosys',
|
|
52
|
+
})
|
|
52
53
|
|
|
53
54
|
def write_and_run_yosys_f_files(self, **kwargs) -> None:
|
|
54
55
|
|
|
@@ -74,13 +75,13 @@ class CommandSynthInvioYosys(CommonSynthYosys, ToolInvioYosys):
|
|
|
74
75
|
os.mkdir(fullp)
|
|
75
76
|
|
|
76
77
|
# create yosys.f so we can run a few commands within yosys.
|
|
77
|
-
yosys_f_path = os.path.join(
|
|
78
|
-
yosys_v_path = os.path.join(
|
|
79
|
-
|
|
80
|
-
synth_command = self.args.get('yosys-synth', 'synth')
|
|
78
|
+
yosys_f_path = os.path.join(self.full_work_dir, 'yosys.f')
|
|
79
|
+
self.yosys_v_path = os.path.join(self.yosys_out_dir, invio_dict['v_filename'])
|
|
81
80
|
|
|
82
81
|
with open(yosys_f_path, 'w', encoding='utf-8') as f:
|
|
83
82
|
lines = []
|
|
83
|
+
if self.args['liberty-file']:
|
|
84
|
+
lines.append('read_liberty -lib ' + self.args['liberty-file'])
|
|
84
85
|
for path in invio_dict.get('blackbox_files_list', []):
|
|
85
86
|
# We have to read the verilog files from the invio blackbox_files_list:
|
|
86
87
|
lines.append(f'read_verilog {path}')
|
|
@@ -88,12 +89,9 @@ class CommandSynthInvioYosys(CommonSynthYosys, ToolInvioYosys):
|
|
|
88
89
|
# But we may blackbox different cells for yosys synthesis.
|
|
89
90
|
lines.append(f'blackbox {module}')
|
|
90
91
|
|
|
92
|
+
|
|
91
93
|
lines.append(f'read_verilog {invio_dict["full_v_filename"]}')
|
|
92
|
-
lines += self.
|
|
93
|
-
lines += [
|
|
94
|
-
synth_command,
|
|
95
|
-
f'write_verilog {yosys_v_path}'
|
|
96
|
-
]
|
|
94
|
+
lines += self.get_synth_command_lines()
|
|
97
95
|
f.write('\n'.join(lines))
|
|
98
96
|
|
|
99
97
|
synth_command_list = util.ShellCommandList(
|
|
@@ -105,6 +103,9 @@ class CommandSynthInvioYosys(CommonSynthYosys, ToolInvioYosys):
|
|
|
105
103
|
['python3', invio_dict['full_py_filename']], tee_fpath=invio_dict['full_py_filename']
|
|
106
104
|
)
|
|
107
105
|
|
|
106
|
+
# Optinally create and run a sta.f:
|
|
107
|
+
sta_command_list = self.create_sta_f() # [] or util.ShellCommandList
|
|
108
|
+
|
|
108
109
|
# We create a run_yosys.sh wrapping these scripts, but we do not run this one.
|
|
109
110
|
util.write_shell_command_file(
|
|
110
111
|
dirpath=self.args['work-dir'],
|
|
@@ -121,18 +122,25 @@ class CommandSynthInvioYosys(CommonSynthYosys, ToolInvioYosys):
|
|
|
121
122
|
# Gives us bash commands with tee and pipstatus:
|
|
122
123
|
invio_command_list,
|
|
123
124
|
synth_command_list,
|
|
125
|
+
sta_command_list,
|
|
124
126
|
],
|
|
125
127
|
)
|
|
126
128
|
|
|
127
129
|
# Do not run this if args['stop-before-compile'] is True
|
|
128
130
|
if self.args.get('stop-before-compile', False) or \
|
|
129
131
|
self.args.get('stop-after-compile', False):
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
# Run the synth commands standalone:
|
|
135
|
+
self.exec( work_dir=work_dir, command_list=synth_command_list,
|
|
136
|
+
tee_fpath=synth_command_list.tee_fpath )
|
|
137
|
+
|
|
138
|
+
if self.args['sta']:
|
|
139
|
+
self.exec(work_dir=self.full_work_dir, command_list=sta_command_list,
|
|
140
|
+
tee_fpath=sta_command_list.tee_fpath)
|
|
141
|
+
|
|
142
|
+
if self.status == 0:
|
|
143
|
+
util.info(f'yosys: wrote verilog to {self.yosys_v_path}')
|
|
136
144
|
|
|
137
145
|
|
|
138
146
|
class CommandElabInvioYosys(CommandSynthInvioYosys):
|
opencos/tools/slang_yosys.py
CHANGED
|
@@ -8,8 +8,7 @@ Contains classes for ToolSlangYosys, CommandSynthSlangYosys
|
|
|
8
8
|
import os
|
|
9
9
|
|
|
10
10
|
from opencos import util
|
|
11
|
-
from opencos.
|
|
12
|
-
from opencos.tools.yosys import ToolYosys
|
|
11
|
+
from opencos.tools.yosys import ToolYosys, CommonSynthYosys
|
|
13
12
|
|
|
14
13
|
class ToolSlangYosys(ToolYosys):
|
|
15
14
|
'''Uses slang.so in yosys plugins directory, called via yosys > plugin -i slang'''
|
|
@@ -28,49 +27,17 @@ class ToolSlangYosys(ToolYosys):
|
|
|
28
27
|
})
|
|
29
28
|
|
|
30
29
|
|
|
31
|
-
class CommandSynthSlangYosys(
|
|
30
|
+
class CommandSynthSlangYosys(CommonSynthYosys, ToolSlangYosys):
|
|
32
31
|
'''CommandSynthSlangYosys is a command handler for: eda synth --tool=slang_yosys'''
|
|
33
32
|
|
|
34
33
|
def __init__(self, config: dict):
|
|
35
|
-
|
|
34
|
+
CommonSynthYosys.__init__(self, config)
|
|
36
35
|
ToolSlangYosys.__init__(self, config=self.config)
|
|
37
|
-
self.args.update({
|
|
38
|
-
'sta': False,
|
|
39
|
-
'liberty-file': '',
|
|
40
|
-
'sdc-file': '',
|
|
41
|
-
'yosys-synth': 'synth', # synth_xilinx, synth_altera, etc (see: yosys help)
|
|
42
|
-
'yosys-pre-synth': ['prep', 'proc'], # command run in yosys prior to yosys-synth.
|
|
43
|
-
'yosys-blackbox': [], # list of modules that yosys will blackbox.
|
|
44
|
-
})
|
|
45
|
-
self.args_help.update({
|
|
46
|
-
'sta': 'After running Yosys, run "sta" with --liberty-file.' \
|
|
47
|
-
+ ' sta can be installed via: https://github.com/The-OpenROAD-Project/OpenSTA',
|
|
48
|
-
'sdc-file': '.sdc file to use with --sta, if not present will use auto constraints',
|
|
49
|
-
'liberty-file': 'Single liberty file for synthesis and sta,' \
|
|
50
|
-
+ ' for example: github/OpenSTA/examples/nangate45_slow.lib.gz',
|
|
51
|
-
'yosys-synth': 'The synth command provided to Yosys, see: yosys help.',
|
|
52
|
-
'yosys-pre-synth': 'Yosys commands performed prior to running "synth"' \
|
|
53
|
-
+ ' (or eda arg value for --yosys-synth)',
|
|
54
|
-
'yosys-blackbox': 'List of modules that yosys will blackbox, likely will need these' \
|
|
55
|
-
+ ' in Verilog-2001 for yosys to read outside of slang and synth',
|
|
56
|
-
})
|
|
57
36
|
|
|
58
37
|
self.slang_out_dir = ''
|
|
59
|
-
self.yosys_out_dir = ''
|
|
60
38
|
self.slang_v_path = ''
|
|
61
|
-
self.yosys_v_path = ''
|
|
62
|
-
self.full_work_dir = ''
|
|
63
|
-
self.blackbox_list = []
|
|
64
39
|
|
|
65
|
-
def
|
|
66
|
-
CommandSynth.do_it(self)
|
|
67
|
-
|
|
68
|
-
if self.is_export_enabled():
|
|
69
|
-
return
|
|
70
|
-
|
|
71
|
-
self._write_and_run_yosys_f_files()
|
|
72
|
-
|
|
73
|
-
def _write_and_run_yosys_f_files(self):
|
|
40
|
+
def write_and_run_yosys_f_files(self, **kwargs) -> None:
|
|
74
41
|
'''
|
|
75
42
|
1. Creates and runs: yosys.slang.f
|
|
76
43
|
-- should create post_slang_ls.txt
|
|
@@ -88,28 +55,20 @@ class CommandSynthSlangYosys(CommandSynth, ToolSlangYosys):
|
|
|
88
55
|
util.debug(f'slang_yosys: {self.blackbox_list=}')
|
|
89
56
|
|
|
90
57
|
# create {work_dir} / yosys
|
|
91
|
-
self.full_work_dir = self.args.get('work-dir', '')
|
|
92
|
-
if not self.full_work_dir:
|
|
93
|
-
self.error(f'work_dir={self.full_work_dir} is not set')
|
|
94
|
-
self.full_work_dir = os.path.abspath(self.full_work_dir)
|
|
95
58
|
self.slang_out_dir = os.path.join(self.full_work_dir, 'slang')
|
|
96
|
-
|
|
97
|
-
for p in [self.slang_out_dir, self.yosys_out_dir]:
|
|
98
|
-
util.safe_mkdir(p)
|
|
59
|
+
util.safe_mkdir(self.slang_out_dir)
|
|
99
60
|
|
|
100
61
|
self.slang_v_path = os.path.join(self.slang_out_dir, f'{self.args["top"]}.v')
|
|
101
|
-
self.yosys_v_path = os.path.join(self.yosys_out_dir, f'{self.args["top"]}.v')
|
|
102
|
-
|
|
103
62
|
|
|
104
63
|
# Run our created yosys.slang.f script
|
|
105
64
|
# Note - this will always run, even if --stop-before-compile is set.
|
|
106
65
|
slang_command_list = self._create_yosys_slang_f() # util.ShellCommandList
|
|
107
66
|
|
|
108
67
|
# Create and run yosys.synth.f
|
|
109
|
-
synth_command_list = self.
|
|
68
|
+
synth_command_list = self.create_yosys_synth_f() # util.ShellCommandList
|
|
110
69
|
|
|
111
70
|
# Optinally create and run a sta.f:
|
|
112
|
-
sta_command_list = self.
|
|
71
|
+
sta_command_list = self.create_sta_f() # [] or util.ShellCommandList
|
|
113
72
|
|
|
114
73
|
# We create a run_yosys.sh wrapping these scripts, but we do not run this one.
|
|
115
74
|
util.write_shell_command_file(
|
|
@@ -246,7 +205,7 @@ class CommandSynthSlangYosys(CommandSynth, ToolSlangYosys):
|
|
|
246
205
|
yosys_blackbox_list.append(line)
|
|
247
206
|
return yosys_blackbox_list
|
|
248
207
|
|
|
249
|
-
def
|
|
208
|
+
def create_yosys_synth_f(self) -> util.ShellCommandList:
|
|
250
209
|
# Create yosys.synth.f
|
|
251
210
|
yosys_synth_f_path = os.path.join(self.full_work_dir, 'yosys.synth.f')
|
|
252
211
|
|
|
@@ -254,10 +213,6 @@ class CommandSynthSlangYosys(CommandSynth, ToolSlangYosys):
|
|
|
254
213
|
# yosys.synth.f script.
|
|
255
214
|
yosys_blackbox_list = self._get_yosys_blackbox_list()
|
|
256
215
|
|
|
257
|
-
synth_command = self.args.get('yosys-synth', 'synth')
|
|
258
|
-
if self.args['flatten-all']:
|
|
259
|
-
synth_command += ' -flatten'
|
|
260
|
-
|
|
261
216
|
if self.args['liberty-file'] and not os.path.exists(self.args['liberty-file']):
|
|
262
217
|
self.error(f'--liberty-file={self.args["liberty-file"]} file does not exist')
|
|
263
218
|
|
|
@@ -273,20 +228,7 @@ class CommandSynthSlangYosys(CommandSynth, ToolSlangYosys):
|
|
|
273
228
|
for inst in yosys_blackbox_list:
|
|
274
229
|
lines.append('blackbox ' + inst)
|
|
275
230
|
|
|
276
|
-
lines += self.
|
|
277
|
-
lines.append(synth_command)
|
|
278
|
-
|
|
279
|
-
# TODO(drew): I need a blackbox flow here? Or a memory_libmap?
|
|
280
|
-
# --> https://yosyshq.readthedocs.io/projects/yosys/en/latest/cmd/memory_libmap.html
|
|
281
|
-
# TODO(drew): can I run multiple liberty files?
|
|
282
|
-
if self.args['liberty-file']:
|
|
283
|
-
lines += [
|
|
284
|
-
'dfflibmap -liberty ' + self.args['liberty-file'],
|
|
285
|
-
#'memory_libmap -lib ' + self.args['liberty-file'], # Has to be unzipped?
|
|
286
|
-
'abc -liberty ' + self.args['liberty-file'],
|
|
287
|
-
]
|
|
288
|
-
|
|
289
|
-
lines.append(f'write_verilog {self.yosys_v_path}')
|
|
231
|
+
lines += self.get_synth_command_lines()
|
|
290
232
|
f.write('\n'.join(lines))
|
|
291
233
|
|
|
292
234
|
synth_command_list = util.ShellCommandList(
|
|
@@ -296,74 +238,7 @@ class CommandSynthSlangYosys(CommandSynth, ToolSlangYosys):
|
|
|
296
238
|
return synth_command_list
|
|
297
239
|
|
|
298
240
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
if not self.args['sta']:
|
|
302
|
-
return []
|
|
303
|
-
|
|
304
|
-
if not self.args['liberty-file']:
|
|
305
|
-
self.error('--sta is set, but need to also set --liberty-file=<file>')
|
|
306
|
-
|
|
307
|
-
if self.args['sdc-file']:
|
|
308
|
-
if not os.path.exists(self.args['sdc-file']):
|
|
309
|
-
self.error(f'--sdc-file={self.args["sdc-file"]} file does not exist')
|
|
310
|
-
|
|
311
|
-
if not self.sta_exe:
|
|
312
|
-
self.error(f'--sta is set, but "sta" was not found in PATH, see: {self._URL}')
|
|
313
|
-
|
|
314
|
-
sta_command_list = util.ShellCommandList(
|
|
315
|
-
[ self.sta_exe, '-no_init', '-exit', 'sta.f' ],
|
|
316
|
-
tee_fpath = 'sta.log'
|
|
317
|
-
)
|
|
318
|
-
|
|
319
|
-
# Need to create sta.f:
|
|
320
|
-
if self.args['sdc-file']:
|
|
321
|
-
sdc_path = self.args['sdc-file']
|
|
322
|
-
else:
|
|
323
|
-
# Need to create sdc.f:
|
|
324
|
-
sdc_path = 'sdc.f'
|
|
325
|
-
self._create_sdc_f()
|
|
326
|
-
|
|
327
|
-
with open(os.path.join(self.args['work-dir'], 'sta.f'), 'w',
|
|
328
|
-
encoding='utf-8') as f:
|
|
329
|
-
lines = [
|
|
330
|
-
'read_liberty ' + self.args['liberty-file'],
|
|
331
|
-
'read_verilog ' + self.yosys_v_path,
|
|
332
|
-
'link_design ' + self.args['top'],
|
|
333
|
-
'read_sdc ' + sdc_path,
|
|
334
|
-
'report_checks',
|
|
335
|
-
]
|
|
336
|
-
f.write('\n'.join(lines))
|
|
337
|
-
|
|
338
|
-
return util.ShellCommandList(
|
|
339
|
-
sta_command_list,
|
|
340
|
-
tee_fpath = 'sta.log'
|
|
341
|
-
)
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
def _create_sdc_f(self) -> None:
|
|
345
|
-
if self.args['sdc-file']:
|
|
346
|
-
# already exists from args, return b/c nothing to create.
|
|
347
|
-
return
|
|
348
|
-
|
|
349
|
-
with open(os.path.join(self.args['work-dir'], 'sdc.f'), 'w',
|
|
350
|
-
encoding='utf-8') as f:
|
|
351
|
-
clock_name = self.args['clock-name']
|
|
352
|
-
period = self.args['clock-ns']
|
|
353
|
-
name_not_equal_clocks_str = f'NAME !~ "{clock_name}"'
|
|
354
|
-
lines = [
|
|
355
|
-
f'create_clock -add -name {clock_name} -period {period} [get_ports ' \
|
|
356
|
-
+ '{' + clock_name + '}];',
|
|
357
|
-
f'set_input_delay -max {self.args["idelay-ns"]} -clock {clock_name}' \
|
|
358
|
-
+ ' [get_ports * -filter {DIRECTION == IN && ' \
|
|
359
|
-
+ name_not_equal_clocks_str + '}];',
|
|
360
|
-
f'set_output_delay -max {self.args["odelay-ns"]} -clock {clock_name}' \
|
|
361
|
-
+ ' [get_ports * -filter {DIRECTION == OUT}];',
|
|
362
|
-
]
|
|
363
|
-
f.write('\n'.join(lines))
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
class CommandElabSlangYosys(CommandSynthSlangYosys):
|
|
241
|
+
class CommandElabSlangYosys(CommandSynthSlangYosys): # pylint: disable=too-many-ancestors
|
|
367
242
|
'''CommandSynthSlangYosys is a command handler for: eda synth --tool=slang_yosys
|
|
368
243
|
|
|
369
244
|
Runs slang-yosys as elab only (does not run the synthesis portion), but is
|