Merge pull request #9317 from naveenkaje/tools_bootloader_script

tools: support cases where bootloader is in chunks
pull/9856/head
Cruz Monrreal 2019-02-26 22:49:42 -06:00 committed by GitHub
commit e77f03cfbc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 153 additions and 24 deletions

9
tools/build_api.py Normal file → Executable file
View File

@ -423,6 +423,10 @@ def merge_region_list(region_list, destination, notify, config, padding=b'\xFF')
merged = IntelHex()
_, format = splitext(destination)
notify.info("Merging Regions")
# Merged file list: Keep track of binary/hex files that we have already
# merged. e.g In some cases, bootloader may be split into multiple parts, but
# all internally referring to the same bootloader file.
merged_list = []
for region in region_list:
if region.active and not region.filename:
@ -432,7 +436,7 @@ def merge_region_list(region_list, destination, notify, config, padding=b'\xFF')
header_filename = header_basename + "_header.hex"
_fill_header(region_list, region).tofile(header_filename, format='hex')
region = region._replace(filename=header_filename)
if region.filename:
if region.filename and (region.filename not in merged_list):
notify.info(" Filling region %s with %s" % (region.name, region.filename))
part = intelhex_offset(region.filename, offset=region.start)
part.start_addr = None
@ -443,7 +447,10 @@ def merge_region_list(region_list, destination, notify, config, padding=b'\xFF')
if part_size > region.size:
raise ToolException("Contents of region %s does not fit"
% region.name)
merged_list.append(region.filename)
merged.merge(part)
elif region.filename in merged_list:
notify.info(" Skipping %s as it is merged previously" % (region.name))
# Hex file can have gaps, so no padding needed. While other formats may
# need padding. Iterate through segments and pad the gaps.

73
tools/config/__init__.py Normal file → Executable file
View File

