-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconfig_generator.py
391 lines (320 loc) · 11.6 KB
/
config_generator.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
"""
This script automates the generation of processor configurations and Jenkinsfiles for FPGA projects.
It includes the following functionality:
- Cloning processor repositories and analyzing their files.
- Extracting hardware modules and testbench files from the repository.
- Building module dependency graphs.
- Generating configuration files for the processor.
- Generating Jenkinsfiles for CI/CD pipelines targeting multiple FPGAs.
- Optionally adding generated configurations to a central configuration file or plotting module
graphs.
Modules and Functions:
----------------------
- **get_top_module_file**: Retrieves the file path of a specific top module.
- **copy_hardware_template**: Copies a hardware template file, naming it after the repository.
- **generate_processor_config**: Clones a repository, analyzes it, and generates a configuration
for the processor.
- **generate_all_pipelines**: Generates Jenkinsfiles for all processors defined in the configuration
file.
- **main**: Parses command-line arguments and triggers the appropriate operations.
Command-Line Interface:
-----------------------
- `-j`, `--generate-all-jenkinsfiles`: Generates Jenkinsfiles for processors.
- `-c`, `--generate-config`: Generates a configuration for a specified processor.
- `-g`, `--plot-graph`: Plots the module dependency graph for the generated configuration.
- `-a`, `--add-config`: Adds the generated configuration to the central config file.
- `-p`, `--path-config`: Specifies the path to the config file (default: `config.json`).
- `-u`, `--processor-url`: Specifies the URL of the processor repository to clone.
Constants:
----------
- **EXTENSIONS**: Supported file extensions (`['v', 'sv', 'vhdl', 'vhd']`).
- **BASE_DIR**: Base directory for storing Jenkinsfiles.
- **FPGAs**: List of supported FPGAs for Jenkinsfile generation.
- **DESTINATION_DIR**: Temporary directory for processing repositories.
- **MAIN_SCRIPT_PATH**: Path to the main synthesis script used in Jenkinsfiles.
Usage:
------
1. To generate a processor configuration:
```python
python script.py -c -u <processor_url>
```
2. To generate all Jenkinsfiles:
```python
python script.py -j
```
3. For help:
```python
python script.py --help
```
Dependencies:
-------------
- `os`, `time`, `json`, `shutil`, `argparse`: Standard Python libraries.
- **Custom Modules**:
- `core.config`: Handles loading and saving configuration files.
- `core.file_manager`: Provides utilities for cloning repositories, finding files, and
extracting modules.
- `core.graph`: Builds and visualizes module dependency graphs.
- `core.jenkins`: Generates Jenkinsfiles.
- `core.ollama`: Filters files and identifies top modules.
"""
import os
import time
import json
import shutil
import argparse
from core.config import load_config, get_processor_data, save_config
from core.file_manager import (
clone_repo,
remove_repo,
find_files_with_extension,
extract_modules,
is_testbench_file,
find_include_dirs,
)
from core.graph import build_module_graph
from core.ollama import get_filtered_files_list, get_top_module
from core.jenkins import generate_jenkinsfile
EXTENSIONS = ['v', 'sv', 'vhdl', 'vhd']
BASE_DIR = 'jenkins_pipeline/'
FPGAs = [
'colorlight_i9',
'digilent_nexys4_ddr',
# "gowin_tangnano_20k",
# "xilinx_vc709",
# "digilent_arty_a7_100t"
]
DESTINATION_DIR = './temp'
MAIN_SCRIPT_PATH = '/eda/processor-ci/main.py'
def get_top_module_file(modules: list[dict[str, str]], top_module: str) -> str:
"""
Retrieves the file path of the specified top module from a list of module dictionaries.
Args:
modules (list[dict[str, str]]): A list of dictionaries where each dictionary
contains the module name and its file path.
top_module (str): The name of the top module to find.
Returns:
str: The file path of the top module if found, or an empty string otherwise.
"""
for module in modules:
if module['module'] == top_module:
return module['file']
return ''
def copy_hardware_template(repo_name: str) -> None:
"""
Copies a hardware template file to a new destination, renaming it based on the repository name.
Args:
repo_name (str): The name of the repository to use in the destination file name.
Returns:
None
"""
orig = 'rtl/template.v'
# Caminho do diretório de destino
dest = f'rtl/{repo_name}.v'
if os.path.exists(dest):
print('Arquivo já existe')
return
# Copiar o diretório
shutil.copy(orig, dest)
def generate_processor_config(
url: str,
add_config: bool,
plot_graph: bool,
config_file_path: str,
no_llama: bool,
) -> None:
"""
Generates a processor configuration by cloning a repository, analyzing its files,
extracting modules, and optionally updating the configuration file and plotting graphs.
Args:
url (str): URL of the processor's repository to clone.
add_config (bool): Whether to add the generated configuration to the config file.
plot_graph (bool): Whether to plot the module dependency graphs.
config_file_path (str): Path to the configuration file.
no_llama (bool): Whether to use OLLAMA to identify the top module.
Returns:
None
"""
repo_name = url.split('/')[-1].replace('.git', '')
destination_path = clone_repo(url, repo_name)
if not destination_path:
print('Não foi possível clonar o repositório.')
return
files, extension = find_files_with_extension(destination_path, EXTENSIONS)
modules = extract_modules(files)
modulename_list = []
for module_name, file_path in modules:
modulename_list.append(
{
'module': module_name,
'file': os.path.relpath(file_path, destination_path),
}
)
tb_files = []
non_tb_files = []
for f in files:
if is_testbench_file(f, repo_name):
tb_files.append(f)
else:
non_tb_files.append(f)
tb_files = [os.path.relpath(tb_f, destination_path) for tb_f in tb_files]
non_tb_files = [
os.path.relpath(non_tb_f, destination_path)
for non_tb_f in non_tb_files
]
include_dirs = find_include_dirs(destination_path)
# Construir os grafos direto e inverso
module_graph, module_graph_inverse = build_module_graph(files, modules)
filtered_files = non_tb_files
top_module = ''
if not no_llama:
filtered_files = get_filtered_files_list(
non_tb_files, tb_files, modules, module_graph, repo_name
)
top_module = get_top_module(
non_tb_files, tb_files, modules, module_graph, repo_name
)
language_version = '2005'
if extension == '.vhdl':
language_version = '08'
elif extension == '.vhd':
language_version = '08'
elif extension == '.sv':
language_version = '2012'
output_json = {
'name': repo_name,
'folder': repo_name,
'sim_files': tb_files,
'files': filtered_files,
'include_dirs': list(include_dirs),
'repository': url,
'top_module': top_module,
'extra_flags': [],
'language_version': language_version,
}
print('Result: ')
print(json.dumps(output_json, indent=4))
output_json['modules'] = modulename_list
output_json['module_graph'] = module_graph
output_json['module_graph_inverse'] = module_graph_inverse
output_json['non_tb_files'] = non_tb_files
with open(
f'logs/{repo_name}_{time.time()}.json', 'w', encoding='utf-8'
) as log_file:
log_file.write(json.dumps(output_json, indent=4))
log_file.close()
copy_hardware_template(repo_name)
# top_module_file = get_top_module_file(modulename_list, top_module)
# generate_top_file(top_module_file, repo_name)
remove_repo(repo_name)
if add_config:
config = load_config(config_file_path)
config['cores'][repo_name] = output_json
save_config(config_file_path, config)
if plot_graph:
# Plotar os grafos
plot_graph(module_graph, 'Grafo Direto dos Módulos')
plot_graph(module_graph_inverse, 'Grafo Inverso dos Módulos')
def generate_all_pipelines(config_file_path: str) -> None:
"""
Generates Jenkinsfiles for all processors defined in the configuration file.
Args:
config_file_path (str): Path to the configuration file.
Returns:
None
"""
config = load_config(config_file_path)
for key in config['cores'].keys():
processor_data = get_processor_data(config, key)
generate_jenkinsfile(
processor_data,
FPGAs,
MAIN_SCRIPT_PATH,
processor_data['language_version'],
processor_data['extra_flags'],
)
os.rename(
'Jenkinsfile', f'{BASE_DIR}{processor_data["name"]}.Jenkinsfile'
)
print('Jenkinsfiles generated successfully.')
def main() -> None:
"""
Main entry point of the script. Parses command-line arguments and executes the
corresponding actions.
Command-line arguments:
-j, --generate-all-jenkinsfiles: Generates Jenkinsfiles for the processors.
-c, --generate-config: Generates a processor configuration.
-g, --plot-graph: Plots the module dependency graph.
-a, --add-config: Adds the generated configuration to the config file.
-p, --path-config: Path to the config file (default: 'config.json').
-u, --processor-url: URL of the processor repository.
Raises:
ValueError: If `--generate-config` is used without providing `--processor-url`.
Returns:
None
"""
parser = argparse.ArgumentParser(
description='Script para gerar as configurações de um processador'
)
parser = argparse.ArgumentParser(
description='Script to generate processor configurations'
)
parser.add_argument(
'-j',
'--generate-all-jenkinsfiles',
action='store_true',
help='Generates a Jenkinsfiles for the processors',
)
parser.add_argument(
'-c',
'--generate-config',
action='store_true',
help='Generates a processor configuration',
)
parser.add_argument(
'-g',
'--plot-graph',
action='store_true',
help='Plots the graph of the generated configuration',
)
parser.add_argument(
'-a',
'--add-config',
action='store_true',
help='Adds the generated configuration to the config file',
)
parser.add_argument(
'-p',
'--path-config',
type=str,
default='config.json',
help='Path to the config file',
)
parser.add_argument(
'-u',
'--processor-url',
type=str,
help='URL of the processor repository',
)
parser.add_argument(
'-n',
'--no-llama',
action='store_true',
help='Não utilizar o OLLAMA para identificar o módulo principal',
)
args = parser.parse_args()
if args.generate_config:
if not args.processor_url:
raise ValueError('Argumento processor-url não encontrado')
generate_processor_config(
args.processor_url,
args.add_config,
args.plot_graph,
args.path_config,
args.no_llama,
)
if args.generate_all_jenkinsfiles:
generate_all_pipelines(args.path_config)
if not args.generate_config and not args.generate_all_jenkinsfiles:
print('Nenhum comando fornecido, utilize --help para listar as opcões')
if __name__ == '__main__':
main()