Skip to content

Commit a7917c6

Browse files
committed
[ot] scripts/opentitan: autotop.py: rename multiple file format generation support
Rather than being defined by language, the output file content are defined for either QEMU or baremetal tests. Signed-off-by: Emmanuel Blot <[email protected]>
1 parent e1103a6 commit a7917c6

File tree

2 files changed

+48
-57
lines changed

2 files changed

+48
-57
lines changed

docs/opentitan/tools.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ of options and the available features.
3737

3838
* [`autoregs.py`] is a Python script that generates register definitions and function templates to
3939
create a skeleton file QEMU system bus device emulation.
40+
* [`autotop.py`] is a Python script that generates machine definitions to create a skeleton file for
41+
a QEMU OpenTitan machine.
4042
* [`checkregs.py`](checkregs.md) is an internal tool design to check the discrepancies between
4143
OpenTitan generated register definition files and their QEMU counterparts. It is only useful to
4244
develop the machine itself.

scripts/opentitan/autotop.py

Lines changed: 46 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Copyright (c) 2025 Rivos, Inc.
44
# SPDX-License-Identifier: Apache2
55

6-
"""QEMU OT tool to generate machine definition for OT device.
6+
"""Generate machine definitions for OpenTitan top.
77
88
:author: Emmanuel Blot <[email protected]>
99
"""
@@ -13,7 +13,6 @@
1313
from os import listdir
1414
from os.path import (abspath, basename, commonprefix, dirname, isdir, isfile,
1515
join as joinpath)
16-
from textwrap import dedent, indent
1716
from traceback import format_exception
1817
from typing import Any, NamedTuple, TextIO
1918
import re
@@ -26,7 +25,7 @@
2625
# ruff: noqa: E402
2726
from ot.util.arg import ArgError
2827
from ot.util.log import configure_loggers
29-
from ot.util.misc import HexInt, camel_to_snake_uppercase, classproperty
28+
from ot.util.misc import HexInt, camel_to_snake_uppercase, classproperty, redent
3029

3130
try:
3231
_HJSON_ERROR = None
@@ -50,7 +49,7 @@ class QEMUSignal(NamedTuple):
5049
index: int
5150

5251

53-
class QEMUAutoTop:
52+
class AutoTop:
5453
"""Helper class to generate QEMU machine definition from Top definitions.
5554
"""
5655

@@ -97,20 +96,6 @@ def load(self, ot_dir: str) -> None:
9796
self._load_all_alerts(hjson)
9897
self._load_pinmux(hjson)
9998

100-
@classmethod
101-
def redent(cls, text: str, spc: int = 0, strip_end: bool = False) -> str:
102-
"""Utility function to re-indent code string.
103-
104-
:param text: the text to re-indent
105-
:param spc: the number of leading empty space chars to prefix lines
106-
:param strip_end: whether to strip trailing whitespace and newline
107-
"""
108-
text = dedent(text.lstrip('\n'))
109-
text = indent(text, ' ' * spc)
110-
if strip_end:
111-
text = text.rstrip(' ').rstrip('\n')
112-
return text
113-
11499
@classmethod
115100
def device_name(cls, name: str, discard_aon: bool = False) -> str:
116101
"""Generate the devices name.
@@ -123,40 +108,40 @@ def device_name(cls, name: str, discard_aon: bool = False) -> str:
123108
pname = pname.replace('_AON', '')
124109
return cls.DEVICE_MAP.get(pname, pname)
125110

