Skip to content

tools: support cases where bootloader is in chunks #9317

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion tools/build_api.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand All @@ -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.
Expand Down
73 changes: 53 additions & 20 deletions tools/config/__init__.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -780,13 +780,29 @@ def _make_header_region(self, start, header_format, offset=None):
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
Expand All @@ -803,26 +819,41 @@ def _generate_bootloader_build(self, rom_memories):
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)
Expand All @@ -832,14 +863,14 @@ def _generate_bootloader_build(self, rom_memories):
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
Expand All @@ -849,8 +880,10 @@ def _generate_bootloader_build(self, rom_memories):
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 "
Expand Down
94 changes: 91 additions & 3 deletions tools/test/build_api/build_api_test.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
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
"""
Expand Down Expand Up @@ -247,7 +247,10 @@ def test_build_library_no_app_config(self, mock_prepare_toolchain, mock_exists,
@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
Expand All @@ -274,5 +277,90 @@ def test_merge_region_no_fit(self, mock_config, mock_intelhex_offset):
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 change: 1 addition & 0 deletions tools/toolchains/__init__.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,7 @@ def add_regions(self):
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)
Expand Down