Skip to content

Commit e77f03c

Browse files
author
Cruz Monrreal
authored
Merge pull request #9317 from naveenkaje/tools_bootloader_script
tools: support cases where bootloader is in chunks
2 parents 4b13c8a + a47cfd4 commit e77f03c

File tree

4 files changed

+153
-24
lines changed

4 files changed

+153
-24
lines changed

tools/build_api.py

100644100755
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,10 @@ def merge_region_list(region_list, destination, notify, config, padding=b'\xFF')
423423
merged = IntelHex()
424424
_, format = splitext(destination)
425425
notify.info("Merging Regions")
426+
# Merged file list: Keep track of binary/hex files that we have already
427+
# merged. e.g In some cases, bootloader may be split into multiple parts, but
428+
# all internally referring to the same bootloader file.
429+
merged_list = []
426430

427431
for region in region_list:
428432
if region.active and not region.filename:
@@ -432,7 +436,7 @@ def merge_region_list(region_list, destination, notify, config, padding=b'\xFF')
432436
header_filename = header_basename + "_header.hex"
433437
_fill_header(region_list, region).tofile(header_filename, format='hex')
434438
region = region._replace(filename=header_filename)
435-
if region.filename:
439+
if region.filename and (region.filename not in merged_list):
436440
notify.info(" Filling region %s with %s" % (region.name, region.filename))
437441
part = intelhex_offset(region.filename, offset=region.start)
438442
part.start_addr = None
@@ -443,7 +447,10 @@ def merge_region_list(region_list, destination, notify, config, padding=b'\xFF')
443447
if part_size > region.size:
444448
raise ToolException("Contents of region %s does not fit"
445449
% region.name)
450+
merged_list.append(region.filename)
446451
merged.merge(part)
452+
elif region.filename in merged_list:
453+
notify.info(" Skipping %s as it is merged previously" % (region.name))
447454

448455
# Hex file can have gaps, so no padding needed. While other formats may
449456
# need padding. Iterate through segments and pad the gaps.

tools/config/__init__.py

100644100755
Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -780,13 +780,29 @@ def _make_header_region(self, start, header_format, offset=None):
780780
return (start, region)
781781

782782
@staticmethod
783-
def _assign_new_offset(rom_start, start, new_offset, region_name):
783+
def _assign_new_offset(rom_start, new_offset, region_name, regions):
784784
newstart = rom_start + integer(new_offset, 0)
785-
if newstart < start:
786-
raise ConfigException(
787-
"Can not place %r region inside previous region" % region_name)
785+
786+
for s, e in regions:
787+
if newstart > s and newstart < e:
788+
raise ConfigException(
789+
"Can not place %r region inside previous region" % region_name)
788790
return newstart
789791

792+
@staticmethod
793+
def _get_end_address(region_list, start_address, rom_end):
794+
"""Given a start address and set of regions, sort the
795+
regions and then compute the end address.
796+
The end address is either rom_end or beginning of the
797+
next section, whichever is smaller
798+
"""
799+
# Sort the list by starting address
800+
region_list = sorted(region_list, key=lambda x:x[0])
801+
for s, e in region_list:
802+
if start_address < s:
803+
return s
804+
return rom_end
805+
790806
def _generate_bootloader_build(self, rom_memories):
791807
rom_start, rom_size = rom_memories.get('ROM')
792808
start = rom_start
@@ -803,26 +819,41 @@ def _generate_bootloader_build(self, rom_memories):
803819
if part.minaddr() != rom_start:
804820
raise ConfigException("bootloader executable does not "
805821
"start at 0x%x" % rom_start)
822+
regions = part.segments()
806823

807824
# find the last valid address that's within rom_end and use that
808825
# to compute the bootloader size
809-
end_address = None
810-
for start, stop in part.segments():
811-
if (stop < rom_end):
812-
end_address = stop
813-
else:
814-
break
815-
if end_address == None:
816-
raise ConfigException("bootloader segments don't fit within rom region")
817-
part_size = Config._align_ceiling(end_address, self.sectors) - rom_start
818826

