blob: 0ada86baf816a6bddf822d85cb2447f057359c0f [file] [log] [blame]
Michael Schaffnerbdcb2cd2021-01-28 15:54:37 -08001#!/usr/bin/env python3
2# Copyright lowRISC contributors.
3# Licensed under the Apache License, Version 2.0, see LICENSE for details.
4# SPDX-License-Identifier: Apache-2.0
5r"""Generate RTL and documentation collateral from OTP memory
6map definition file (hjson).
7"""
8import argparse
9import datetime
10import logging as log
11import random
Michael Schaffner82c21812021-06-23 15:07:39 -070012import re
Michael Schaffnerbdcb2cd2021-01-28 15:54:37 -080013from pathlib import Path
14
15import hjson
Timothy Trippel5c592dd2022-04-18 11:07:57 -070016
Michael Schaffnerbdcb2cd2021-01-28 15:54:37 -080017from lib.common import wrapped_docstring
18from lib.OtpMemImg import OtpMemImg
19
20# Get the memory map definition.
21MMAP_DEFINITION_FILE = 'hw/ip/otp_ctrl/data/otp_ctrl_mmap.hjson'
22# Life cycle state and ECC poly definitions.
23LC_STATE_DEFINITION_FILE = 'hw/ip/lc_ctrl/data/lc_ctrl_state.hjson'
24# Default image file definition (can be overridden on the command line).
25IMAGE_DEFINITION_FILE = 'hw/ip/otp_ctrl/data/otp_ctrl_img_dev.hjson'
26# Default output path (can be overridden on the command line).
Michael Schaffner7e0b00a2021-02-23 16:22:05 -080027MEMORY_HEX_FILE = 'otp-img.vmem'
Michael Schaffnerbdcb2cd2021-01-28 15:54:37 -080028
29
30def _override_seed(args, name, config):
31 '''Override the seed key in config with value specified in args'''
32 arg_seed = getattr(args, name)
33 if arg_seed:
34 log.warning('Commandline override of {} with {}.'.format(
35 name, arg_seed))
36 config['seed'] = arg_seed
37 # Otherwise, we either take it from the .hjson if present, or
38 # randomly generate a new seed if not.
39 else:
Michael Schaffnerbdcb2cd2021-01-28 15:54:37 -080040 new_seed = random.getrandbits(64)
41 if config.setdefault('seed', new_seed) == new_seed:
42 log.warning('No {} specified, setting to {}.'.format(
43 name, new_seed))
44
45
Michael Schaffner82c21812021-06-23 15:07:39 -070046def _permutation_string(data_perm):
47 '''Check permutation format and expand the ranges'''
48
49 if not isinstance(data_perm, str):
50 TypeError()
51
52 if not data_perm:
53 return ''
54
55 # Check the format first
56 pattern = r'^((?:\[[0-9]+:[0-9]+\])+(?:,\[[0-9]+:[0-9]+\])*)'
57 match = re.fullmatch(pattern, data_perm)
58 if match is None:
59 raise ValueError()
60 # Expand the ranges
61 expanded_perm = []
62 groups = match.groups()
63 for group in groups[0].split(','):
64 k1, k0 = [int(x) for x in group[1:-1].split(':')]
65 if k1 > k0:
66 expanded_perm = list(range(k0, k1 + 1)) + expanded_perm
67 else:
68 expanded_perm = list(range(k0, k1 - 1, -1)) + expanded_perm
69
70 return expanded_perm
71
72
Michael Schaffner59876602021-02-23 19:25:31 -080073# TODO: this can be removed when we have moved to Python 3.8
74# in all regressions, since the extend action is only available
75# from that version onward.
76# This workaround solution has been taken verbatim from
77# https://stackoverflow.com/questions/41152799/argparse-flatten-the-result-of-action-append
78class ExtendAction(argparse.Action):
79 '''Extend action for the argument parser'''
Timothy Trippel5c592dd2022-04-18 11:07:57 -070080
Michael Schaffner59876602021-02-23 19:25:31 -080081 def __call__(self, parser, namespace, values, option_string=None):
82 items = getattr(namespace, self.dest) or []
83 items.extend(values)
84 setattr(namespace, self.dest, items)
85
86
Michael Schaffnerbdcb2cd2021-01-28 15:54:37 -080087def main():
88 log.basicConfig(level=log.INFO, format="%(levelname)s: %(message)s")
89
90 # Make sure the script can also be called from other dirs than
91 # just the project root by adapting the default paths accordingly.
92 proj_root = Path(__file__).parent.joinpath('../../')
93 lc_state_def_file = Path(proj_root).joinpath(LC_STATE_DEFINITION_FILE)
94 mmap_def_file = Path(proj_root).joinpath(MMAP_DEFINITION_FILE)
95 img_def_file = Path(proj_root).joinpath(IMAGE_DEFINITION_FILE)
96 hex_file = Path(MEMORY_HEX_FILE)
97
98 parser = argparse.ArgumentParser(
99 prog="gen-otp-img",
100 description=wrapped_docstring(),
101 formatter_class=argparse.RawDescriptionHelpFormatter)
Michael Schaffner59876602021-02-23 19:25:31 -0800102 parser.register('action', 'extend', ExtendAction)
Timothy Trippel5c592dd2022-04-18 11:07:57 -0700103 parser.add_argument('--quiet',
104 '-q',
105 action='store_true',
Rupert Swarbrick717b0c02021-04-09 15:57:43 +0100106 help='''Don't print out progress messages.''')
Srikrishna Iyer0913e132021-11-23 17:28:12 -0800107 parser.add_argument('--seed',
108 type=int,
109 metavar='<seed>',
110 help="Custom seed used for randomization.")
Michael Schaffnerbdcb2cd2021-01-28 15:54:37 -0800111 parser.add_argument('--img-seed',
112 type=int,
113 metavar='<seed>',
114 help='''
115 Custom seed for RNG to compute randomized items in OTP image.
116
117 Can be used to override the seed value specified in the image
118 config Hjson.
119 ''')
120 parser.add_argument('--lc-seed',
121 type=int,
122 metavar='<seed>',
123 help='''
124 Custom seed for RNG to compute randomized life cycle netlist constants.
125
126 Note that this seed must coincide with the seed used for generating
127 the LC state encoding (gen-lc-state-enc.py).
128
129 This value typically does not need to be specified as it is taken from
130 the LC state encoding definition Hjson.
131 ''')
132 parser.add_argument('--otp-seed',
133 type=int,
134 metavar='<seed>',
135 help='''
136 Custom seed for RNG to compute randomized OTP netlist constants.
137
138 Note that this seed must coincide with the seed used for generating
139 the OTP memory map (gen-otp-mmap.py).
140
141 This value typically does not need to be specified as it is taken from
142 the OTP memory map definition Hjson.
143 ''')
144 parser.add_argument('-o',
145 '--out',
146 type=Path,
147 metavar='<path>',
148 default=hex_file,
149 help='''
150 Custom output path for generated hex file.
151 Defaults to {}
152 '''.format(hex_file))
Timothy Trippel5c592dd2022-04-18 11:07:57 -0700153 parser.add_argument('--lc-state-def',
154 type=Path,
155 metavar='<path>',
156 default=lc_state_def_file,
157 help='''
158 Life cycle state definition file in Hjson format.
159 ''')
160 parser.add_argument('--mmap-def',
161 type=Path,
162 metavar='<path>',
163 default=mmap_def_file,
164 help='''
165 OTP memory map file in Hjson format.
166 ''')
Michael Schaffnerbdcb2cd2021-01-28 15:54:37 -0800167 parser.add_argument('--img-cfg',
168 type=Path,
169 metavar='<path>',
170 default=img_def_file,
171 help='''
172 Image configuration file in Hjson format.
173 Defaults to {}
174 '''.format(img_def_file))
175 parser.add_argument('--add-cfg',
176 type=Path,
177 metavar='<path>',
178 action='extend',
179 nargs='+',
180 default=[],
181 help='''
182 Additional image configuration file in Hjson format.
183
184 This switch can be specified multiple times.
185 Image configuration files are parsed in the same
186 order as they are specified on the command line,
187 and partition item values that are specified multiple
188 times are overridden in that order.
189
190 Note that seed values in additional configuration files
191 are ignored.
192 ''')
Michael Schaffner82c21812021-06-23 15:07:39 -0700193 parser.add_argument('--data-perm',
194 type=_permutation_string,
195 metavar='<map>',
196 default='',
197 help='''
198 This is a post-processing option and allows permuting
199 the bit positions before writing the hexfile.
200 The bit mapping needs to be supplied as a comma separated list
201 of bit slices, where the numbers refer to the bit positions in
202 the original data word before remapping, for example:
203
204 "[7:0],[16:8]".
205
206 The mapping must be bijective - otherwise this will generate
207 an error.
208 ''')
Michael Schaffnerbdcb2cd2021-01-28 15:54:37 -0800209
210 args = parser.parse_args()
211
Rupert Swarbrick717b0c02021-04-09 15:57:43 +0100212 if args.quiet:
213 log.getLogger().setLevel(log.WARNING)
214
Timothy Trippel5c592dd2022-04-18 11:07:57 -0700215 log.info('Loading LC state definition file {}'.format(args.lc_state_def))
216 with open(args.lc_state_def, 'r') as infile:
Michael Schaffnerbdcb2cd2021-01-28 15:54:37 -0800217 lc_state_cfg = hjson.load(infile)
Timothy Trippel5c592dd2022-04-18 11:07:57 -0700218 log.info('Loading OTP memory map definition file {}'.format(args.mmap_def))
219 with open(args.mmap_def, 'r') as infile:
Michael Schaffnerbdcb2cd2021-01-28 15:54:37 -0800220 otp_mmap_cfg = hjson.load(infile)
221 log.info('Loading main image configuration file {}'.format(args.img_cfg))
222 with open(args.img_cfg, 'r') as infile:
223 img_cfg = hjson.load(infile)
Timothy Trippel5c592dd2022-04-18 11:07:57 -0700224
225 # Set the initial random seed so that the generated image is
Srikrishna Iyer0913e132021-11-23 17:28:12 -0800226 # deterministically randomized.
227 random.seed(args.seed)
Michael Schaffnerbdcb2cd2021-01-28 15:54:37 -0800228
229 # If specified, override the seeds.
230 _override_seed(args, 'lc_seed', lc_state_cfg)
231 _override_seed(args, 'otp_seed', otp_mmap_cfg)
232 _override_seed(args, 'img_seed', img_cfg)
233
234 try:
Timothy Trippel5c592dd2022-04-18 11:07:57 -0700235 otp_mem_img = OtpMemImg(lc_state_cfg, otp_mmap_cfg, img_cfg,
236 args.data_perm)
Michael Schaffnerbdcb2cd2021-01-28 15:54:37 -0800237
238 for f in args.add_cfg:
239 log.info(
240 'Processing additional image configuration file {}'.format(f))
241 log.info('')
242 with open(f, 'r') as infile:
243 cfg = hjson.load(infile)
244 otp_mem_img.override_data(cfg)
245 log.info('')
246
247 except RuntimeError as err:
248 log.error(err)
249 exit(1)
250
251 # Print all defined args into header comment for referqence
252 argstr = ''
253 for arg, argval in sorted(vars(args).items()):
254 if argval:
255 if not isinstance(argval, list):
256 argval = [argval]
257 for a in argval:
258 argname = '-'.join(arg.split('_'))
259 # Get absolute paths for all files specified.
260 a = a.resolve() if isinstance(a, Path) else a
261 argstr += ' \\\n// --' + argname + ' ' + str(a) + ''
262
263 dt = datetime.datetime.now(datetime.timezone.utc)
264 dtstr = dt.strftime("%a, %d %b %Y %H:%M:%S %Z")
265 memfile_header = '// Generated on {} with\n// $ gen-otp-img.py {}\n//\n'.format(
266 dtstr, argstr)
267
268 hexfile_content = memfile_header + otp_mem_img.streamout_hexfile()
269
270 with open(args.out, 'w') as outfile:
271 outfile.write(hexfile_content)
272
273
274if __name__ == "__main__":
275 main()