126-
# pylint: disable=no-self-argument
127111
@classproperty
128-
def languages(cls) -> list[str]:
129-
"""Report which language generations are supported.
112+
def outkinds(cls) -> list[str]:
113+
"""Report which generation output formats are supported.
130114
"""
115+
# pylint: disable=no-self-argument
131116
prefix = 'generate_'
132-
return list(sorted(f[len(prefix):]
133-
for f in dir(cls) if f.startswith(prefix)))
117+
return [f.removeprefix(prefix) for f in dir(cls)
118+
if f.startswith(prefix)]
134119

135-
def generate_c(self, prefix: str, tfp: TextIO) -> None:
120+
def generate_qemu(self, prefix: str, tfp: TextIO) -> None:
136121
"""Generate a QEMU template file or machine definition.
137122
138123
:param prefix: prefix for C definition
139124
:param tfp: output file stream
140125
"""
141126
lprefix = prefix.lower()
142-
self._generate_c_dev_enum(prefix, tfp)
143-
self._generate_c_pinmux(prefix, tfp)
127+
self._generate_qemu_dev_enum(prefix, tfp)
128+
self._generate_qemu_pinmux(prefix, tfp)
144129
print(f'static const IbexDeviceDef {lprefix}_devices[] = {{', file=tfp)
145130
print('/* clang-format off */', file=tfp)
146131
for devname in sorted(self._devices, key=self._device_address):
147-
self._generate_c_devices(prefix, devname, tfp)
132+
self._generate_qemu_devices(prefix, devname, tfp)
148133
print('/* clang-format on */', file=tfp)
149134
print('};', file=tfp)
150135

151-
def generate_rust(self, _: str, tfp: TextIO) -> None:
136+
def generate_bmtest(self, _: str, tfp: TextIO) -> None:
152137
"""Generate a Rust template file for machine definition.
153138
154139
:param tfp: output file stream
155140
"""
156-
self._generate_rust_base_addresses(tfp)
157-
self._generate_rust_interrupts(tfp)
158-
self._generate_rust_alerts(tfp)
159-
self._generate_rust_pinmux(tfp)
141+
self._generate_bmtest_base_addresses(tfp)
142+
self._generate_bmtest_interrupts(tfp)
143+
self._generate_bmtest_alerts(tfp)
144+
self._generate_bmtest_pinmux(tfp)
160145

161146
def _load_devices(self, topdir: str) -> None:
162147
iptopdir = f'{topdir}/ip'
@@ -318,18 +303,18 @@ def _get_mbox_index(self, udev: str) -> int:
318303
self._mbox_indices[udev] = len(self._mbox_indices)
319304
return self._mbox_indices[udev]
320305

321-
def _generate_c_dev_enum(self, prefix: str, tfp: TextIO) -> None:
306+
def _generate_qemu_dev_enum(self, prefix: str, tfp: TextIO) -> None:
322307
lines = []
323308
uprefix = prefix.upper()
324309
tprefix = prefix.title().replace('_', '')
325310
print(f'enum {tprefix}Device {{', file=tfp)
326311
for dev in sorted(self._devices):
327312
lines.append(f'{uprefix}_DEV_{dev}')
328-
code = self.redent(',\n'.join(lines), 4)
313+
code = redent(',\n'.join(lines), 4)
329314
print(code, file=tfp)
330315
print('}\n', file=tfp)
331316

332-
def _generate_c_pinmux(self, prefix: str, tfp: TextIO) -> None:
317+
def _generate_qemu_pinmux(self, prefix: str, tfp: TextIO) -> None:
333318
tprefix = prefix.title().replace('_', '')
334319
for ioname, pinmux in self._pinmux.items():
335320
lines = []
@@ -342,16 +327,17 @@ def _generate_c_pinmux(self, prefix: str, tfp: TextIO) -> None:
342327
lines.append(f'{u_ioname}_{us_ion}, /* {val} */')
343328
max_val = max(val, max_val)
344329
lines.append(f'{u_ioname}_COUNT, /* {max_val + 1} */')
345-
code = self.redent('\n'.join(lines), 4)
330+
code = redent('\n'.join(lines), 4)
346331
print(code, file=tfp)
347332
print('}\n', file=tfp)
348333

349-
def _generate_c_devices(self, prefix: str, dev: str, tfp: TextIO) -> None:
334+
def _generate_qemu_devices(self, prefix: str, dev: str, tfp: TextIO) \
335+
-> None:
350336
lines: list[str] = []
351337
uprefix = prefix.upper()
352-
irq_defs = self._generate_c_irq_defs(uprefix, dev)
353-
alert_defs = self._generate_c_alert_defs(uprefix, dev)
354-
mmap_defs = self._generate_c_mmap_defs(dev)
338+
irq_defs = self._generate_qemu_irq_defs(uprefix, dev)
339+
alert_defs = self._generate_qemu_alert_defs(uprefix, dev)
340+
mmap_defs = self._generate_qemu_mmap_defs(dev)
355341
if dev not in self._devices:
356342
self._log.warning('%s not in devices', dev)
357343
lines.append(f'[{uprefix}_DEV_{dev}] = {{')
@@ -382,20 +368,20 @@ def _generate_c_devices(self, prefix: str, dev: str, tfp: TextIO) -> None:
382368
lines.append(f' .type = TYPE_OT_{devbase},')
383369
if mmap_defs:
384370
lines.append(' .memmap = MEMMAPENTRIES(')
385-
lines.append(self.redent(',\n'.join(mmap_defs), 8))
371+
lines.append(redent(',\n'.join(mmap_defs), 8))
386372
lines.append(' ),')
387373
if irq_defs or alert_defs:
388374
defs = []
389375
defs.extend(irq_defs)
390376
defs.extend(alert_defs)
391377
lines.append(' .gpio = IBEXGPIOCONNDEFS(')
392-
lines.append(self.redent(',\n'.join(defs), 8))
378+
lines.append(redent(',\n'.join(defs), 8))
393379
lines.append(' ),')
394380
lines.append('},')
395-
code = self.redent('\n'.join(lines), 4)
381+
code = redent('\n'.join(lines), 4)
396382
print(code, file=tfp)
397383

398-
def _generate_c_mmap_defs(self, device: str) -> list[str]:
384+
def _generate_qemu_mmap_defs(self, device: str) -> list[str]:
399385
# sorting memory range the weird way (hack ahead)
400386
# we want the IBEX bus to be seen first (vs. debug or external buses)
401387
# and appear in the logical address order
@@ -412,20 +398,20 @@ def _sort_mems(dev):
412398
mmaps.append(f'{{ .base = 0x{dev.base:{width}x}u }}')
413399
return mmaps
414400

415-
def _generate_c_alert_defs(self, prefix: str, dev: str) -> list[str]:
401+
def _generate_qemu_alert_defs(self, prefix: str, dev: str) -> list[str]:
416402
alerts = []
417403
for pos, alert in enumerate(self._alerts.get(dev, [])):
418404
alerts.append(f'{prefix}_GPIO_ALERT({pos}, {alert.index})')
419405
return alerts
420406

421-
def _generate_c_irq_defs(self, prefix: str, dev: str) -> list[str]:
407+
def _generate_qemu_irq_defs(self, prefix: str, dev: str) -> list[str]:
422408
irqs = []
423409
for pos, irq in enumerate(self._interrupts.get(dev, [])):
424410
irqs.append(f'{prefix}_GPIO_SYSBUS_IRQ({pos}, PLIC, '
425411
f'{irq.index})')
426412
return irqs
427413

428-
def _generate_rust_base_addresses(self, tfp):
414+
def _generate_bmtest_base_addresses(self, tfp):
429415
utop = self._topname.upper()
430416
print('pub mod base_addresses {', file=tfp)
431417
for dev in sorted(self._devices, key=self._device_address):
@@ -456,7 +442,7 @@ def _generate_rust_base_addresses(self, tfp):
456442
file=tfp)
457443
print('}\n', file=tfp)
458444

459-
def _generate_rust_interrupts(self, tfp):
445+
def _generate_bmtest_interrupts(self, tfp):
460446
irqs: dict[str, int] = {}
461447
if not self._interrupts:
462448
return
@@ -475,7 +461,7 @@ def _generate_rust_interrupts(self, tfp):
475461
print(f' pub const COUNT: usize = {max_val + 1};', file=tfp)
476462
print('}\n', file=tfp)
477463

478-
def _generate_rust_alerts(self, tfp) -> None:
464+
def _generate_bmtest_alerts(self, tfp) -> None:
479465
alerts: dict[str, int] = {}
480466
if not self._alerts:
481467
return
@@ -490,7 +476,7 @@ def _generate_rust_alerts(self, tfp) -> None:
490476
print(f' pub const COUNT: usize = {max_val};', file=tfp)
491477
print('}\n', file=tfp)
492478

493-
def _generate_rust_pinmux(self, tfp) -> None:
479+
def _generate_bmtest_pinmux(self, tfp) -> None:
494480
for ioname, pinmux in self._pinmux.items():
495481
if not pinmux:
496482
continue
@@ -510,7 +496,10 @@ def main():
510496
desc = sys.modules[__name__].__doc__.split('.', 1)[0].strip()
511497
argparser = ArgumentParser(description=f'{desc}.')
512498
try:
513-
languages = list(QEMUAutoTop.languages)
499+
default_outkind = 'qemu'
500+
outkinds = sorted(AutoTop.outkinds,
501+
key=lambda n: '' if n == default_outkind else n)
502+
assert len(outkinds) > 0
514503
top = argparser.add_argument_group(title='Top')
515504
top.add_argument('opentitan', nargs='?', metavar='OTDIR',
516505
help='OpenTitan root directory')
@@ -522,10 +511,10 @@ def main():
522511
help='output file name')
523512
files.add_argument('-p', '--prefix', default='ot_dj_soc',
524513
help='constant prefix (default: ot_dj_soc)')
525-
files.add_argument('-l', '--language', choices=languages,
526-
default=languages[0],
527-
help=f'output file language '
528-
f'(default: {languages[0]})')
514+
files.add_argument('-k', '--out-kind', choices=outkinds,
515+
default=outkinds[0],
516+
help=f'output file format '
517+
f'(default: {outkinds[0]})')
529518
extra = argparser.add_argument_group(title='Extras')
530519
extra.add_argument('-v', '--verbose', action='count',
531520
help='increase verbosity')
@@ -545,9 +534,9 @@ def main():
545534
argparser.error('OTDIR is required is no top file is specified')
546535
if not isdir(ot_dir):
547536
argparser.error('Invalid OpenTitan root directory')
548-
atop = QEMUAutoTop(args.top)
537+
atop = AutoTop(args.top)
549538
atop.load(ot_dir)
550-
getattr(atop, f'generate_{args.language}')(args.prefix,
539+
getattr(atop, f'generate_{args.out_kind}')(args.prefix,
551540
args.output or sys.stdout)
552541

553542
except ArgError as exc:

0 commit comments

Comments
 (0)