819-
yield Region("bootloader", rom_start, part_size, False,
820-
filename)
827+
# we have multiple parts in bootloader. Treat each of them as
828+
# a different region (BLP1, BLP2 ...)
829+
if len(part.segments()) > 1:
830+
end_address = None
831+
part_count = 0
832+
for start, stop in part.segments():
833+
part_count += 1
834+
if (stop < rom_end):
835+
end_address = stop
836+
else:
837+
break
838+
if end_address == None:
839+
raise ConfigException("bootloader segments don't fit within rom")
840+
part_size = Config._align_ceiling(end_address, self.sectors) - rom_start
841+
# Generate the region in the loop (bootloader0, bootloader1, ...)
842+
yield Region("bootloader"+str(part_count), start, part_size, False, filename)
843+
else:
844+
# Number of segments is 1
845+
_, end_address = part.segments()[0]
846+
if (end_address > rom_end):
847+
raise ConfigException("bootloader segments don't fit within rom")
848+
part_size = Config._align_ceiling(end_address, self.sectors) - rom_start
849+
yield Region("bootloader", rom_start, part_size, False,
850+
filename)
851+
821852
start = rom_start + part_size
822853
if self.target.header_format:
823854
if self.target.header_offset:
824855
start = self._assign_new_offset(
825-
rom_start, start, self.target.header_offset, "header")
856+
rom_start, self.target.header_offset, "header", regions)
826857
start, region = self._make_header_region(
827858
start, self.target.header_format)
828859
yield region._replace(filename=self.target.header_format)
@@ -832,14 +863,14 @@ def _generate_bootloader_build(self, rom_memories):
832863
new_size = Config._align_floor(start + new_size, self.sectors) - start
833864

834865
if self.target.app_offset:
835-
start = self._assign_new_offset(rom_start, start, self.target.app_offset, "application")
866+
start = self._assign_new_offset(rom_start, self.target.app_offset, "application", regions)
836867

