1
1
#!/usr/bin/env python3
2
2
3
3
# Copyright (c) 2024 Rivos, Inc.
4
+ # Copyright (c) 2025 lowRISC contributors.
4
5
# SPDX-License-Identifier: Apache2
5
6
6
7
"""OpenTitan QEMU configuration file generator.
11
12
from argparse import ArgumentParser
12
13
from configparser import ConfigParser
13
14
from logging import getLogger
14
- from os .path import dirname , isdir , isfile , join as joinpath , normpath
15
+ from os .path import abspath , dirname , isdir , isfile , join as joinpath , normpath
15
16
from traceback import format_exc
16
17
from typing import Optional
17
18
import re
@@ -52,12 +53,19 @@ def __init__(self):
52
53
self ._roms : dict [Optional [int ], dict [str , str ]] = {}
53
54
self ._otp : dict [str , str ] = {}
54
55
self ._lc : dict [str , str ] = {}
56
+ self ._top_name : Optional [str ] = None
57
+
58
+ @property
59
+ def top_name (self ) -> Optional [str ]:
60
+ """Return the name of the top as defined in a configuration file."""
61
+ return self ._top_name
55
62
56
63
def load_top_config (self , toppath : str ) -> None :
57
64
"""Load data from HJSON top configuration file."""
58
65
assert not _HJSON_ERROR
59
66
with open (toppath , 'rt' ) as tfp :
60
67
cfg = hjload (tfp )
68
+ self ._top_name = cfg .get ('name' )
61
69
for module in cfg .get ('module' ) or []:
62
70
modtype = module .get ('type' )
63
71
if modtype == 'rom_ctrl' :
@@ -223,17 +231,20 @@ def _generate_life_cycle(self, cfg: ConfigParser,
223
231
def main ():
224
232
"""Main routine"""
225
233
debug = True
226
- default_top = 'Darjeeling'
234
+ top_map = {
235
+ 'darjeeling' : 'dj' ,
236
+ 'earlgrey' : 'eg' ,
237
+ }
227
238
try :
228
239
desc = sys .modules [__name__ ].__doc__ .split ('.' , 1 )[0 ].strip ()
229
240
argparser = ArgumentParser (description = f'{ desc } .' )
230
241
files = argparser .add_argument_group (title = 'Files' )
231
- files .add_argument ('opentitan' , nargs = 1 , metavar = 'TOPDIR' ,
232
- help = 'OpenTitan top directory' )
242
+ files .add_argument ('opentitan' , nargs = '?' , metavar = 'OTDIR' ,
243
+ help = 'OpenTitan root directory' )
244
+ files .add_argument ('-T' , '--top' , choices = top_map .keys (),
245
+ help = 'OpenTitan top name' )
233
246
files .add_argument ('-o' , '--out' , metavar = 'CFG' ,
234
247
help = 'Filename of the config file to generate' )
235
- files .add_argument ('-T' , '--top' , default = default_top ,
236
- help = f'OpenTitan Top name (default: { default_top } )' )
237
248
files .add_argument ('-c' , '--otpconst' , metavar = 'SV' ,
238
249
help = 'OTP Constant SV file (default: auto)' )
239
250
files .add_argument ('-l' , '--lifecycle' , metavar = 'SV' ,
@@ -254,29 +265,61 @@ def main():
254
265
args = argparser .parse_args ()
255
266
debug = args .debug
256
267
257
- configure_loggers (args .verbose , 'cfggen' , 'otp' )
268
+ log = configure_loggers (args .verbose , 'cfggen' , 'otp' )[ 0 ]
258
269
259
270
if _HJSON_ERROR :
260
271
argparser .error ('Missing HJSON module: {_HJSON_ERROR}' )
261
272
262
- topdir = args .opentitan [0 ]
263
- if not isdir (topdir ):
264
- argparser .error ('Invalid OpenTitan top directory' )
265
- ot_dir = normpath (topdir )
266
- top = f'top_{ args .top .lower ()} '
267
- if args .top .lower () != default_top .lower ():
268
- var = '' .join (w [0 ]
269
- for w in camel_to_snake_case (args .top ).split ('_' ))
270
- else :
271
- var = 'dj'
273
+ cfg = OtConfiguration ()
272
274
273
- if not args .topcfg :
274
- cfgpath = joinpath (ot_dir , f'hw/{ top } /data/autogen/{ top } .gen.hjson' )
275
+ topcfg = args .topcfg
276
+ if not topcfg :
277
+ if not args .opentitan :
278
+ argparser .error ('OTDIR is required is no top file is specified' )
279
+ ot_dir = args .opentitan [0 ]
280
+ if not isdir (ot_dir ):
281
+ argparser .error ('Invalid OpenTitan root directory' )
282
+ ot_dir = abspath (ot_dir )
283
+ if not args .top :
284
+ argparser .error ('Top name is required if no top file is '
285
+ 'specified' )
286
+ top = f'top_{ args .top } '
287
+ topvar = top_map [args .top ]
288
+ topcfg = joinpath (ot_dir , f'hw/{ top } /data/autogen/{ top } .gen.hjson' )
289
+ if not isfile (topcfg ):
290
+ argparser .error (f"No such file '{ topcfg } '" )
291
+ log .info ("Top config: '%s'" , topcfg )
292
+ cfg .load_top_config (topcfg )
275
293
else :
276
- cfgpath = args .topcfg
277
- if not isfile (cfgpath ):
278
- argparser .error (f"No such file '{ cfgpath } '" )
279
-
294
+ if not isfile (topcfg ):
295
+ argparser .error (f'No such top file: { topcfg } ' )
296
+ cfg .load_top_config (topcfg )
297
+ ltop = cfg .top_name
298
+ if not ltop :
299
+ argparser .error ('Unknown top name' )
300
+ log .info ("Top: '%s'" , cfg .top_name )
301
+ ltop = ltop .lower ()
302
+ topvar = {k .lower (): v for k , v in top_map .items ()}.get (ltop )
303
+ if not topvar :
304
+ argparser .error (f'Unsupported top name: { cfg .top_name } ' )
305
+ top = f'top_{ ltop } '
306
+ ot_dir = args .opentitan [0 ] if args .opentitan else None
307
+ if not ot_dir :
308
+ check_dir = f'hw/{ top } /data'
309
+ cur_dir = dirname (topcfg )
310
+ while cur_dir :
311
+ check_path = joinpath (cur_dir , check_dir )
312
+ if isdir (check_path ):
313
+ ot_dir = cur_dir
314
+ break
315
+ cur_dir = dirname (cur_dir )
316
+ if not ot_dir :
317
+ argparser .error ('Cannot find OT root directory' )
318
+ elif not isdir (ot_dir ):
319
+ argparser .error ('Invalid OpenTitan root directory' )
320
+ ot_dir = abspath (ot_dir )
321
+ log .info ("OT directory: '%s'" , ot_dir )
322
+ log .info ("Variant: '%s'" , topvar )
280
323
if not args .lifecycle :
281
324
lcpath = joinpath (ot_dir , 'hw/ip/lc_ctrl/rtl/lc_ctrl_state_pkg.sv' )
282
325
else :
@@ -293,10 +336,9 @@ def main():
293
336
argparser .error (f"No such file '{ ocpath } '" )
294
337
295
338
cfg = OtConfiguration ()
296
- cfg .load_top_config (cfgpath )
297
339
cfg .load_lifecycle (lcpath )
298
340
cfg .load_otp_constants (ocpath )
299
- cfg .save (var , args .socid , args .count , args .out )
341
+ cfg .save (topvar , args .socid , args .count , args .out )
300
342
301
343
except (IOError , ValueError , ImportError ) as exc :
302
344
print (f'\n Error: { exc } ' , file = sys .stderr )
0 commit comments