Skip to content

Commit c68ae17

Browse files
committed
[ot] scripts/opentitan: cfggen.py: improve top option switches
QEMU only supports a known list of OT machine, ensure the specified top matches one of the supported machines. It is also now possible to either: - specify the path to the top configuration file, or - specify the path to the OT directory and the top name Signed-off-by: Emmanuel Blot <eblot@rivosinc.com>
1 parent 2ce9880 commit c68ae17

File tree

2 files changed

+91
-48
lines changed

2 files changed

+91
-48
lines changed

docs/opentitan/cfggen.md

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,42 +11,38 @@ the QEMU binary.
1111
## Usage
1212

1313
````text
14-
usage: cfggen.py [-h] [-o CFG] [-T TOP] [-c SV] [-l SV] [-t HJSON] [-s SOCID]
15-
[-C COUNT] [-v] [-d]
16-
TOPDIR
14+
usage: cfggen.py [-h] [-T {darjeeling,earlgrey}] [-o CFG] [-c SV] [-l SV]
15+
[-t HJSON] [-s SOCID] [-C COUNT] [-v] [-d]
16+
[OTDIR]
1717
1818
OpenTitan QEMU configuration file generator.
1919
2020
options:
2121
-h, --help show this help message and exit
2222
2323
Files:
24-
TOPDIR OpenTitan top directory
25-
-o CFG, --out CFG Filename of the config file to generate
26-
-T TOP, --top TOP OpenTitan Top name (default: darjeeling)
27-
-c SV, --otpconst SV OTP Constant SV file (default: auto)
28-
-l SV, --lifecycle SV
29-
LifeCycle SV file (default: auto)
30-
-t HJSON, --topcfg HJSON
31-
OpenTitan top HJSON config file (default: auto)
24+
OTDIR OpenTitan root directory
25+
-T, --top {darjeeling,earlgrey}
26+
OpenTitan top name
27+
-o, --out CFG Filename of the config file to generate
28+
-c, --otpconst SV OTP Constant SV file (default: auto)
29+
-l, --lifecycle SV LifeCycle SV file (default: auto)
30+
-t, --topcfg HJSON OpenTitan top HJSON config file (default: auto)
3231
3332
Modifiers:
34-
-s SOCID, --socid SOCID
35-
SoC identifier, if any
36-
-C COUNT, --count COUNT
37-
SoC count (default: 1)
33+
-s, --socid SOCID SoC identifier, if any
34+
-C, --count COUNT SoC count (default: 1)
3835
3936
Extras:
4037
-v, --verbose increase verbosity
4138
-d, --debug enable debug mode
4239
````
4340

44-
4541
### Arguments
4642

47-
`TOPDIR` is a required positional argument which should point to the top-level directory of the
48-
OpenTitan repository to analyze. It is used to generate the path towards the required files to
49-
parse, each of which can be overidden with options `-c`, `-l` and `-t`.
43+
`OTDIR` is a required positional argument which should point to the root directory of the OpenTitan
44+
repository to analyze. It is used to generate the path towards the required files to parse, each of
45+
which can be overidden with options `-c`, `-l` and `-t`.
5046

5147
* `-C` specify how many SoCs are used on the platform
5248

@@ -61,16 +57,21 @@ parse, each of which can be overidden with options `-c`, `-l` and `-t`.
6157

6258
* `-s` specify a SoC identifier for OT platforms with mulitple SoCs
6359

64-
* `-T` specify the OpenTitan _top_ name, such as `Darjeeling`, `EarlGrey`, ... This option is
65-
case-insensitive.
60+
* `-T` specify the OpenTitan _top_ name, such as `darjeeling`, `earlgrey`, ... This option is
61+
mandatory if `-t` is not specified. An OTDIR root directory should be specified with this option.
6662

67-
* `-t` alternative path to the `top_<top>.gen.hjson` file
63+
* `-t` path to the `top_<top>.gen.hjson` file. This option is mandatory is `-T` is not specified.
6864

6965
* `-v` can be repeated to increase verbosity of the script, mostly for debug purpose.
7066

7167

7268
### Examples
7369

7470
````sh
75-
./scripts/opentitan/cfggen.py ../opentitan-integrated -o opentitan.cfg
71+
./scripts/opentitan/cfggen.py ../opentitan -T earlgrey -o opentitan.cfg
72+
````
73+
74+
````sh
75+
./scripts/opentitan/cfggen.py -o opentitan.cfg \
76+
-t ../opentitan/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
7677
````

scripts/opentitan/cfggen.py

Lines changed: 67 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python3
22

33
# Copyright (c) 2024 Rivos, Inc.
4+
# Copyright (c) 2025 lowRISC contributors.
45
# SPDX-License-Identifier: Apache2
56

67
"""OpenTitan QEMU configuration file generator.
@@ -11,7 +12,7 @@
1112
from argparse import ArgumentParser
1213
from configparser import ConfigParser
1314
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
1516
from traceback import format_exc
1617
from typing import Optional
1718
import re
@@ -52,12 +53,19 @@ def __init__(self):
5253
self._roms: dict[Optional[int], dict[str, str]] = {}
5354
self._otp: dict[str, str] = {}
5455
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
5562

5663
def load_top_config(self, toppath: str) -> None:
5764
"""Load data from HJSON top configuration file."""
5865
assert not _HJSON_ERROR
5966
with open(toppath, 'rt') as tfp:
6067
cfg = hjload(tfp)
68+
self._top_name = cfg.get('name')
6169
for module in cfg.get('module') or []:
6270
modtype = module.get('type')
6371
if modtype == 'rom_ctrl':
@@ -223,17 +231,20 @@ def _generate_life_cycle(self, cfg: ConfigParser,
223231
def main():
224232
"""Main routine"""
225233
debug = True
226-
default_top = 'Darjeeling'
234+
top_map = {
235+
'darjeeling': 'dj',
236+
'earlgrey': 'eg',
237+
}
227238
try:
228239
desc = sys.modules[__name__].__doc__.split('.', 1)[0].strip()
229240
argparser = ArgumentParser(description=f'{desc}.')
230241
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')
233246
files.add_argument('-o', '--out', metavar='CFG',
234247
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})')
237248
files.add_argument('-c', '--otpconst', metavar='SV',
238249
help='OTP Constant SV file (default: auto)')
239250
files.add_argument('-l', '--lifecycle', metavar='SV',
@@ -254,29 +265,61 @@ def main():
254265
args = argparser.parse_args()
255266
debug = args.debug
256267

257-
configure_loggers(args.verbose, 'cfggen', 'otp')
268+
log = configure_loggers(args.verbose, 'cfggen', 'otp')[0]
258269

259270
if _HJSON_ERROR:
260271
argparser.error('Missing HJSON module: {_HJSON_ERROR}')
261272

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()
272274

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)
275293
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)
280323
if not args.lifecycle:
281324
lcpath = joinpath(ot_dir, 'hw/ip/lc_ctrl/rtl/lc_ctrl_state_pkg.sv')
282325
else:
@@ -293,10 +336,9 @@ def main():
293336
argparser.error(f"No such file '{ocpath}'")
294337

295338
cfg = OtConfiguration()
296-
cfg.load_top_config(cfgpath)
297339
cfg.load_lifecycle(lcpath)
298340
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)
300342

301343
except (IOError, ValueError, ImportError) as exc:
302344
print(f'\nError: {exc}', file=sys.stderr)

0 commit comments

Comments
 (0)