@ -780,13 +780,29 @@ class Config(object):
return (start, region)
@staticmethod
def _assign_new_offset(rom_start, start, new_offset, region_name):
def _assign_new_offset(rom_start, new_offset, region_name, regions):
newstart = rom_start + integer(new_offset, 0)
if newstart < start:
raise ConfigException(
"Can not place %r region inside previous region" % region_name)
for s, e in regions:
if newstart > s and newstart < e:
raise ConfigException(
"Can not place %r region inside previous region" % region_name)
return newstart
@staticmethod
def _get_end_address(region_list, start_address, rom_end):
"""Given a start address and set of regions, sort the
regions and then compute the end address.
The end address is either rom_end or beginning of the
next section, whichever is smaller
"""
# Sort the list by starting address
region_list = sorted(region_list, key=lambda x:x[0])
for s, e in region_list:
if start_address < s:
return s
return rom_end
def _generate_bootloader_build(self, rom_memories):
rom_start, rom_size = rom_memories.get('ROM')
start = rom_start
@ -803,26 +819,41 @@ class Config(object):
if part.minaddr() != rom_start:
raise ConfigException("bootloader executable does not "
"start at 0x%x" % rom_start)
regions = part.segments()
# find the last valid address that's within rom_end and use that
# to compute the bootloader size
end_address = None
for start, stop in part.segments():
if (stop < rom_end):
end_address = stop
else:
break
if end_address == None:
raise ConfigException("bootloader segments don't fit within rom region")
part_size = Config._align_ceiling(end_address, self.sectors) - rom_start
yield Region("bootloader", rom_start, part_size, False,
filename)
# we have multiple parts in bootloader. Treat each of them as
# a different region (BLP1, BLP2 ...)
if len(part.segments()) > 1:
end_address = None
part_count = 0
for start, stop in part.segments():
part_count += 1
if (stop < rom_end):
end_address = stop
else:
break
if end_address == None:
raise ConfigException("bootloader segments don't fit within rom")
part_size = Config._align_ceiling(end_address, self.sectors) - rom_start
# Generate the region in the loop (bootloader0, bootloader1, ...)
yield Region("bootloader"+str(part_count), start, part_size, False, filename)
else:
# Number of segments is 1
_, end_address = part.segments()[0]
if (end_address > rom_end):
raise ConfigException("bootloader segments don't fit within rom")
part_size = Config._align_ceiling(end_address, self.sectors) - rom_start
yield Region("bootloader", rom_start, part_size, False,
filename)
start = rom_start + part_size
if self.target.header_format:
if self.target.header_offset:
start = self._assign_new_offset(
rom_start, start, self.target.header_offset, "header")
rom_start, self.target.header_offset, "header", regions)
start, region = self._make_header_region(
start, self.target.header_format)
yield region._replace(filename=self.target.header_format)
@ -832,14 +863,14 @@ class Config(object):
new_size = Config._align_floor(start + new_size, self.sectors) - start
if self.target.app_offset:
start = self._assign_new_offset(rom_start, start, self.target.app_offset, "application")
start = self._assign_new_offset(rom_start, self.target.app_offset, "application", regions)
yield Region("application", start, new_size, True, None)
start += new_size
if self.target.header_format and not self.target.bootloader_img:
if self.target.header_offset:
start = self._assign_new_offset(
rom_start, start, self.target.header_offset, "header")
rom_start, self.target.header_offset, "header", regions)
start, region = self._make_header_region(
start, self.target.header_format)
yield region
@ -849,8 +880,10 @@ class Config(object):
else:
if self.target.app_offset:
start = self._assign_new_offset(
rom_start, start, self.target.app_offset, "application")
yield Region("application", start, rom_end - start,
rom_start, self.target.app_offset, "application", regions)
# compute the end address of the application region based on existing segments
end = self._get_end_address(regions, start, rom_end)
yield Region("application", start, end - start,
True, None)
if start > rom_end:
raise ConfigException("Not enough memory on device to fit all "

94
tools/test/build_api/build_api_test.py Normal file → Executable file
View File

@ -22,10 +22,10 @@ from tools.build_api import prepare_toolchain, build_project, build_library, mer
from tools.resources import Resources
from tools.toolchains import TOOLCHAINS
from tools.notifier.mock import MockNotifier
from tools.config import Region
from tools.config import Region, Config, ConfigException
from tools.utils import ToolException
from intelhex import IntelHex
import intelhex
"""
Tests for build_api.py
"""
@ -247,7 +247,10 @@ class BuildApiTests(unittest.TestCase):
@patch('tools.config')
def test_merge_region_no_fit(self, mock_config, mock_intelhex_offset):
"""
Test that merge region fails as expected when part size overflows region size.
Test that merge_region_list call fails when part size overflows region size.
:param mock_config: config object that is mocked.
:param mock_intelhex_offset: mocked intel_hex_offset call.
:return:
"""
max_addr = 87444
# create a dummy hex file with above max_addr
@ -274,5 +277,90 @@ class BuildApiTests(unittest.TestCase):
self.assertTrue(toolexception, "Expected ToolException not raised")
@patch('tools.config.exists')
@patch('tools.config.isabs')
@patch('tools.config.intelhex_offset')
def test_bl_pieces(self, mock_intelhex_offset, mock_exists, mock_isabs):
"""
:param mock_intelhex_offset: mock intel_hex_ofset call
:param mock_exists: mock the file exists call
:param mock_isabs: mock the isabs call
:return:
"""
"""
Test that merge region fails as expected when part size overflows region size.
"""
cfg = Config('NRF52_DK')
mock_exists.return_value = True
mock_isabs.return_value = True
max = 0x960
#create mock MBR and BL and merge them
mbr = IntelHex()
for v in range(max):
mbr[v] = v
bl = IntelHex()
min = 0x16000
max = 0x22000
for v in range(min, max):
bl[v] = v
mbr.merge(bl)
mock_intelhex_offset.return_value = mbr
# Place application within the bootloader and verify
# that config exception is generated
cfg.target.bootloader_img = True
cfg.target.app_offset = min + 0x200
cfg.target.restrict_size = '4096'
ce = False
if cfg.has_regions:
try:
for r in list(cfg.regions):
print(r)
except ConfigException:
ce = True
self.assertTrue(ce)
@patch('tools.config.exists')
@patch('tools.config.isabs')
@patch('tools.config.intelhex_offset')
def test_bl_too_large(self, mock_intelhex_offset, mock_exists, mock_isabs):
"""
Create a BL that's too large to fit in ROM and test that exception is
generated.
:param mock_intelhex_offset: mock intel hex
:param mock_exists: mock the file exists call
:param mock_isabs: mock the isabs call
:return:
"""
cfg = Config('NRF52_DK')
mock_exists.return_value = True
mock_isabs.return_value = True
# setup the hex file
bl = IntelHex()
min = 0x0
max = 0x88000
for v in range(max):
bl[v] = v
mock_intelhex_offset.return_value = bl
cfg.target.bootloader_img = True
ce = False
if cfg.has_regions:
try:
for r in list(cfg.regions):
print(r)
except ConfigException as e:
print("args %s" % (e.args))
if (e.args[0] == "bootloader segments don't fit within rom"):
ce = True
self.assertTrue(ce)
if __name__ == '__main__':
unittest.main()

1
tools/toolchains/__init__.py Normal file → Executable file
View File

@ -742,6 +742,7 @@ class mbedToolchain:
if self.config.has_regions:
try:
regions = list(self.config.regions)
regions.sort(key=lambda x:x.start)
self.notify.info("Using ROM region%s %s in this build." % (
"s" if len(regions) > 1 else "",
", ".join(r.name for r in regions)