Skip to content

Commit

Permalink
[ot] scripts/opentitan: cfggen.py: improve top option switches
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
rivos-eblot committed Jan 27, 2025
1 parent 6b96205 commit bc4626e
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 48 deletions.
47 changes: 24 additions & 23 deletions docs/opentitan/cfggen.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,38 @@ the QEMU binary.
## Usage

````text
usage: cfggen.py [-h] [-o CFG] [-T TOP] [-c SV] [-l SV] [-t HJSON] [-s SOCID]
[-C COUNT] [-v] [-d]
TOPDIR
usage: cfggen.py [-h] [-T {darjeeling,earlgrey}] [-o CFG] [-c SV] [-l SV]
[-t HJSON] [-s SOCID] [-C COUNT] [-v] [-d]
[OTDIR]
OpenTitan QEMU configuration file generator.
options:
-h, --help show this help message and exit
Files:
TOPDIR OpenTitan top directory
-o CFG, --out CFG Filename of the config file to generate
-T TOP, --top TOP OpenTitan Top name (default: darjeeling)
-c SV, --otpconst SV OTP Constant SV file (default: auto)
-l SV, --lifecycle SV
LifeCycle SV file (default: auto)
-t HJSON, --topcfg HJSON
OpenTitan top HJSON config file (default: auto)
OTDIR OpenTitan root directory
-T, --top {darjeeling,earlgrey}
OpenTitan top name
-o, --out CFG Filename of the config file to generate
-c, --otpconst SV OTP Constant SV file (default: auto)
-l, --lifecycle SV LifeCycle SV file (default: auto)
-t, --topcfg HJSON OpenTitan top HJSON config file (default: auto)
Modifiers:
-s SOCID, --socid SOCID
SoC identifier, if any
-C COUNT, --count COUNT
SoC count (default: 1)
-s, --socid SOCID SoC identifier, if any
-C, --count COUNT SoC count (default: 1)
Extras:
-v, --verbose increase verbosity
-d, --debug enable debug mode
````


### Arguments

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

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

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

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

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

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

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


### Examples

````sh
./scripts/opentitan/cfggen.py ../opentitan-integrated -o opentitan.cfg
./scripts/opentitan/cfggen.py ../opentitan -T earlgrey -o opentitan.cfg
````

````sh
./scripts/opentitan/cfggen.py -o opentitan.cfg \
-t ../opentitan/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
````
91 changes: 66 additions & 25 deletions scripts/opentitan/cfggen.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env python3

# Copyright (c) 2024 Rivos, Inc.
# Copyright (c) 2025 lowRISC contributors.
# SPDX-License-Identifier: Apache2

