Skip to content

Commit 3b1c1b6

Browse files
committed
Enable boot-loader builds
To enable a boot-loader style application, override "targets.bootloader_exec" or "targets.restrict_size" on a particular target. These parameters are a bin or hex file, and an integer, in bytes, respectively. If either override is present, then an application region is created after the boot-loader region, when "targets.bootloader_exec" is present, and before post-application, when "targets.restric_size" is present. The size of the boot-loader region is read from the file provided in the configuration.
1 parent aa6d673 commit 3b1c1b6

File tree

3 files changed

+151
-10
lines changed

3 files changed

+151
-10
lines changed

tools/build_api.py

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@
1919
import tempfile
2020
from types import ListType
2121
from shutil import rmtree
22-
from os.path import join, exists, dirname, basename, abspath, normpath
23-
from os import linesep, remove
22+
from os.path import join, exists, dirname, basename, abspath, normpath, splitext
23+
from os import linesep, remove, makedirs
2424
from time import time
25+
from intelhex import IntelHex
2526

2627
from tools.utils import mkdir, run_cmd, run_cmd_ext, NotSupportedException,\
27-
ToolException, InvalidReleaseTargetException
28+
ToolException, InvalidReleaseTargetException, intelhex_offset
2829
from tools.paths import MBED_CMSIS_PATH, MBED_TARGETS_PATH, MBED_LIBRARIES,\
2930
MBED_HEADER, MBED_DRIVERS, MBED_PLATFORM, MBED_HAL, MBED_CONFIG_FILE,\
3031
MBED_LIBRARIES_DRIVERS, MBED_LIBRARIES_PLATFORM, MBED_LIBRARIES_HAL,\
@@ -274,6 +275,29 @@ def get_mbed_official_release(version):
274275

275276
return mbed_official_release
276277

278+
def add_regions_to_profile(profile, config, toolchain_class):
279+
"""Add regions to the build profile, if there are any.
280+
281+
Positional Arguments:
282+
profile - the profile to update
283+
config - the configuration object that owns the region
284+
toolchain_class - the class of the toolchain being used
285+
"""
286+
regions = list(config.regions)
287+
for region in regions:
288+
for define in [(region.name.upper() + "_ADDR", region.start),
289+
(region.name.upper() + "_SIZE", region.size)]:
290+
profile["common"].append("-D%s=0x%x" % define)
291+
active_region = [r for r in regions if r.active][0]
292+
for define in [("MBED_APP_START", active_region.start),
293+
("MBED_APP_SIZE", active_region.size)]:
294+
profile["ld"].append(toolchain_class.make_ld_define(*define))
295+
296+
print("Using regions in this build:")
297+
for region in regions:
298+
print(" Region %s size 0x%x, offset 0x%x"
299+
% (region.name, region.size, region.start))
300+
277301

