Skip to content

Commit 78f658d

Browse files
rivos-eblotloiclefort
authored andcommitted
[ot] scripts/opentitan: otptool.py: add LC template generation
Rework all generation-related option to simplify the syntax. Signed-off-by: Emmanuel Blot <eblot@rivosinc.com>
1 parent baf2128 commit 78f658d

File tree

4 files changed

+79
-43
lines changed

4 files changed

+79
-43
lines changed

docs/opentitan/otptool.md

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ usage: otptool.py [-h] [-j HJSON] [-m VMEM] [-l SV] [-o C] [-r RAW]
1010
[-k {auto,otp,fuz}] [-e BITS] [-C CONFIG] [-c INT] [-i INT]
1111
[-w] [-n] [-s] [-E] [-D] [-U] [--empty PARTITION]
1212
[--clear-bit CLEAR_BIT] [--set-bit SET_BIT]
13-
[--toggle-bit TOGGLE_BIT] [-L | -P | -R] [-v] [-d]
13+
[--toggle-bit TOGGLE_BIT] [-G {LCVAL,LCTPL,PARTS,REGS}] [-v]
14+
[-d]
1415
1516
QEMU OT tool to manage OTP files.
1617
@@ -23,7 +24,7 @@ Files:
2324
-m VMEM, --vmem VMEM input VMEM file
2425
-l SV, --lifecycle SV
2526
input lifecycle system verilog file
26-
-o C, --output C output C file
27+
-o C, --output C output filename for C file generation
2728
-r RAW, --raw RAW QEMU OTP raw image file
2829
2930
Parameters:
@@ -50,9 +51,8 @@ Commands:
5051
--set-bit SET_BIT set a bit at specified location
5152
--toggle-bit TOGGLE_BIT
5253
toggle a bit at specified location
53-
-L, --generate-lc generate lc_ctrl C arrays
54-
-P, --generate-parts generate partition descriptor C arrays
55-
-R, --generate-regs generate partition register C definitions
54+
-G {LCVAL,LCTPL,PARTS,REGS}, --generate {LCVAL,LCTPL,PARTS,REGS}
55+
generate C code, see doc for options
5656
5757
Extras:
5858
-v, --verbose increase verbosity
@@ -124,8 +124,9 @@ Fuse RAW images only use the v1 type.
124124
the kind of the input VMEM file from its content when this option is not specified or set to
125125
`auto`. It is fails to detect the file kind or if the kind needs to be enforced, use this option.
126126

