opencos-eda 0.2.51__py3-none-any.whl → 0.2.53__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/build.py +1 -1
- opencos/commands/deps_help.py +259 -0
- opencos/commands/export.py +1 -1
- opencos/commands/flist.py +3 -0
- opencos/commands/lec.py +1 -1
- opencos/commands/open.py +2 -0
- opencos/commands/proj.py +1 -1
- opencos/commands/shell.py +1 -1
- opencos/commands/sim.py +32 -8
- opencos/commands/synth.py +1 -1
- opencos/commands/upload.py +3 -0
- opencos/commands/waves.py +13 -2
- opencos/deps/defaults.py +1 -0
- opencos/deps/deps_file.py +30 -4
- opencos/deps/deps_processor.py +72 -2
- opencos/deps_schema.py +3 -0
- opencos/eda.py +66 -29
- opencos/eda_base.py +159 -20
- opencos/eda_config.py +2 -2
- opencos/eda_config_defaults.yml +83 -3
- opencos/eda_extract_targets.py +1 -58
- opencos/tests/helpers.py +16 -0
- opencos/tests/test_eda.py +13 -2
- opencos/tests/test_tools.py +227 -6
- opencos/tools/cocotb.py +492 -0
- opencos/tools/modelsim_ase.py +67 -51
- opencos/tools/quartus.py +638 -0
- opencos/tools/questa.py +167 -88
- opencos/tools/questa_fse.py +10 -0
- opencos/tools/riviera.py +1 -0
- opencos/tools/verilator.py +31 -0
- opencos/tools/vivado.py +3 -3
- opencos/util.py +22 -3
- opencos/utils/str_helpers.py +85 -0
- opencos/utils/vscode_helper.py +47 -0
- {opencos_eda-0.2.51.dist-info → opencos_eda-0.2.53.dist-info}/METADATA +2 -1
- {opencos_eda-0.2.51.dist-info → opencos_eda-0.2.53.dist-info}/RECORD +43 -39
- {opencos_eda-0.2.51.dist-info → opencos_eda-0.2.53.dist-info}/WHEEL +0 -0
- {opencos_eda-0.2.51.dist-info → opencos_eda-0.2.53.dist-info}/entry_points.txt +0 -0
- {opencos_eda-0.2.51.dist-info → opencos_eda-0.2.53.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.2.51.dist-info → opencos_eda-0.2.53.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.2.51.dist-info → opencos_eda-0.2.53.dist-info}/top_level.txt +0 -0
opencos/tools/questa.py
CHANGED
|
@@ -14,7 +14,7 @@ import shutil
|
|
|
14
14
|
|
|
15
15
|
from opencos import util
|
|
16
16
|
from opencos.eda_base import Tool
|
|
17
|
-
from opencos.commands import CommandSim
|
|
17
|
+
from opencos.commands import CommandSim, CommandFList
|
|
18
18
|
|
|
19
19
|
class ToolQuesta(Tool):
|
|
20
20
|
'''Base class for CommandSimQuesta, collects version information about qrun'''
|
|
@@ -23,6 +23,7 @@ class ToolQuesta(Tool):
|
|
|
23
23
|
_EXE = 'qrun'
|
|
24
24
|
|
|
25
25
|
starter_edition = False # Aka, modelsim_ase
|
|
26
|
+
use_vopt = shutil.which('vopt') # vopt exists in qrun/vsim framework, and we'll use it.
|
|
26
27
|
sim_exe = '' # vsim or qrun
|
|
27
28
|
sim_exe_base_path = ''
|
|
28
29
|
questa_major = None
|
|
@@ -74,126 +75,204 @@ class CommandSimQuesta(CommandSim, ToolQuesta):
|
|
|
74
75
|
CommandSim.__init__(self, config)
|
|
75
76
|
ToolQuesta.__init__(self, config=self.config)
|
|
76
77
|
# add args specific to this simulator
|
|
77
|
-
self.args
|
|
78
|
-
|
|
78
|
+
self.args.update({
|
|
79
|
+
'gui': False,
|
|
80
|
+
'tcl-file': 'sim.tcl',
|
|
81
|
+
'work-lib': 'work',
|
|
82
|
+
})
|
|
83
|
+
self.args_help.update({
|
|
84
|
+
'gui': 'Run Questa in GUI mode',
|
|
85
|
+
'tcl-file': 'name of TCL file to be created for Questa simulation',
|
|
86
|
+
'work-lib': 'Questa work library name',
|
|
87
|
+
})
|
|
88
|
+
|
|
79
89
|
self.shell_command = self.sim_exe # set by ToolQuesta.get_versions(self)
|
|
90
|
+
self.vlog_commands = []
|
|
91
|
+
self.vopt_commands = []
|
|
92
|
+
self.vsim_commands = []
|
|
80
93
|
|
|
81
94
|
def set_tool_defines(self):
|
|
82
95
|
ToolQuesta.set_tool_defines(self)
|
|
83
96
|
|
|
84
|
-
|
|
85
|
-
|
|
97
|
+
# We do not override CommandSim.do_it(), CommandSim.check_logs_for_errors(...)
|
|
98
|
+
|
|
99
|
+
def prepare_compile(self):
|
|
86
100
|
self.set_tool_defines()
|
|
101
|
+
self.vlog_commands = self.get_compile_command_lists()
|
|
102
|
+
self.vopt_commands = self.get_elaborate_command_lists()
|
|
103
|
+
self.vsim_commands = self.get_simulate_command_lists()
|
|
104
|
+
|
|
105
|
+
util.write_shell_command_file(dirpath=self.args['work-dir'], filename='compile.sh',
|
|
106
|
+
command_lists=self.vlog_commands)
|
|
107
|
+
util.write_shell_command_file(dirpath=self.args['work-dir'], filename='elaborate.sh',
|
|
108
|
+
command_lists=self.vopt_commands)
|
|
109
|
+
util.write_shell_command_file(dirpath=self.args['work-dir'], filename='simulate.sh',
|
|
110
|
+
command_lists=self.vsim_commands)
|
|
111
|
+
util.write_shell_command_file(dirpath=self.args['work-dir'], filename='all.sh',
|
|
112
|
+
command_lists = [
|
|
113
|
+
['./pre_compile_dep_shell_commands.sh'],
|
|
114
|
+
['./compile.sh'],
|
|
115
|
+
['./elaborate.sh'],
|
|
116
|
+
['./simulate.sh'],
|
|
117
|
+
])
|
|
118
|
+
|
|
87
119
|
self.write_eda_config_and_args()
|
|
88
120
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
#
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
#
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
121
|
+
def compile(self):
|
|
122
|
+
if self.args['stop-before-compile']:
|
|
123
|
+
return
|
|
124
|
+
self.run_commands_check_logs(
|
|
125
|
+
self.vlog_commands, check_logs=True, log_filename='vlog.log'
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
def elaborate(self):
|
|
129
|
+
if self.args['stop-before-compile'] or self.args['stop-after-compile']:
|
|
130
|
+
return
|
|
131
|
+
self.run_commands_check_logs(
|
|
132
|
+
self.vopt_commands, check_logs=True, log_filename='vopt.log'
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
def simulate(self):
|
|
136
|
+
if self.args['stop-before-compile'] or self.args['stop-after-compile'] or \
|
|
137
|
+
self.args['stop-after-elaborate']:
|
|
138
|
+
return
|
|
139
|
+
self.run_commands_check_logs(
|
|
140
|
+
self.vsim_commands, check_logs=True, log_filename='vsim.log'
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
def get_compile_command_lists(self, **kwargs) -> list:
|
|
144
|
+
self.set_tool_defines()
|
|
145
|
+
ret = []
|
|
146
|
+
|
|
147
|
+
# Create work library
|
|
148
|
+
vlib_cmd = [os.path.join(self.sim_exe_base_path, 'vlib'), self.args['work-lib']]
|
|
149
|
+
ret.append(vlib_cmd)
|
|
150
|
+
|
|
151
|
+
# Map work library
|
|
152
|
+
vmap_cmd = [os.path.join(self.sim_exe_base_path, 'vmap'), 'work', self.args['work-lib']]
|
|
153
|
+
ret.append(vmap_cmd)
|
|
154
|
+
|
|
155
|
+
# Compile files
|
|
156
|
+
if self.files_v or self.files_sv:
|
|
157
|
+
vlog_cmd = [os.path.join(self.sim_exe_base_path, 'vlog'), '-64', '-sv']
|
|
158
|
+
|
|
159
|
+
# Add include directories
|
|
160
|
+
for incdir in self.incdirs:
|
|
161
|
+
vlog_cmd += [f'+incdir+{incdir}']
|
|
162
|
+
|
|
163
|
+
# Add defines
|
|
164
|
+
for key, value in self.defines.items():
|
|
165
|
+
if value is None:
|
|
166
|
+
vlog_cmd += [f'+define+{key}']
|
|
167
|
+
else:
|
|
168
|
+
vlog_cmd += [f'+define+{key}={value}']
|
|
169
|
+
|
|
170
|
+
# Add suppression flags
|
|
171
|
+
vlog_cmd += [
|
|
172
|
+
'-svinputport=net',
|
|
173
|
+
'-suppress', 'vlog-2275',
|
|
174
|
+
'-suppress', 'vlog-2583',
|
|
175
|
+
]
|
|
176
|
+
|
|
177
|
+
# Add source files
|
|
178
|
+
vlog_cmd += self.files_v + self.files_sv
|
|
179
|
+
|
|
180
|
+
ret.append(vlog_cmd)
|
|
181
|
+
|
|
182
|
+
# Compile VHDL files if any
|
|
183
|
+
if self.files_vhd:
|
|
184
|
+
vcom_cmd = [os.path.join(self.sim_exe_base_path, 'vcom'), '-64']
|
|
185
|
+
vcom_cmd += self.files_vhd
|
|
186
|
+
ret.append(vcom_cmd)
|
|
187
|
+
|
|
188
|
+
return ret
|
|
189
|
+
|
|
190
|
+
def get_elaborate_command_lists(self, **kwargs) -> list:
|
|
191
|
+
if self.args['stop-after-compile']:
|
|
192
|
+
return []
|
|
193
|
+
|
|
194
|
+
vopt_cmd = [os.path.join(self.sim_exe_base_path, 'vopt'), '-64']
|
|
195
|
+
|
|
196
|
+
# Add optimization flags
|
|
197
|
+
vopt_cmd += [
|
|
133
198
|
'-suppress', 'vopt-13159',
|
|
134
|
-
# Too few port connections for 'uAW_FIFO'. Expected 10, found 8
|
|
135
199
|
'-suppress', 'vopt-2685',
|
|
136
|
-
# Missing connection for port 'almostEmpty' ... same message for inputs and outputs.
|
|
137
200
|
'-note', 'vopt-2718',
|
|
138
201
|
]
|
|
139
|
-
if self.args['gui']:
|
|
140
|
-
command_list += ['-gui=interactive', '+acc', '-i']
|
|
141
|
-
elif self.args['waves']:
|
|
142
|
-
command_list += ['+acc', '-c']
|
|
143
|
-
else:
|
|
144
|
-
command_list += ['-c']
|
|
145
202
|
|
|
146
|
-
if
|
|
147
|
-
|
|
203
|
+
if self.args['gui'] or self.args['waves']:
|
|
204
|
+
vopt_cmd += ['+acc']
|
|
148
205
|
|
|
149
|
-
#
|
|
150
|
-
|
|
151
|
-
# To get this to work, need to add this, among other items:
|
|
152
|
-
# --> command_list += '''-L xil_defaultlib -L unisims_ver -L unimacro_ver
|
|
153
|
-
# -L xpm -L secureip -L xilinx_vip'''.split(" ")
|
|
206
|
+
# Add top module and output
|
|
207
|
+
vopt_cmd += [self.args['top'], '-o', 'opt_design']
|
|
154
208
|
|
|
155
|
-
|
|
156
|
-
if self.args['stop-after-elaborate']:
|
|
157
|
-
command_list += ['-elab', 'elab.output', '-do', '"quit"' ]
|
|
209
|
+
return [vopt_cmd]
|
|
158
210
|
|
|
159
|
-
|
|
211
|
+
def get_simulate_command_lists(self, **kwargs) -> list:
|
|
212
|
+
# Create TCL file
|
|
160
213
|
tcl_name = os.path.abspath(os.path.join(self.args['work-dir'], self.args['tcl-file']))
|
|
161
|
-
|
|
214
|
+
|
|
215
|
+
if self.args['waves']:
|
|
216
|
+
util.artifacts.add_extension(
|
|
217
|
+
search_paths=self.args['work-dir'], file_extension='wlf',
|
|
218
|
+
typ='waveform', description='Questa Waveform WLF file'
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
with open(tcl_name, 'w', encoding='utf-8') as fo:
|
|
162
222
|
if self.args['waves']:
|
|
163
223
|
if self.args['waves-start']:
|
|
164
224
|
print(f"run {self.args['waves-start']} ns", file=fo)
|
|
165
225
|
print("add wave -r /*", file=fo)
|
|
166
226
|
print("run -all", file=fo)
|
|
167
|
-
if
|
|
227
|
+
if self.run_in_batch_mode():
|
|
168
228
|
print("quit", file=fo)
|
|
169
|
-
command_list += ['-do', tcl_name ]
|
|
170
229
|
|
|
171
|
-
#
|
|
172
|
-
|
|
230
|
+
# Create vsim command
|
|
231
|
+
vsim_cmd = [os.path.join(self.sim_exe_base_path, 'vsim'), '-64']
|
|
173
232
|
|
|
174
|
-
|
|
175
|
-
|
|
233
|
+
if not self.run_in_batch_mode():
|
|
234
|
+
vsim_cmd += ['-gui']
|
|
235
|
+
else:
|
|
236
|
+
vsim_cmd += ['-c']
|
|
176
237
|
|
|
177
|
-
|
|
178
|
-
|
|
238
|
+
if util.args['verbose']:
|
|
239
|
+
vsim_cmd += ['-verbose']
|
|
179
240
|
|
|
180
|
-
|
|
181
|
-
|
|
241
|
+
# Add simulation arguments
|
|
242
|
+
vsim_cmd += ['-do', tcl_name, 'opt_design']
|
|
182
243
|
|
|
183
|
-
|
|
184
|
-
pass
|
|
244
|
+
return [vsim_cmd]
|
|
185
245
|
|
|
186
|
-
def
|
|
246
|
+
def get_post_simulate_command_lists(self, **kwargs) -> list:
|
|
187
247
|
return []
|
|
188
248
|
|
|
189
|
-
def
|
|
190
|
-
|
|
249
|
+
def run_in_batch_mode(self) -> bool:
|
|
250
|
+
'''Returns bool if we should run in batch mode (-c) from command line'''
|
|
251
|
+
if self.args['test-mode']:
|
|
252
|
+
return True
|
|
253
|
+
if self.args['gui']:
|
|
254
|
+
return False
|
|
255
|
+
return True
|
|
191
256
|
|
|
192
|
-
def
|
|
193
|
-
|
|
257
|
+
def artifacts_add(self, name: str, typ: str, description: str) -> None:
|
|
258
|
+
'''Override from Command.artifacts_add for better descriptions'''
|
|
259
|
+
_, leafname = os.path.split(name)
|
|
260
|
+
if leafname == 'vsim.log':
|
|
261
|
+
description = 'Questa simulation step (3/3) log from stdout/stderr'
|
|
262
|
+
elif leafname == 'vopt.log':
|
|
263
|
+
description = 'Questa elaboration step (2/3) log from stdout/stderr'
|
|
264
|
+
elif leafname == 'vlog.log':
|
|
265
|
+
description = 'Questa compile step (1/3) log from stdout/stderr'
|
|
194
266
|
|
|
195
|
-
|
|
196
|
-
|
|
267
|
+
super().artifacts_add(name=name, typ=typ, description=description)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
class CommandFListQuesta(CommandFList, ToolQuesta):
|
|
271
|
+
'''CommandFListQuesta is a command handler for: eda flist --tool=questa'''
|
|
272
|
+
|
|
273
|
+
def __init__(self, config: dict):
|
|
274
|
+
CommandFList.__init__(self, config=config)
|
|
275
|
+
ToolQuesta.__init__(self, config=self.config)
|
|
197
276
|
|
|
198
277
|
|
|
199
278
|
class CommandElabQuesta(CommandSimQuesta):
|
opencos/tools/questa_fse.py
CHANGED
|
@@ -11,6 +11,7 @@ For: Questa Intel Starter FPGA Edition-64 vsim 20XX.X Simulator
|
|
|
11
11
|
import os
|
|
12
12
|
|
|
13
13
|
from opencos.tools.modelsim_ase import CommandSimModelsimAse
|
|
14
|
+
from opencos.tools.questa import CommandFListQuesta
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class CommandSimQuestaFse(CommandSimModelsimAse):
|
|
@@ -20,6 +21,7 @@ class CommandSimQuestaFse(CommandSimModelsimAse):
|
|
|
20
21
|
'''
|
|
21
22
|
_TOOL = 'questa_fse'
|
|
22
23
|
_EXE = 'vsim'
|
|
24
|
+
use_vopt = True
|
|
23
25
|
|
|
24
26
|
def __init__(self, config: dict):
|
|
25
27
|
# this will setup with self._TOOL = modelsim_ase, which is not ideal so
|
|
@@ -57,3 +59,11 @@ class CommandElabQuestaFse(CommandSimQuestaFse):
|
|
|
57
59
|
def __init__(self, config:dict):
|
|
58
60
|
super().__init__(config)
|
|
59
61
|
self.args['stop-after-elaborate'] = True
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class CommandFListQuestaFse(CommandFListQuesta):
|
|
65
|
+
'''CommandFListQuestaFse is a command handler for: eda flist --tool=questa_fse'''
|
|
66
|
+
|
|
67
|
+
def __init__(self, config: dict):
|
|
68
|
+
CommandFListQuesta.__init__(self, config=config)
|
|
69
|
+
self._TOOL = 'questa_fse'
|
opencos/tools/riviera.py
CHANGED
opencos/tools/verilator.py
CHANGED
|
@@ -79,6 +79,8 @@ class VerilatorSim(CommandSim, ToolVerilator):
|
|
|
79
79
|
'lint-only': False,
|
|
80
80
|
'cc-mode': False,
|
|
81
81
|
'verilator-coverage-args': [],
|
|
82
|
+
'x-assign': '',
|
|
83
|
+
'x-initial': '',
|
|
82
84
|
})
|
|
83
85
|
|
|
84
86
|
self.args_help.update({
|
|
@@ -92,6 +94,14 @@ class VerilatorSim(CommandSim, ToolVerilator):
|
|
|
92
94
|
'gui': 'Not supported for Verilator',
|
|
93
95
|
'cc-mode': 'Run verilator with --cc, requires a sim_main.cpp or similar sources',
|
|
94
96
|
'optimize': 'Run verilator with: -CLAGS -O3, if no other CFLAGS args are presented',
|
|
97
|
+
'x-assign': ('String value to added to verilator call: --x-assign <string>;'
|
|
98
|
+
' where valid string values are: 0, 1, unique, fast.'
|
|
99
|
+
' Also conditinally adds to verilated exe call:'
|
|
100
|
+
' +verilator+rand+reset+[0,1,2] for arg values 0, 1, unique|fast'),
|
|
101
|
+
'x-initial': ('String value to added to verilator call: --x-initial <string>;'
|
|
102
|
+
' where valid string values are: 0, unique, fast.'
|
|
103
|
+
' Also conditinally adds to verilated exe call:'
|
|
104
|
+
' +verilator+rand+reset+[0,2] for arg values 0, unique|fast'),
|
|
95
105
|
})
|
|
96
106
|
|
|
97
107
|
self.verilate_command_lists = []
|
|
@@ -219,6 +229,14 @@ class VerilatorSim(CommandSim, ToolVerilator):
|
|
|
219
229
|
'-o', 'sim.exe',
|
|
220
230
|
]
|
|
221
231
|
|
|
232
|
+
for arg in ('x-assign', 'x-initial'):
|
|
233
|
+
if self.args[arg] and f'--{arg}' not in verilate_command_list:
|
|
234
|
+
# Only add this if arg is set, and not present in verilator call
|
|
235
|
+
# this takes care of it being in our self.tool_config for compile-args.
|
|
236
|
+
verilate_command_list += [
|
|
237
|
+
f'--{arg}', self.args[arg]
|
|
238
|
+
]
|
|
239
|
+
|
|
222
240
|
# incdirs
|
|
223
241
|
for value in self.incdirs:
|
|
224
242
|
verilate_command_list += [ f"+incdir+{value}" ]
|
|
@@ -295,6 +313,19 @@ class VerilatorSim(CommandSim, ToolVerilator):
|
|
|
295
313
|
if not any(x.startswith('+verilator+seed+') for x in verilated_exec_command_list):
|
|
296
314
|
verilated_exec_command_list.append(f'+verilator+seed+{verilator_seed}')
|
|
297
315
|
|
|
316
|
+
if any(self.args[arg] in ('unique', 'fast') for arg in ('x-assign', 'x-initial')) and \
|
|
317
|
+
not any(x.startswith('+verilator+rand+reset') for x in verilated_exec_command_list):
|
|
318
|
+
# Only add this if arg is one of x-assign/x-initial is set to "unique" or "fast",
|
|
319
|
+
# we use the encoded value "2" for +verilator+rand+reset+2
|
|
320
|
+
verilated_exec_command_list.append('+verilator+rand+reset+2')
|
|
321
|
+
|
|
322
|
+
if self.args['x-assign'] == '1' and \
|
|
323
|
+
not any(x.startswith('+verilator+rand+reset') for x in verilated_exec_command_list):
|
|
324
|
+
# Only add this if --x-assign=1 (not valid for --x-initial),
|
|
325
|
+
# we use the encoded value "1" for +verilator+rand+reset+1
|
|
326
|
+
verilated_exec_command_list.append('+verilator+rand+reset+1')
|
|
327
|
+
|
|
328
|
+
|
|
298
329
|
return [
|
|
299
330
|
util.ShellCommandList(verilated_exec_command_list, tee_fpath='sim.log')
|
|
300
331
|
] # single entry list
|
opencos/tools/vivado.py
CHANGED
|
@@ -259,7 +259,7 @@ class CommandSimVivado(CommandSim, ToolVivado):
|
|
|
259
259
|
print(f"run {self.args['waves-start']} ns", file=fo)
|
|
260
260
|
print("log_wave -recursive *", file=fo)
|
|
261
261
|
print("run -all", file=fo)
|
|
262
|
-
if not self.args['gui']:
|
|
262
|
+
if not self.args['gui'] or self.args['test-mode']:
|
|
263
263
|
print("exit", file=fo)
|
|
264
264
|
|
|
265
265
|
sv_seed = str(self.args['seed'])
|
|
@@ -280,7 +280,7 @@ class CommandSimVivado(CommandSim, ToolVivado):
|
|
|
280
280
|
if sys.platform == "win32":
|
|
281
281
|
command_list[0] += ".bat"
|
|
282
282
|
command_list += self.tool_config.get('simulate-args', 'snapshot --stats').split()
|
|
283
|
-
if self.args['gui']:
|
|
283
|
+
if self.args['gui'] and not self.args['test-mode']:
|
|
284
284
|
command_list += ['-gui']
|
|
285
285
|
command_list += [
|
|
286
286
|
'--tclbatch', tcl_name.replace('\\','\\\\'), # needed for windows paths
|
|
@@ -701,7 +701,7 @@ class CommandBuildVivado(CommandBuild, ToolVivado):
|
|
|
701
701
|
command_list = [self.vivado_exe]
|
|
702
702
|
command_list += [
|
|
703
703
|
'-mode',
|
|
704
|
-
'gui' if self.args['gui'] else 'batch',
|
|
704
|
+
'gui' if self.args['gui'] and not self.args['test-mode'] else 'batch',
|
|
705
705
|
'-log', os.path.join(self.args['work-dir'], self.args['top'] + '.build.log')
|
|
706
706
|
]
|
|
707
707
|
if not util.args['verbose']:
|
opencos/util.py
CHANGED
|
@@ -224,16 +224,17 @@ class UtilLogger:
|
|
|
224
224
|
# util's argparser: --no-default-log, --logfile=<name>, or --force-logfile=<name>
|
|
225
225
|
default_log_enabled = False
|
|
226
226
|
default_log_filepath = os.path.join('eda.work', 'eda.log')
|
|
227
|
+
default_log_disable_with_args = ['-h', '--help'] # common argparse help
|
|
227
228
|
enable = True
|
|
228
229
|
|
|
229
230
|
def clear(self) -> None:
|
|
230
|
-
'''Resets internals'''
|
|
231
|
+
'''Resets some internals for logging, but preserve others'''
|
|
231
232
|
self.file = None
|
|
232
233
|
self.filepath = ''
|
|
233
234
|
self.time_last = 0
|
|
234
235
|
|
|
235
236
|
def stop(self) -> None:
|
|
236
|
-
'''Closes open log, resets internals'''
|
|
237
|
+
'''Closes open log, resets some internals'''
|
|
237
238
|
if self.file:
|
|
238
239
|
self.write_timestamp(f'stop - {self.filepath}')
|
|
239
240
|
info(f"Closing logfile: {self.filepath}")
|
|
@@ -367,6 +368,21 @@ def get_argparser() -> argparse.ArgumentParser:
|
|
|
367
368
|
return parser
|
|
368
369
|
|
|
369
370
|
|
|
371
|
+
def get_argparser_args_list(parser: object = None) -> list:
|
|
372
|
+
'''Returns list of all args, all items will include the -- prefix (--help, etc)'''
|
|
373
|
+
if not isinstance(parser, argparse.ArgumentParser):
|
|
374
|
+
return []
|
|
375
|
+
return list(vars(parser).get('_option_string_actions', {}).keys())
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def get_argparsers_args_list(parsers: list) -> list:
|
|
379
|
+
'''Returns list of all args from list of parsers, all items have -- prefix (--help, etc)'''
|
|
380
|
+
_args_list = []
|
|
381
|
+
for parser in parsers:
|
|
382
|
+
_args_list.extend(get_argparser_args_list(parser))
|
|
383
|
+
return list(dict.fromkeys(_args_list)) # uniqify, preserve order
|
|
384
|
+
|
|
385
|
+
|
|
370
386
|
def get_argparser_short_help(parser: object = None) -> str:
|
|
371
387
|
'''Returns short help for our ArgumentParser'''
|
|
372
388
|
if not parser:
|
|
@@ -420,15 +436,18 @@ def process_tokens(tokens:list) -> (argparse.Namespace, list):
|
|
|
420
436
|
# set artifacts.enabled based on args['artifacts-json']
|
|
421
437
|
artifacts.reset(enable=parsed.artifacts_json)
|
|
422
438
|
|
|
439
|
+
|
|
423
440
|
if parsed.force_logfile:
|
|
424
441
|
start_log(parsed.force_logfile, force=True)
|
|
425
442
|
elif parsed.logfile:
|
|
426
443
|
start_log(parsed.logfile, force=False)
|
|
427
444
|
elif parsed.default_log and \
|
|
428
|
-
not
|
|
445
|
+
not parsed.version and \
|
|
446
|
+
not any(x in unparsed for x in global_log.default_log_disable_with_args) and \
|
|
429
447
|
(parsed.force_logfile is None and parsed.logfile is None):
|
|
430
448
|
# Use a forced logfile in the eda.work/eda.log:
|
|
431
449
|
# avoid this if someone has --help arg not yet parsed.
|
|
450
|
+
# avoid this if someone called --version, b/c that will print and exit.
|
|
432
451
|
start_log(global_log.default_log_filepath, force=True)
|
|
433
452
|
|
|
434
453
|
parsed_as_dict = vars(parsed)
|
opencos/utils/str_helpers.py
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
'''opencos.utils.str_helpers -- Various str helpers for printing, indenting'''
|
|
2
2
|
|
|
3
3
|
import fnmatch
|
|
4
|
+
import os
|
|
4
5
|
import re
|
|
5
6
|
import shlex
|
|
6
7
|
import textwrap
|
|
7
8
|
|
|
9
|
+
VALID_TARGET_INFO_STR = (
|
|
10
|
+
"should start with an underscore/letter, rest should be alpha-numeric, dashes, or underscores."
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
def is_valid_target_name(s: str) -> bool:
|
|
14
|
+
'''Returns True if str starts with underscore/letter, rest alphanum, dash, or underscores'''
|
|
15
|
+
if not s or not (s[0].isalpha() or s[0] == '_'):
|
|
16
|
+
return False
|
|
17
|
+
return s.replace('_', '').replace('-', '').isalnum()
|
|
18
|
+
|
|
8
19
|
def strip_all_quotes(s: str) -> str:
|
|
9
20
|
'''Returns str with all ' and " removed'''
|
|
10
21
|
return s.replace("'", '').replace('"', '')
|
|
@@ -109,3 +120,77 @@ def fnmatch_or_re(pattern: str, string: str) -> bool:
|
|
|
109
120
|
# could have been an illegal/unsupported regex, so don't match.
|
|
110
121
|
pass
|
|
111
122
|
return any(matches)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def get_terminal_columns():
|
|
126
|
+
"""
|
|
127
|
+
Retrieves the number of columns (width) of the terminal window.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
int: The number of columns in the terminal, or a default value (e.g., 80)
|
|
131
|
+
if the terminal size cannot be determined.
|
|
132
|
+
"""
|
|
133
|
+
try:
|
|
134
|
+
size = os.get_terminal_size()
|
|
135
|
+
return size.columns
|
|
136
|
+
except OSError:
|
|
137
|
+
# Handle cases where the terminal size cannot be determined (e.g., not in a TTY)
|
|
138
|
+
return 80 # Default to 80 columns
|
|
139
|
+
|
|
140
|
+
return 80 # else default to 80.
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def pretty_list_columns_manual(data: list, num_columns: int = 4, auto_columns: bool = True) -> list:
|
|
144
|
+
"""Returns list, from list of str, organized into columns, manually aligning them."""
|
|
145
|
+
|
|
146
|
+
ret_lines = []
|
|
147
|
+
if not data:
|
|
148
|
+
return ret_lines
|
|
149
|
+
|
|
150
|
+
_spacing = 2
|
|
151
|
+
|
|
152
|
+
# Calculate maximum width for each column
|
|
153
|
+
max_lengths = [0] * num_columns
|
|
154
|
+
max_item_len = 0
|
|
155
|
+
for i, item in enumerate(data):
|
|
156
|
+
col_index = i % num_columns
|
|
157
|
+
max_lengths[col_index] = max(max_lengths[col_index], len(item))
|
|
158
|
+
max_item_len = max(max_item_len, len(item))
|
|
159
|
+
|
|
160
|
+
if auto_columns and num_columns > 1:
|
|
161
|
+
window_cols = get_terminal_columns()
|
|
162
|
+
max_line_len = 0
|
|
163
|
+
for x in max_lengths:
|
|
164
|
+
max_line_len += x + _spacing
|
|
165
|
+
if max_line_len > window_cols:
|
|
166
|
+
# subtract a column (already >= 2):
|
|
167
|
+
ret_lines.extend(
|
|
168
|
+
pretty_list_columns_manual(data=data, num_columns=num_columns-1, auto_columns=True)
|
|
169
|
+
)
|
|
170
|
+
return ret_lines
|
|
171
|
+
if max_line_len + max_item_len + _spacing < window_cols:
|
|
172
|
+
# add 1 more column if we're guaranteed to have room.
|
|
173
|
+
ret_lines.extend(
|
|
174
|
+
pretty_list_columns_manual(data=data, num_columns=num_columns+1, auto_columns=True)
|
|
175
|
+
)
|
|
176
|
+
return ret_lines
|
|
177
|
+
# else continue
|
|
178
|
+
|
|
179
|
+
# Print data in columns
|
|
180
|
+
ret_lines.append('')
|
|
181
|
+
for i, item in enumerate(data):
|
|
182
|
+
col_index = i % num_columns
|
|
183
|
+
ret_lines[-1] += str(item).ljust(max_lengths[col_index] + _spacing)
|
|
184
|
+
if col_index == num_columns - 1 or i == len(data) - 1:
|
|
185
|
+
ret_lines.append('')
|
|
186
|
+
|
|
187
|
+
return ret_lines
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def print_columns_manual(data: list, num_columns: int = 4, auto_columns: bool = True) -> None:
|
|
191
|
+
"""Prints a list of strings in columns, manually aligning them."""
|
|
192
|
+
|
|
193
|
+
lines = pretty_list_columns_manual(
|
|
194
|
+
data=data, num_columns=num_columns, auto_columns=auto_columns
|
|
195
|
+
)
|
|
196
|
+
print('\n'.join(lines))
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
'''Because > 1 tools use the exe 'code', I don't want to run
|
|
2
|
+
|
|
3
|
+
`code --list-extensions --show-versionsvsim -version` for every tool that needs
|
|
4
|
+
to check if they exist in VScode land.
|
|
5
|
+
|
|
6
|
+
Instead, eda.py can call this once, and then query if the VScode extension exists when
|
|
7
|
+
running opencos.eda.auto_tool_setup(..)
|
|
8
|
+
'''
|
|
9
|
+
|
|
10
|
+
import shutil
|
|
11
|
+
import subprocess
|
|
12
|
+
|
|
13
|
+
from opencos.util import debug
|
|
14
|
+
|
|
15
|
+
vscode_path = shutil.which('code')
|
|
16
|
+
|
|
17
|
+
INIT_HAS_RUN = False
|
|
18
|
+
EXTENSIONS = {} # dict of {name: version} for VScode extensions of name
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def init() -> None:
|
|
22
|
+
'''Sets INIT_HAS_RUN=True (only runs once) and one of TOOL_IS[tool] = True'''
|
|
23
|
+
global INIT_HAS_RUN # pylint: disable=global-statement
|
|
24
|
+
|
|
25
|
+
if INIT_HAS_RUN:
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
INIT_HAS_RUN = True
|
|
29
|
+
|
|
30
|
+
if not vscode_path:
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
proc = None
|
|
34
|
+
try:
|
|
35
|
+
proc = subprocess.run([vscode_path, '--list-extensions', '--show-versions'],
|
|
36
|
+
capture_output=True, check=False)
|
|
37
|
+
except Exception as e:
|
|
38
|
+
debug(f'vscode --list-extensions --show-versions: exception {e}')
|
|
39
|
+
|
|
40
|
+
if proc is None or proc.returncode != 0:
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
for line in proc.stdout.decode('utf-8', errors='replace').split('\n'):
|
|
44
|
+
if '@' in line:
|
|
45
|
+
parts = line.split('@')
|
|
46
|
+
if parts[0] and parts[1]:
|
|
47
|
+
EXTENSIONS[parts[0]] = parts[1]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opencos-eda
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.53
|
|
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
|
|
@@ -15,4 +15,5 @@ Requires-Dist: schema>=0.7.7
|
|
|
15
15
|
Requires-Dist: toml>=0.10.2
|
|
16
16
|
Requires-Dist: yamllint>=1.35.1
|
|
17
17
|
Requires-Dist: PySerial>=3.5
|
|
18
|
+
Requires-Dist: cocotb>=2.0.0b1
|
|
18
19
|
Dynamic: license-file
|