opencos-eda 0.2.36__py3-none-any.whl → 0.2.39__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/multi.py +7 -3
- opencos/commands/sweep.py +12 -4
- opencos/commands/targets.py +49 -0
- opencos/eda.py +39 -99
- opencos/eda_base.py +111 -19
- 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 +1 -1
- opencos/eda_extract_targets.py +37 -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/iverilog.py +4 -2
- opencos/tools/modelsim_ase.py +4 -3
- opencos/tools/slang_yosys.py +10 -135
- opencos/tools/verilator.py +10 -8
- opencos/tools/vivado.py +0 -1
- opencos/tools/yosys.py +135 -0
- {opencos_eda-0.2.36.dist-info → opencos_eda-0.2.39.dist-info}/METADATA +1 -1
- {opencos_eda-0.2.36.dist-info → opencos_eda-0.2.39.dist-info}/RECORD +29 -28
- {opencos_eda-0.2.36.dist-info → opencos_eda-0.2.39.dist-info}/WHEEL +0 -0
- {opencos_eda-0.2.36.dist-info → opencos_eda-0.2.39.dist-info}/entry_points.txt +0 -0
- {opencos_eda-0.2.36.dist-info → opencos_eda-0.2.39.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.2.36.dist-info → opencos_eda-0.2.39.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.2.36.dist-info → opencos_eda-0.2.39.dist-info}/top_level.txt +0 -0
opencos/eda_config_defaults.yml
CHANGED
|
@@ -3,6 +3,28 @@
|
|
|
3
3
|
# python mergedeep with Strategy.TYPESAFE_REPLACE. I considered doing TYPESAFE_ADDITIVE but then
|
|
4
4
|
# the user would lose full control over existing list values.
|
|
5
5
|
|
|
6
|
+
DEFAULT_HANDLERS:
|
|
7
|
+
# These commands (sim, elab, etc) require a tool, but have a default handler
|
|
8
|
+
# base class:
|
|
9
|
+
sim : opencos.commands.CommandSim
|
|
10
|
+
elab : opencos.commands.CommandElab
|
|
11
|
+
synth : opencos.commands.CommandSynth
|
|
12
|
+
flist : opencos.commands.CommandFList
|
|
13
|
+
proj : opencos.commands.CommandProj
|
|
14
|
+
build : opencos.commands.CommandBuild
|
|
15
|
+
upload : opencos.commands.CommandUpload
|
|
16
|
+
open : opencos.commands.CommandOpen
|
|
17
|
+
# These commands don't necessarily require a tool
|
|
18
|
+
multi : opencos.commands.CommandMulti
|
|
19
|
+
tools-multi : opencos.commands.CommandToolsMulti
|
|
20
|
+
sweep : opencos.commands.CommandSweep
|
|
21
|
+
# These commands (waves, export, targets) do not require a tool, or
|
|
22
|
+
# will self determine the tool:
|
|
23
|
+
waves : opencos.commands.CommandWaves
|
|
24
|
+
export : opencos.commands.CommandExport
|
|
25
|
+
targets : opencos.commands.CommandTargets
|
|
26
|
+
|
|
27
|
+
|
|
6
28
|
defines: { } # Add these defines to every eda call
|
|
7
29
|
|
|
8
30
|
dep_command_enables:
|
|
@@ -47,9 +69,14 @@ file_extensions:
|
|
|
47
69
|
- .vhdl
|
|
48
70
|
|
|
49
71
|
command_determines_tool:
|
|
50
|
-
# eda commands that will self-determine the tool to use
|
|
72
|
+
# eda commands that will self-determine the tool to use
|
|
51
73
|
- waves
|
|
52
74
|
|
|
75
|
+
command_uses_no_tools:
|
|
76
|
+
# eda commands that do not use a tool at all, will skip auto_tools_order
|
|
77
|
+
- export
|
|
78
|
+
- targets
|
|
79
|
+
|
|
53
80
|
|
|
54
81
|
tools:
|
|
55
82
|
|
opencos/eda_config_reduced.yml
CHANGED
|
@@ -35,7 +35,7 @@ _eda_script_completion() {
|
|
|
35
35
|
if [ -z "${completions}" ]; then
|
|
36
36
|
# If we didn't find anything in a DEPS.[yml|yaml|toml|json], then use:
|
|
37
37
|
# 1. a bunch of known eda words or args.
|
|
38
|
-
eda_words="multi sim elab flist build synth waves proj waves \
|
|
38
|
+
eda_words="multi sim elab flist build synth waves proj waves targets \
|
|
39
39
|
+define+ +incdirs+ \
|
|
40
40
|
--help --quiet --verbose --debug \
|
|
41
41
|
--tool --seed --top --keep --force --fake --lint --work-dir \
|
opencos/eda_extract_targets.py
CHANGED
|
@@ -72,12 +72,8 @@ def print_columns_manual(data: list, num_columns: int = 4, auto_columns: bool =
|
|
|
72
72
|
if col_index == num_columns - 1 or i == len(data) - 1:
|
|
73
73
|
print() # New line at the end of a row or end of data
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
'''Returns None, prints DEPS keys into pretty columns, using arg
|
|
78
|
-
|
|
79
|
-
partial_path as a string filter for target completions.
|
|
80
|
-
'''
|
|
75
|
+
def get_path_and_pattern(partial_path: str = '', base_path=str(Path('.'))) -> (str, str):
|
|
76
|
+
'''Returns tuple of (partial_path, partial_target or filter)'''
|
|
81
77
|
partial_target = ''
|
|
82
78
|
if not partial_path or partial_path == str(Path('.')):
|
|
83
79
|
partial_path = PATH_LPREFIX
|
|
@@ -86,18 +82,40 @@ def run(partial_path: str = '', base_path=str(Path('.'))) -> None:
|
|
|
86
82
|
if not partial_path:
|
|
87
83
|
partial_path = PATH_LPREFIX
|
|
88
84
|
|
|
89
|
-
|
|
90
|
-
keys = get_all_targets(
|
|
91
|
-
dirs=[partial_path],
|
|
92
|
-
base_path=base_path,
|
|
93
|
-
filter_str=partial_target,
|
|
94
|
-
error_on_empty_return=False,
|
|
95
|
-
lstrip_path=True
|
|
96
|
-
)
|
|
97
|
-
except:
|
|
98
|
-
keys = []
|
|
85
|
+
return partial_path, partial_target
|
|
99
86
|
|
|
100
|
-
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def run(partial_paths: list, base_path=str(Path('.'))) -> None:
|
|
90
|
+
'''Returns None, prints DEPS keys into pretty columns, using arg
|
|
91
|
+
|
|
92
|
+
partial_path as a string filter for target completions.
|
|
93
|
+
'''
|
|
94
|
+
|
|
95
|
+
targets_set = set()
|
|
96
|
+
if not partial_paths:
|
|
97
|
+
partial_paths = [PATH_LPREFIX] # run on current directory.
|
|
98
|
+
|
|
99
|
+
for partial_path in partial_paths:
|
|
100
|
+
partial_path, partial_target = get_path_and_pattern(
|
|
101
|
+
partial_path=partial_path, base_path=base_path
|
|
102
|
+
)
|
|
103
|
+
try:
|
|
104
|
+
keys = get_all_targets(
|
|
105
|
+
dirs=[partial_path],
|
|
106
|
+
base_path=base_path,
|
|
107
|
+
filter_str=partial_target,
|
|
108
|
+
error_on_empty_return=False,
|
|
109
|
+
lstrip_path=True
|
|
110
|
+
)
|
|
111
|
+
except:
|
|
112
|
+
keys = []
|
|
113
|
+
for key in keys:
|
|
114
|
+
targets_set.add(key)
|
|
115
|
+
|
|
116
|
+
data = list(targets_set)
|
|
117
|
+
data.sort()
|
|
118
|
+
print_columns_manual(data=data, num_columns=4, auto_columns=True)
|
|
101
119
|
|
|
102
120
|
|
|
103
121
|
def main() -> None:
|
|
@@ -109,8 +127,8 @@ def main() -> None:
|
|
|
109
127
|
if len(sys.argv) > 1:
|
|
110
128
|
partial_path = sys.argv[1]
|
|
111
129
|
else:
|
|
112
|
-
partial_path =
|
|
113
|
-
run(partial_path)
|
|
130
|
+
partial_path = ''
|
|
131
|
+
run(partial_paths=[partial_path])
|
|
114
132
|
|
|
115
133
|
|
|
116
134
|
if __name__ == "__main__":
|
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/iverilog.py
CHANGED
|
@@ -156,8 +156,10 @@ class CommandSimIverilog(CommandSim, ToolIverilog):
|
|
|
156
156
|
# +define+{k}={v}, but also for SystemVerilog plusargs
|
|
157
157
|
command_list += [ '-D', f'{k}={sanitize_defines_for_sh(v)}' ]
|
|
158
158
|
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
if not self.files_sv and not self.files_v:
|
|
160
|
+
if not self.args['stop-before-compile']:
|
|
161
|
+
self.error(f'{self.target=} {self.files_sv=} and {self.files_v=} are empty,',
|
|
162
|
+
'cannot call iverilog')
|
|
161
163
|
|
|
162
164
|
command_list += list(self.files_sv) + list(self.files_v)
|
|
163
165
|
|
opencos/tools/modelsim_ase.py
CHANGED
|
@@ -178,9 +178,10 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
|
|
|
178
178
|
'-source',
|
|
179
179
|
] + list(self.files_sv) + list(self.files_v)
|
|
180
180
|
|
|
181
|
-
if
|
|
182
|
-
|
|
183
|
-
|
|
181
|
+
if not self.files_sv and not self.files_v:
|
|
182
|
+
if not self.args['stop-before-compile']:
|
|
183
|
+
self.error(f'{self.target=} {self.files_sv=} and {self.files_v=} are empty,',
|
|
184
|
+
'cannot create a valid vlog.f')
|
|
184
185
|
|
|
185
186
|
with open(vlog_dot_f_fpath, 'w', encoding='utf-8') as f:
|
|
186
187
|
f.writelines(line + "\n" for line in vlog_dot_f_lines)
|
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
|
opencos/tools/verilator.py
CHANGED
|
@@ -82,11 +82,12 @@ class VerilatorSim(CommandSim, ToolVerilator):
|
|
|
82
82
|
})
|
|
83
83
|
|
|
84
84
|
self.args_help.update({
|
|
85
|
-
'waves': 'Include waveforms, if possible for Verilator by applying'
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
'dump-vcd': 'If using --waves, apply simulation runtime arg +trace=vcd. User'
|
|
89
|
-
|
|
85
|
+
'waves': ('Include waveforms, if possible for Verilator by applying'
|
|
86
|
+
' simulation runtime arg +trace. User will need SV code to interpret the'
|
|
87
|
+
'plusarg and apply $dumpfile("dump.fst").'),
|
|
88
|
+
'dump-vcd': ('If using --waves, apply simulation runtime arg +trace=vcd. User'
|
|
89
|
+
' will need SV code to interpret the plusarg and apply'
|
|
90
|
+
' $dumpfile("dump.vcd").'),
|
|
90
91
|
'lint-only': 'Run verilator with --lint-only, instead of --binary',
|
|
91
92
|
'gui': 'Not supported for Verilator',
|
|
92
93
|
'cc-mode': 'Run verilator with --cc, requires a sim_main.cpp or similar sources',
|
|
@@ -227,9 +228,10 @@ class VerilatorSim(CommandSim, ToolVerilator):
|
|
|
227
228
|
# +define+{k}={v}, but also for SystemVerilog plusargs
|
|
228
229
|
verilate_command_list += [ f'+define+{k}={sanitize_defines_for_sh(v)}' ]
|
|
229
230
|
|
|
230
|
-
if
|
|
231
|
-
|
|
232
|
-
|
|
231
|
+
if not self.files_sv and not self.files_v:
|
|
232
|
+
if not self.args['stop-before-compile']:
|
|
233
|
+
self.error(f'{self.target=} {self.files_sv=} and {self.files_v=} are empty,',
|
|
234
|
+
'cannot call verilator')
|
|
233
235
|
|
|
234
236
|
verilate_command_list += list(self.files_sv) + list(self.files_v)
|
|
235
237
|
|