127-
* `-L` generate a file describing the LifeCycle contants as C arrays. Mutually exclusive with the
128-
`-P` and `-R` option switches. See `-o` to specify an output file.
127+
* `-G` can be used to generate C code for QEMU, from OTP and LifeCycle known definitions. See the
128+
[Generation](#generation) section for details. See option `-o` to specify the path to the file to
129+
generate
129130

130131
* `-l` specify the life cycle system verilog file that defines the encoding of the life cycle
131132
states. This option is not required to generate a RAW image file, but required when the `-L`
@@ -136,14 +137,8 @@ Fuse RAW images only use the v1 type.
136137
* `-n` tell the script not to attempt to decode the content of encoded fields, such as the hardened
137138
booleans values. When used, the raw value of each field is printed out.
138139

139-
* `-o` specify the path to the output C file to generate, see `-L`, `-P` and `-R` option switches.
140-
Defaults to the standard output.
141-
142-
* `-P` generate a file describing the OTP partition properties as C arrays. Mutually exclusive with
143-
the `-L` and `-R` option switches. See `-o` to specify an output file.
144-
145-
* `-R` generate a file describing the OTP partition registers as C defintion. Mutually exclusive
146-
with the `-L` and `-P` option switches. See `-o` to specify an output file.
140+
* `-o` specify the path to the output C file to generate, see `-G` option. If not specified, the
141+
generated file is emitted on the standard output.
147142

148143
* `-r` specify the path to the QEMU OTP RAW image file. When used with the `-m` option switch, a
149144
new QEMU OTP image file is generated. Otherwise, the QEMU OTP RAW image file should exist and is
@@ -194,6 +189,22 @@ If the bit is larger than the data slot, it indicates the location with the ECC
194189
fuses are organized as 16-bit slots wtih 6-bit ECC, bit 0 to 15 indicates a bit into the data slot,
195190
while bit 16 to 21 indicates an ECC bit.
196191

192+
### Generation [#generation]
193+
194+
The generation feature may be used to generate part of the OTP and LifeCycle QEMU implementation,
195+
based on known definitions from the OpenTitan constants. This option accepts on of the following
196+
argument:
197+
198+
* `LCVAL` generates a file describing the LifeCycle constants as C arrays. Requires `-l` option.
199+
* `LCTPL` generates a file describing the LifeCycle State encoding as a C array. . Requires `-l`
200+
option.
201+
* `PARTS` generates a file describing the OTP partition properties as C arrays. Requires `-j`
202+
option.
203+
* `REGS` generates a file describing the OTP partition registers as C definitions. Requires `-j`
204+
option.
205+
206+
See `-o` to specify an output file for the generated file.
207+
197208
### Examples
198209

199210
Generate a QEMU RAW v1 image for the virtual OTP controller, here with an RMA OTP configuration:
@@ -248,10 +259,10 @@ scripts/opentitan/otptool.py -r otp.raw -j otp_ctrl_mmap.hjson -D \
248259

249260
Generate a C source file with LifeCycle constant definitions:
250261
````sh
251-
scripts/opentitan/otptool.py -L -l lc_ctrl_state_pkg.sv -o lc_state.c
262+
scripts/opentitan/otptool.py -G LCVAL -l lc_ctrl_state_pkg.sv -o lc_state.c
252263
````
253264

254265
Generates a C source file with OTP partition properties:
255266
````sh
256-
scripts/opentitan/otptool.py -j otp_ctrl_mmap.hjson -P -o otp_part.c
267+
scripts/opentitan/otptool.py -j otp_ctrl_mmap.hjson -G PARTS -o otp_part.c
257268
````

scripts/opentitan/cfggen.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from logging import getLogger
1414
from os.path import isdir, isfile, join as joinpath, normpath
1515
from re import match, search
16-
from sys import exit as sysexit, modules, stderr
16+
from sys import exit as sysexit, modules, stderr, stdout
1717
from traceback import format_exc
1818
from typing import Optional
1919

@@ -107,7 +107,7 @@ def save(self, socid: Optional[str] = None, count: Optional[int] = 1,
107107
with open(outpath, 'wt') as ofp:
108108
cfg.write(ofp)
109109
else:
110-
cfg.write(stderr)
110+
cfg.write(stdout)
111111

112112
@classmethod
113113
def add_pair(cls, data: dict[str, str], kname: str, value: str) -> None:

scripts/opentitan/ot/otp/lifecycle.py

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010
from io import StringIO
1111
from logging import getLogger
1212
from os.path import basename
13-
from re import finditer, match
13+
from re import finditer, match, sub
1414
from textwrap import fill
1515
from typing import TextIO
1616

17-
from ot.util.misc import camel_to_snake_case
17+
from ot.util.misc import camel_to_snake_case, group
1818

1919

2020
class OtpLifecycle:
@@ -31,6 +31,7 @@ class OtpLifecycle:
3131

3232
def __init__(self):
3333
self._log = getLogger('otp.lc')
34+
self._sequences: dict[str, list[str]] = {}
3435
self._tables: dict[str, dict[str, str]] = {}
3536
self._tokens: dict[str, str] = {}
3637

@@ -55,6 +56,7 @@ def load(self, svp: TextIO):
5556
name = abmo.group(1)
5657
sval = abmo.group(2)
5758
val = int(sval[1:], 2 if sval.startswith('b') else 16)
59+
val = ((val >> 8) & 0xff) | ((val & 0xff) << 8)
5860
if name in codes:
5961
self._log.error('Redefinition of %s', name)
6062
continue
@@ -65,14 +67,15 @@ def load(self, svp: TextIO):
6567
kind = smo.group(1).lower()
6668
name = smo.group(2)
6769
seq = smo.group(3)
68-
items = [x.strip() for x in seq.split(',')]
70+
items = [x.strip() for x in reversed(seq.split(','))]
6971
inv = [it for it in items if it not in codes]
7072
if inv:
7173
self._log.error('Unknown state seq: %s', ', '.join(inv))
7274
if kind not in sequences:
7375
sequences[kind] = {}
7476
sequences[kind][name] = items
7577
continue
78+
self._sequences = sequences
7679
svp.seek(0)
7780
for tmo in finditer(r"\s+parameter\s+lc_token_t\s+(\w+)\s+="
7881
r"\s+\{\s+128'h([0-9A-F]+)\s+\};",
@@ -89,13 +92,21 @@ def load(self, svp: TextIO):
8992
seq = ''.join((f'{x:04x}'for x in map(codes.get, seq)))
9093
self._tables[mkind][seq] = conv(ref)
9194

92-
def save(self, cfp: TextIO):
95+
def save(self, cfp: TextIO, data_mode: bool) -> None:
9396
"""Save OTP life cycle definitions as a C file.
9497
9598
:param cfp: output text stream
99+
:param data_mode: whether to output data or template
96100
"""
97101
print(f'/* Section auto-generated with {basename(__file__)} '
98102
f'script */', file=cfp)
103+
if data_mode:
104+
self._save_data(cfp)
105+
else:
106+
self._save_template(cfp)
107+
print('/* End of auto-generated section */', file=cfp)
108+
109+
def _save_data(self, cfp: TextIO) -> None:
99110
for kind, table in self._tables.items():
100111
enum_io = StringIO()
101112
array_io = StringIO()
@@ -125,7 +136,22 @@ def save(self, cfp: TextIO):
125136
# likely to be moved to a header file
126137
print(f'enum {kind.lower()} {{\n{enum_str}}};\n', file=cfp)
127138
print(f'{array_io.getvalue()}', file=cfp)
128-
print('/* End of auto-generated section */', file=cfp)
139+
140+
def _save_template(self, cfp: TextIO) -> None:
141+
print('/* clang-format off */', file=cfp)
142+
states = self._sequences.get('st') or {}
143+
print('static const uint8_t', file=cfp)
144+
print('LC_STATES_TPL[LC_STATE_VALID_COUNT][LC_STATE_SLOT_COUNT] = {',
145+
file=cfp)
146+
for stname, stwords in states.items():
147+
print(f' [LC_STATE_{stname.upper()}] = {{', file=cfp)
148+
for wgrp in group(stwords, len(stwords)//2):
149+
items = (sub(r'(\d+)', r'(\1)', wg) for wg in wgrp)
150+
stws = ' '.join(f'{w:<6s}' for w in (f'{i},' for i in items))
151+
print(f' {stws.rstrip()}', file=cfp)
152+
print(' },', file=cfp)
153+
print('};', file=cfp)
154+
print('/* clang-format on */', file=cfp)
129155

130156
def get_configuration(self, name: str) -> dict[str, str]:
131157
"""Provide a dictionary of configurable elements for QEMU."""

scripts/opentitan/otptool.py

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
def main():
2929
"""Main routine"""
3030
debug = True
31+
genfmts = 'LCVAL LCTPL PARTS REGS'.split()
3132
try:
3233
desc = modules[__name__].__doc__.split('.', 1)[0].strip()
3334
argparser = ArgumentParser(description=f'{desc}.')
@@ -41,7 +42,7 @@ def main():
4142
metavar='SV',
4243
help='input lifecycle system verilog file')
4344
files.add_argument('-o', '--output', metavar='C', type=FileType('wt'),
44-
help='output C file')
45+
help='output filename for C file generation')
4546
files.add_argument('-r', '--raw',
4647
help='QEMU OTP raw image file')
4748
params = argparser.add_argument_group(title='Parameters')
@@ -85,13 +86,8 @@ def main():
8586
help='set a bit at specified location')
8687
commands.add_argument('--toggle-bit', action='append', default=[],
8788
help='toggle a bit at specified location')
88-
generate = commands.add_mutually_exclusive_group()
89-
generate.add_argument('-L', '--generate-lc', action='store_true',
90-
help='generate lc_ctrl C arrays')
91-
generate.add_argument('-P', '--generate-parts', action='store_true',
92-
help='generate partition descriptor C arrays')
93-
generate.add_argument('-R', '--generate-regs', action='store_true',
94-
help='generate partition register C definitions')
89+
commands.add_argument('-G', '--generate', choices=genfmts,
90+
help='generate C code, see doc for options')
9591
extra = argparser.add_argument_group(title='Extras')
9692
extra.add_argument('-v', '--verbose', action='count',
9793
help='increase verbosity')
@@ -136,7 +132,7 @@ def main():
136132
partdesc: Optional[OTPPartitionDesc] = None
137133

138134
if not args.otp_map:
139-
if args.generate_parts or args.generate_regs:
135+
if args.generate in ('PARTS', 'REGS'):
140136
argparser.error('Generator requires an OTP map')
141137
if args.show:
142138
argparser.error('Cannot decode OTP values without an OTP map')
@@ -151,22 +147,26 @@ def main():
151147
if args.lifecycle:
152148
lcext = OtpLifecycleExtension()
153149
lcext.load(args.lifecycle)
150+
elif args.generate in ('LCVAL', 'LCTPL'):
151+
argparser.error('Cannot generate LC array w/o a lifecycle file')
154152

155153
output = stdout if not args.output else args.output
156154

157-
if args.generate_parts:
155+
if not args.generate:
156+
pass
157+
elif args.generate == 'PARTS':
158158
partdesc = OTPPartitionDesc(otpmap)
159159
partdesc.save(basename(args.otp_map.name), basename(argv[0]),
160160
output)
161-
162-
if args.generate_regs:
161+
elif args.generate == 'REGS':
163162
regdef = OTPRegisterDef(otpmap)
164163
regdef.save(basename(args.otp_map.name), basename(argv[0]), output)
165-
166-
if args.generate_lc:
167-
if not lcext:
168-
argparser.error('Cannot generate LC array w/o a lifecycle file')
169-
lcext.save(output)
164+
elif args.generate == 'LCVAL':
165+
lcext.save(output, True)
166+
elif args.generate == 'LCTPL':
167+
lcext.save(output, False)
168+
else:
169+
argparser.error(f'Unsupported generation: {args.generate}')
170170

171171
if args.vmem:
172172
otp.load_vmem(args.vmem, args.kind)
@@ -186,8 +186,7 @@ def main():
186186

187187
if otp.loaded:
188188
if not otp.is_opentitan:
189-
ot_opts = ('iv', 'constant', 'digest', 'generate_parts',
190-
'generate_regs', 'generate_lc', 'otp_map',
189+
ot_opts = ('iv', 'constant', 'digest', 'generate', 'otp_map',
191190
'lifecycle')
192191
if any(getattr(args, a) for a in ot_opts):
193192
argparser.error('Selected option only applies to OpenTitan '

0 commit comments

Comments
 (0)