Skip to content

Commit f86e26e

Browse files
committed
flash: import modified flash algo conversion script
1 parent 716b383 commit f86e26e

File tree

1 file changed

+267
-0
lines changed

1 file changed

+267
-0
lines changed

tools/generate_flash_algo.py

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
#!/usr/bin/env python3
2+
#
3+
# DAPLink Interface Firmware
4+
# Copyright (c) 2011-2021 Arm Limited
5+
# Copyright (c) 2021 Chris Reed
6+
# Copyright (c) 2021 Mathias Brossard
7+
# SPDX-License-Identifier: Apache-2.0
8+
#
9+
# Licensed under the Apache License, Version 2.0 (the "License");
10+
# you may not use this file except in compliance with the License.
11+
# You may obtain a copy of the License at
12+
#
13+
# http://www.apache.org/licenses/LICENSE-2.0
14+
#
15+
# Unless required by applicable law or agreed to in writing, software
16+
# distributed under the License is distributed on an "AS IS" BASIS,
17+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
# See the License for the specific language governing permissions and
19+
# limitations under the License.
20+
21+
import argparse
22+
import binascii
23+
import colorama
24+
import hashlib
25+
import jinja2
26+
import os
27+
import struct
28+
from datetime import datetime
29+
from pyocd.target.pack.flash_algo import PackFlashAlgo
30+
31+
BLOB_HEADER = '0xe00abe00,'
32+
HEADER_SIZE = 4
33+
STACK_SIZE = 0x400
34+
35+
DAPLINK_TEMPLATE = \
36+
"""/* Flash OS Routines (Automagically Generated)
37+
*
38+
* DAPLink Interface Firmware
39+
* Copyright (c) {{year}} {{copyright_owner}}
40+
* SPDX-License-Identifier: Apache-2.0
41+
*
42+
* Licensed under the Apache License, Version 2.0 (the "License");
43+
* you may not use this file except in compliance with the License.
44+
* You may obtain a copy of the License at
45+
*
46+
* http://www.apache.org/licenses/LICENSE-2.0
47+
*
48+
* Unless required by applicable law or agreed to in writing, software
49+
* distributed under the License is distributed on an "AS IS" BASIS,
50+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
51+
* See the License for the specific language governing permissions and
52+
* limitations under the License.
53+
*/
54+
55+
// Generated from '{{ filename }}'{%- if algo.flash_info.name %}{{ " (%s)" % algo.flash_info.name.decode('utf-8') }}{% endif %}
56+
{%- if pack_file %}
57+
// Originating from '{{ pack_file }}'
58+
{%- endif %}
59+
// digest = {{ digest }}, file size = {{ file_size}}
60+
// {{ 'algo version = 0x%x, algo size = %d (0x%x)' % (algo.flash_info.version, algo_size + header_size, algo_size + header_size) }}
61+
static const uint32_t {{name}}_flash_prog_blob[] = {
62+
{{prog_header}}
63+
{{algo.format_algo_data(4, 8, "c")}}
64+
};
65+
66+
// Start address of flash
67+
static const uint32_t flash_start = {{"0x%08x" % algo.flash_start}};
68+
// Size of flash
69+
static const uint32_t flash_size = {{"0x%08x" % algo.flash_size}};
70+
71+
/**
72+
* List of start and size for each size of flash sector - even indexes are start, odd are size
73+
* The size will apply to all sectors between the listed address and the next address
74+
* in the list.
75+
* The last pair in the list will have sectors starting at that address and ending
76+
* at address flash_start + flash_size.
77+
*/
78+
static const sector_info_t sectors_info[] = {
79+
{%- for start, size in algo.sector_sizes %}
80+
{{ "{0x%08x, 0x%08x}" % (start + algo.flash_start, size) }},
81+
{%- endfor %}
82+
};
83+
84+
static const program_target_t flash = {
85+
{{'0x%08x' % (algo.symbols['Init'] + header_size + entry)}}, // Init
86+
{{'0x%08x' % (algo.symbols['UnInit'] + header_size + entry)}}, // UnInit
87+
{{'0x%08x' % (algo.symbols['EraseChip'] + header_size + entry)}}, // EraseChip
88+
{{'0x%08x' % (algo.symbols['EraseSector'] + header_size + entry)}}, // EraseSector
89+
{{'0x%08x' % (algo.symbols['ProgramPage'] + header_size + entry)}}, // ProgramPage
90+
{%- if 'Verify' in algo.symbols and algo.symbols['Verify'] < 0xFFFFFFFF %}
91+
{{'0x%08x' % (algo.symbols['Verify'] + header_size + entry)}}, // Verify
92+
{%- else %}
93+
0x00000000, // Verify
94+
{%- endif %}
95+
96+
// BKPT : start of blob + 1
97+
// RSB : blob start + header + rw data offset
98+
// RSP : stack pointer
99+
{
100+
{{'0x%08x' % (entry + 1)}},
101+
{{'0x%08x' % (entry + header_size + algo.rw_start)}},
102+
{{'0x%08x' % stack_pointer}}
103+
},
104+
105+
// mem buffer location
106+
{{'0x%08x' % stack_pointer}},
107+
// location to write prog_blob in target RAM
108+
{{'0x%08x' % entry}},
109+
// prog_blob size
110+
sizeof({{name}}_flash_prog_blob),
111+
// address of prog_blob
112+
{{name}}_flash_prog_blob,
113+
// ram_to_flash_bytes_to_be_written
114+
{{'0x%08x' % algo.page_size}}
115+
};
116+
117+
"""
118+
119+
colorama.init()
120+
121+
def str_to_num(val):
122+
return int(val, 0) #convert string to number and automatically handle hex conversion
123+
124+
class PackFlashAlgoGenerator(PackFlashAlgo):
125+
"""
126+
Class to wrap a flash algo
127+
128+
This class is intended to provide easy access to the information
129+
provided by a flash algorithm, such as symbols and the flash
130+
algorithm itself.
131+
"""
132+
133+
def format_algo_data(self, spaces, group_size, fmt):
134+
""""
135+
Return a string representing algo_data suitable for use in a template
136+
137+
The string is intended for use in a template.
138+
139+
:param spaces: The number of leading spaces for each line
140+
:param group_size: number of elements per line (element type
141+
depends of format)
142+
:param fmt: - format to create - can be either "hex" or "c"
143+
"""
144+
padding = " " * spaces
145+
if fmt == "hex":
146+
blob = binascii.b2a_hex(self.algo_data)
147+
line_list = []
148+
for i in range(0, len(blob), group_size):
149+
line_list.append('"' + blob[i:i + group_size] + '"')
150+
return ("\n" + padding).join(line_list)
151+
elif fmt == "c":
152+
blob = self.algo_data[:]
153+
pad_size = 0 if len(blob) % 4 == 0 else 4 - len(blob) % 4
154+
blob = blob + b"\x00" * pad_size
155+
integer_list = struct.unpack("<" + "L" * (len(blob) // 4), blob)
156+
line_list = []
157+
for pos in range(0, len(integer_list), group_size):
158+
group = ["0x%08x" % value for value in
159+
integer_list[pos:pos + group_size]]
160+
line_list.append(", ".join(group))
161+
return (",\n" + padding).join(line_list)
162+
else:
163+
raise Exception("Unsupported format %s" % fmt)
164+
165+
def process_template(self, template_text, data_dict=None):
166+
"""
167+
Generate output from the supplied template
168+
169+
All the public methods and fields of this class can be accessed from
170+
the template via "algo".
171+
172+
:param template_path: Relative or absolute file path to the template
173+
:param data_dict: Additional data to use when generating
174+
"""
175+
if data_dict is None:
176+
data_dict = {}
177+
else:
178+
assert isinstance(data_dict, dict)
179+
data_dict = dict(data_dict)
180+
assert "algo" not in data_dict, "algo already set by user data"
181+
data_dict["algo"] = self
182+
data_dict["algo_size"] = len(self.algo_data)
183+
184+
template = jinja2.Template(template_text)
185+
return template.render(data_dict)
186+
187+
def main():
188+
parser = argparse.ArgumentParser(description="Blob generator")
189+
parser.add_argument("elf_path", help="Elf, axf, or flm to extract flash algo from")
190+
parser.add_argument("--blob-start", default=0x20000000, type=str_to_num, help="Starting "
191+
"address of the flash blob in target RAM.")
192+
parser.add_argument("--stack-size", default=STACK_SIZE, type=str_to_num, help="Stack size for the algo "
193+
f"(default {STACK_SIZE}).")
194+
parser.add_argument("--pack-path", default=None, help="Path to pack file from which flash algo is from")
195+
parser.add_argument("-i", "--info-only", action="store_true", help="Only print information about the flash "
196+
"algo, do not generate a blob.")
197+
parser.add_argument("-o", "--output", default="target_blob.c", help="Path of output file "
198+
"(default 'target_blob.c').")
199+
parser.add_argument("-t", "--template", help="Path to Jinja template file (default is an internal "
200+
"template for DAPLink).")
201+
parser.add_argument('-c', '--copyright', help="Set copyright owner.")
202+
args = parser.parse_args()
203+
204+
if not args.copyright:
205+
print(f"{colorama.Fore.YELLOW}Warning! No copyright owner was specified. Defaulting to \"Arm Limited\". "
206+
f"Please set via --copyright, or edit output.{colorama.Style.RESET_ALL}")
207+
208+
if args.template:
209+
with open(args.template, "r") as tmpl_file:
210+
tmpl = tmpl_file.read()
211+
else:
212+
tmpl = DAPLINK_TEMPLATE
213+
214+
with open(args.elf_path, "rb") as file_handle:
215+
algo = PackFlashAlgoGenerator(file_handle)
216+
217+
print(algo.flash_info)
218+
219+
if args.info_only:
220+
return
221+
222+
pack_file = None
223+
if args.pack_path and os.path.exists(args.pack_path) and 'cmsis-pack-manager' in args.pack_path:
224+
(rest, version) = (os.path.split(args.pack_path))
225+
(rest, pack) = (os.path.split(rest))
226+
(_, vendor) = (os.path.split(rest))
227+
pack_file = '%s.%s.%s' % (vendor, pack, version)
228+
elif args.pack_path:
229+
pack_file = os.path.split(args.pack_path)[-1]
230+
else:
231+
print(f"{colorama.Fore.YELLOW}Warning! No CMSIS Pack was set."
232+
f"Please set the path or file name of the CMSIS pack via --pack-path.{colorama.Style.RESET_ALL}")
233+
234+
file_handle.seek(0)
235+
flm_content = file_handle.read()
236+
hash = hashlib.sha256()
237+
hash.update(flm_content)
238+
239+
# Allocate stack after algo and its rw data, with top and bottom rounded to 256 bytes.
240+
stack_base = args.blob_start + HEADER_SIZE + algo.rw_start + algo.rw_size
241+
stack_base = (stack_base + 7) // 8 * 8
242+
sp = stack_base + args.stack_size
243+
sp = (sp + 255) // 256 * 256
244+
245+
data_dict = {
246+
'filename': os.path.split(args.elf_path)[-1],
247+
'digest': hash.hexdigest(),
248+
'file_size': len(flm_content),
249+
'pack_file': pack_file,
250+
'name': os.path.splitext(os.path.split(args.elf_path)[-1])[0],
251+
'prog_header': BLOB_HEADER,
252+
'header_size': HEADER_SIZE,
253+
'entry': args.blob_start,
254+
'stack_pointer': sp,
255+
'year': datetime.now().year,
256+
'copyright_owner': args.copyright or "Arm Limited",
257+
}
258+
259+
text = algo.process_template(tmpl, data_dict)
260+
261+
with open(args.output, "w") as file_handle:
262+
file_handle.write(text)
263+
264+
print(f"Wrote flash algo dict to {args.output}")
265+
266+
if __name__ == '__main__':
267+
main()

0 commit comments

Comments
 (0)