278302
def prepare_toolchain(src_paths, target, toolchain_name,
279303
macros=None, clean=False, jobs=1,
@@ -307,14 +331,16 @@ def prepare_toolchain(src_paths, target, toolchain_name,
307331
# If the configuration object was not yet created, create it now
308332
config = config or Config(target, src_paths, app_config=app_config)
309333
target = config.target
310-
311-
# Toolchain instance
312334
try:
313-
toolchain = TOOLCHAIN_CLASSES[toolchain_name](
314-
target, notify, macros, silent,
315-
extra_verbose=extra_verbose, build_profile=build_profile)
335+
cur_tc = TOOLCHAIN_CLASSES[toolchain_name]
316336
except KeyError:
317337
raise KeyError("Toolchain %s not supported" % toolchain_name)
338+
if config.has_regions:
339+
add_regions_to_profile(build_profile, config, cur_tc)
340+
341+
# Toolchain instance
342+
toolchain = cur_tc(target, notify, macros, silent,
343+
extra_verbose=extra_verbose, build_profile=build_profile)
318344

319345
toolchain.config = config
320346
toolchain.jobs = jobs
@@ -323,6 +349,41 @@ def prepare_toolchain(src_paths, target, toolchain_name,
323349

324350
return toolchain
325351

352+
def merge_region_list(region_list, destination, padding=b'\xFF'):
353+
"""Merege the region_list into a single image
354+
355+
Positional Arguments:
356+
region_list - list of regions, which should contain filenames
357+
destination - file name to write all regions to
358+
padding - bytes to fill gapps with
359+
"""
360+
merged = IntelHex()
361+
362+
print("Merging Regions:")
363+
364+
for region in region_list:
365+
if region.active and not region.filename:
366+
raise ToolException("Active region has no contents: No file found.")
367+
if region.filename:
368+
print(" Filling region %s with %s" % (region.name, region.filename))
369+
part = intelhex_offset(region.filename, offset=region.start)
370+
part_size = (part.maxaddr() - part.minaddr()) + 1
371+
if part_size > region.size:
372+
raise ToolException("Contents of region %s does not fit"
373+
% region.name)
374+
merged.merge(part)
375+
pad_size = region.size - part_size
376+
if pad_size > 0 and region != region_list[-1]:
377+
print(" Padding region %s with 0x%x bytes" % (region.name, pad_size))
378+
merged.puts(merged.maxaddr() + 1, padding * pad_size)
379+
380+
if not exists(dirname(destination)):
381+
makedirs(dirname(destination))
382+
print("Space used after regions merged: 0x%x" %
383+
(merged.maxaddr() - merged.minaddr() + 1))
384+
with open(destination, "wb+") as output:
385+
merged.tofile(output, format='bin')
386+
326387
def scan_resources(src_paths, toolchain, dependencies_paths=None,
327388
inc_dirs=None, base_path=None):
328389
""" Scan resources using initialized toolcain
@@ -453,7 +514,15 @@ def build_project(src_paths, build_path, target, toolchain_name,
453514
resources.objects.extend(objects)
454515

455516
# Link Program
456-
res, _ = toolchain.link_program(resources, build_path, name)
517+
if toolchain.config.has_regions:
518+
res, _ = toolchain.link_program(resources, build_path, name + "_application")
519+
region_list = list(toolchain.config.regions)
520+
region_list = [r._replace(filename=res) if r.active else r
521+
for r in region_list]
522+
res = join(build_path, name) + ".bin"
523+
merge_region_list(region_list, res)
524+
else:
525+
res, _ = toolchain.link_program(resources, build_path, name)
457526

458527
memap_instance = getattr(toolchain, 'memap_instance', None)
459528
memap_table = ''

tools/config.py

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@
1818
from copy import deepcopy
1919
import os
2020
import sys
21+
from collections import namedtuple
22+
from os.path import splitext
23+
from intelhex import IntelHex
2124
# Implementation of mbed configuration mechanism
22-
from tools.utils import json_file_to_dict
25+
from tools.utils import json_file_to_dict, intelhex_offset
26+
from tools.arm_pack_manager import Cache
2327
from tools.targets import CUMULATIVE_ATTRIBUTES, TARGET_MAP, \
2428
generate_py_target, get_resolution_order
2529

@@ -328,6 +332,8 @@ def _process_macros(mlist, macros, unit_name, unit_kind):
328332
macros[macro.macro_name] = macro
329333

330334

335+
Region = namedtuple("Region", "name start size active filename")
336+
331337
class Config(object):
332338
"""'Config' implements the mbed configuration mechanism"""
333339

@@ -346,6 +352,8 @@ class Config(object):
346352
"macros", "__config_path"])
347353
}
348354

355+
__unused_overrides = set(["target.bootloader_exec", "target.restrict_size"])
356+
349357
# Allowed features in configurations
350358
__allowed_features = [
351359
"UVISOR", "BLE", "CLIENT", "IPV4", "LWIP", "COMMON_PAL", "STORAGE", "NANOSTACK",
@@ -455,6 +463,52 @@ def add_config_files(self, flist):
455463
self.lib_config_data[cfg["name"]]["__config_path"]))
456464
self.lib_config_data[cfg["name"]] = cfg
457465

466+
@property
467+
def has_regions(self):
468+
"""Does this config have regions defined?"""
469+
if 'target_overrides' in self.app_config_data:
470+
target_overrides = self.app_config_data['target_overrides'].get(
471+
self.target.name, {})
472+
return ('target.bootloader_exec' in target_overrides or
473+
'target.restrict_size' in target_overrides)
474+
else:
475+
return False
476+
477+
@property
478+
def regions(self):
479+
"""Generate a list of regions from the config"""
480+
cmsis_part = Cache(False, False).index[self.target.device_name]
481+
start = 0
482+
target_overrides = self.app_config_data['target_overrides'].get(
483+
self.target.name, {})
484+
try:
485+
size = int(cmsis_part['memory']['IROM1']['size'], 0)
486+
rom_start = int(cmsis_part['memory']['IROM1']['start'], 0)
487+
except KeyError:
488+
raise ConfigException("Not enough information in CMSIS packs to "
489+
"build a bootloader project")
490+
if 'target.bootloader_exec' in target_overrides:
491+
filename = target_overrides['target.bootloader_exec']
492+
part = intelhex_offset(filename, offset=rom_start)
493+
if part.minaddr() != rom_start:
494+
raise ConfigException("bootloader executable does not "
495+
"start at 0x%x" % rom_start)
496+
part_size = (part.maxaddr() - part.minaddr()) + 1
497+
yield Region("bootloader", rom_start + start, part_size, False,
498+
filename)
499+
start += part_size
500+
if 'target.restrict_size' in target_overrides:
501+
new_size = int(target_overrides['target.restrict_size'], 0)
502+
yield Region("application", rom_start + start, new_size, True, None)
503+
start += new_size
504+
yield Region("post_application", rom_start +start, size - start,
505+
False, None)
506+
else:
507+
yield Region("application", rom_start + start, size - start, True,
508+
None)
509+
if start > size:
510+
raise ConfigException("Not enogh memeroy on device to fit all "
511+
"application regions")
458512

459513
def _process_config_and_overrides(self, data, params, unit_name, unit_kind):
460514
"""Process "config_parameters" and "target_config_overrides" into a
@@ -508,6 +562,8 @@ def _process_config_and_overrides(self, data, params, unit_name, unit_kind):
508562
if full_name in params:
509563
params[full_name].set_value(val, unit_name, unit_kind,
510564
label)
565+
elif name in self.__unused_overrides:
566+
pass
511567
else:
512568
self.config_errors.append(
513569
ConfigException(
@@ -560,6 +616,8 @@ def get_target_config_data(self):
560616
rel_names = [tgt for tgt, _ in
561617
get_resolution_order(self.target.json_data, tname,
562618
[])]
619+
if full_name in self.__unused_overrides:
620+
continue
563621
if (full_name not in params) or \
564622
(params[full_name].defined_by[7:] not in rel_names):
565623
raise ConfigException(

tools/utils.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import json
2929
from collections import OrderedDict
3030
import logging
31+
from intelhex import IntelHex
3132

3233
def remove_if_in(lst, thing):
3334
if thing in lst:
@@ -514,3 +515,16 @@ def print_large_string(large_string):
514515
else:
515516
end_index = ((string_part + 1) * string_limit) - 1
516517
print large_string[start_index:end_index],
518+
519+
def intelhex_offset(filename, offset):
520+
"""Load a hex or bin file at a particular offset"""
521+
_, inteltype = splitext(filename)
522+
ih = IntelHex()
523+
if inteltype == ".bin":
524+
ih.loadbin(filename, offset=offset)
525+
elif inteltype == ".hex":
526+
ih.loadhex(filename)
527+
else:
528+
raise ToolException("File %s does not have a known binary file type"
529+
% filename)
530+
return ih

0 commit comments

Comments
 (0)