"""OpenTitan QEMU configuration file generator.
Expand All @@ -11,7 +12,7 @@
from argparse import ArgumentParser
from configparser import ConfigParser
from logging import getLogger
from os.path import dirname, isdir, isfile, join as joinpath, normpath
from os.path import abspath, dirname, isdir, isfile, join as joinpath, normpath
from traceback import format_exc
from typing import Optional
import re
Expand Down Expand Up @@ -52,12 +53,19 @@ def __init__(self):
self._roms: dict[Optional[int], dict[str, str]] = {}
self._otp: dict[str, str] = {}
self._lc: dict[str, str] = {}
self._top_name: Optional[str] = None

@property
def top_name(self) -> Optional[str]:
"""Return the name of the top as defined in a configuration file."""
return self._top_name

def load_top_config(self, toppath: str) -> None:
"""Load data from HJSON top configuration file."""
assert not _HJSON_ERROR
with open(toppath, 'rt') as tfp:
cfg = hjload(tfp)
self._top_name = cfg.get('name')
for module in cfg.get('module') or []:
modtype = module.get('type')
if modtype == 'rom_ctrl':
Expand Down Expand Up @@ -223,17 +231,20 @@ def _generate_life_cycle(self, cfg: ConfigParser,
def main():
"""Main routine"""
debug = True
default_top = 'Darjeeling'
top_map = {
'darjeeling': 'dj',
'earlgrey': 'eg',
}
try:
desc = sys.modules[__name__].__doc__.split('.', 1)[0].strip()
argparser = ArgumentParser(description=f'{desc}.')
files = argparser.add_argument_group(title='Files')
files.add_argument('opentitan', nargs=1, metavar='TOPDIR',
help='OpenTitan top directory')
files.add_argument('opentitan', nargs='?', metavar='OTDIR',
help='OpenTitan root directory')
files.add_argument('-T', '--top', choices=top_map.keys(),
help='OpenTitan top name')
files.add_argument('-o', '--out', metavar='CFG',
help='Filename of the config file to generate')
files.add_argument('-T', '--top', default=default_top,
help=f'OpenTitan Top name (default: {default_top})')
files.add_argument('-c', '--otpconst', metavar='SV',
help='OTP Constant SV file (default: auto)')
files.add_argument('-l', '--lifecycle', metavar='SV',
Expand All @@ -254,29 +265,60 @@ def main():
args = argparser.parse_args()
debug = args.debug

configure_loggers(args.verbose, 'cfggen', 'otp')
log = configure_loggers(args.verbose, 'cfggen', 'otp')[0]

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

topdir = args.opentitan[0]
if not isdir(topdir):
argparser.error('Invalid OpenTitan top directory')
ot_dir = normpath(topdir)
top = f'top_{args.top.lower()}'
if args.top.lower() != default_top.lower():
var = ''.join(w[0]
for w in camel_to_snake_case(args.top).split('_'))
else:
var = 'dj'
cfg = OtConfiguration()

if not args.topcfg:
cfgpath = joinpath(ot_dir, f'hw/{top}/data/autogen/{top}.gen.hjson')
topcfg = args.topcfg
ot_dir = args.opentitan
if not topcfg:
if not args.opentitan:
argparser.error('OTDIR is required is no top file is specified')
if not isdir(ot_dir):
argparser.error('Invalid OpenTitan root directory')
ot_dir = abspath(ot_dir)
if not args.top:
argparser.error('Top name is required if no top file is '
'specified')
top = f'top_{args.top}'
topvar = top_map[args.top]
topcfg = joinpath(ot_dir, f'hw/{top}/data/autogen/{top}.gen.hjson')
if not isfile(topcfg):
argparser.error(f"No such file '{topcfg}'")
log.info("Top config: '%s'", topcfg)
cfg.load_top_config(topcfg)
else:
cfgpath = args.topcfg
if not isfile(cfgpath):
argparser.error(f"No such file '{cfgpath}'")

if not isfile(topcfg):
argparser.error(f'No such top file: {topcfg}')
cfg.load_top_config(topcfg)
ltop = cfg.top_name
if not ltop:
argparser.error('Unknown top name')
log.info("Top: '%s'", cfg.top_name)
ltop = ltop.lower()
topvar = {k.lower(): v for k, v in top_map.items()}.get(ltop)
if not topvar:
argparser.error(f'Unsupported top name: {cfg.top_name}')
top = f'top_{ltop}'
if not ot_dir:
check_dir = f'hw/{top}/data'
cur_dir = dirname(topcfg)
while cur_dir:
check_path = joinpath(cur_dir, check_dir)
if isdir(check_path):
ot_dir = cur_dir
break
cur_dir = dirname(cur_dir)
if not ot_dir:
argparser.error('Cannot find OT root directory')
elif not isdir(ot_dir):
argparser.error('Invalid OpenTitan root directory')
ot_dir = abspath(ot_dir)
log.info("OT directory: '%s'", ot_dir)
log.info("Variant: '%s'", topvar)
if not args.lifecycle:
lcpath = joinpath(ot_dir, 'hw/ip/lc_ctrl/rtl/lc_ctrl_state_pkg.sv')
else:
Expand All @@ -293,10 +335,9 @@ def main():
argparser.error(f"No such file '{ocpath}'")

cfg = OtConfiguration()
cfg.load_top_config(cfgpath)
cfg.load_lifecycle(lcpath)
cfg.load_otp_constants(ocpath)
cfg.save(var, args.socid, args.count, args.out)
cfg.save(topvar, args.socid, args.count, args.out)

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

0 comments on commit bc4626e

Please sign in to comment.