837868
yield Region("application", start, new_size, True, None)
838869
start += new_size
839870
if self.target.header_format and not self.target.bootloader_img:
840871
if self.target.header_offset:
841872
start = self._assign_new_offset(
842-
rom_start, start, self.target.header_offset, "header")
873+
rom_start, self.target.header_offset, "header", regions)
843874
start, region = self._make_header_region(
844875
start, self.target.header_format)
845876
yield region
@@ -849,8 +880,10 @@ def _generate_bootloader_build(self, rom_memories):
849880
else:
850881
if self.target.app_offset:
851882
start = self._assign_new_offset(
852-
rom_start, start, self.target.app_offset, "application")
853-
yield Region("application", start, rom_end - start,
883+
rom_start, self.target.app_offset, "application", regions)
884+
# compute the end address of the application region based on existing segments
885+
end = self._get_end_address(regions, start, rom_end)
886+
yield Region("application", start, end - start,
854887
True, None)
855888
if start > rom_end:
856889
raise ConfigException("Not enough memory on device to fit all "

tools/test/build_api/build_api_test.py

100644100755
Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@
2222
from tools.resources import Resources
2323
from tools.toolchains import TOOLCHAINS
2424
from tools.notifier.mock import MockNotifier
25-
from tools.config import Region
25+
from tools.config import Region, Config, ConfigException
2626
from tools.utils import ToolException
2727
from intelhex import IntelHex
28-
import intelhex
28+
2929
"""
3030
Tests for build_api.py
3131
"""
@@ -247,7 +247,10 @@ def test_build_library_no_app_config(self, mock_prepare_toolchain, mock_exists,
247247
@patch('tools.config')
248248
def test_merge_region_no_fit(self, mock_config, mock_intelhex_offset):
249249
"""
250-
Test that merge region fails as expected when part size overflows region size.
250+
Test that merge_region_list call fails when part size overflows region size.
251+
:param mock_config: config object that is mocked.
252+
:param mock_intelhex_offset: mocked intel_hex_offset call.
253+
:return:
251254
"""
252255
max_addr = 87444
253256
# create a dummy hex file with above max_addr
@@ -274,5 +277,90 @@ def test_merge_region_no_fit(self, mock_config, mock_intelhex_offset):
274277
self.assertTrue(toolexception, "Expected ToolException not raised")
275278

276279

280+
@patch('tools.config.exists')
281+
@patch('tools.config.isabs')
282+
@patch('tools.config.intelhex_offset')
283+
def test_bl_pieces(self, mock_intelhex_offset, mock_exists, mock_isabs):
284+
"""
285+
286+
:param mock_intelhex_offset: mock intel_hex_ofset call
287+
:param mock_exists: mock the file exists call
288+
:param mock_isabs: mock the isabs call
289+
:return:
290+
"""
291+
"""
292+
Test that merge region fails as expected when part size overflows region size.
293+
"""
294+
cfg = Config('NRF52_DK')
295+
mock_exists.return_value = True
296+
mock_isabs.return_value = True
297+
max = 0x960
298+
#create mock MBR and BL and merge them
299+
mbr = IntelHex()
300+
for v in range(max):
301+
mbr[v] = v
302+
303+
bl = IntelHex()
304+
min = 0x16000
305+
max = 0x22000
306+
for v in range(min, max):
307+
bl[v] = v
308+
mbr.merge(bl)
309+
310+
mock_intelhex_offset.return_value = mbr
311+
312+
# Place application within the bootloader and verify
313+
# that config exception is generated
314+
cfg.target.bootloader_img = True
315+
cfg.target.app_offset = min + 0x200
316+
cfg.target.restrict_size = '4096'
317+
318+
ce = False
319+
if cfg.has_regions:
320+
try:
321+
for r in list(cfg.regions):
322+
print(r)
323+
except ConfigException:
324+
ce = True
325+
326+
self.assertTrue(ce)
327+
328+
@patch('tools.config.exists')
329+
@patch('tools.config.isabs')
330+
@patch('tools.config.intelhex_offset')
331+
def test_bl_too_large(self, mock_intelhex_offset, mock_exists, mock_isabs):
332+
"""
333+
Create a BL that's too large to fit in ROM and test that exception is
334+
generated.
335+
:param mock_intelhex_offset: mock intel hex
336+
:param mock_exists: mock the file exists call
337+
:param mock_isabs: mock the isabs call
338+
:return:
339+
"""
340+
cfg = Config('NRF52_DK')
341+
mock_exists.return_value = True
342+
mock_isabs.return_value = True
343+
344+
# setup the hex file
345+
bl = IntelHex()
346+
min = 0x0
347+
max = 0x88000
348+
for v in range(max):
349+
bl[v] = v
350+
mock_intelhex_offset.return_value = bl
351+
cfg.target.bootloader_img = True
352+
ce = False
353+
354+
if cfg.has_regions:
355+
try:
356+
for r in list(cfg.regions):
357+
print(r)
358+
except ConfigException as e:
359+
print("args %s" % (e.args))
360+
if (e.args[0] == "bootloader segments don't fit within rom"):
361+
ce = True
362+
363+
self.assertTrue(ce)
364+
277365
if __name__ == '__main__':
278366
unittest.main()

tools/toolchains/__init__.py

100644100755
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,7 @@ def add_regions(self):
742742
if self.config.has_regions:
743743
try:
744744
regions = list(self.config.regions)
745+
regions.sort(key=lambda x:x.start)
745746
self.notify.info("Using ROM region%s %s in this build." % (
746747
"s" if len(regions) > 1 else "",
747748
", ".join(r.name for r in regions)

0 commit comments

Comments
 (0)