nrfhex.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. # Copyright (c) 2015, Nordic Semiconductor
  2. # All rights reserved.
  3. #
  4. # Redistribution and use in source and binary forms, with or without
  5. # modification, are permitted provided that the following conditions are met:
  6. #
  7. # * Redistributions of source code must retain the above copyright notice, this
  8. # list of conditions and the following disclaimer.
  9. #
  10. # * Redistributions in binary form must reproduce the above copyright notice,
  11. # this list of conditions and the following disclaimer in the documentation
  12. # and/or other materials provided with the distribution.
  13. #
  14. # * Neither the name of Nordic Semiconductor ASA nor the names of its
  15. # contributors may be used to endorse or promote products derived from
  16. # this software without specific prior written permission.
  17. #
  18. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  19. # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  20. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  22. # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  23. # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  24. # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  25. # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  26. # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  27. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. from nordicsemi.dfu import intelhex
  29. from struct import unpack
  30. class nRFHex(intelhex.IntelHex):
  31. """
  32. Converts and merges .hex and .bin files into one .bin file.
  33. """
  34. info_struct_address_base = 0x00003000
  35. info_struct_address_offset = 0x1000
  36. info_struct_magic_number = 0x51B1E5DB
  37. info_struct_magic_number_offset = 0x004
  38. s1x0_mbr_end_address = 0x1000
  39. s132_mbr_end_address = 0x3000
  40. def __init__(self, source, bootloader=None):
  41. """
  42. Constructor that requires a firmware file path.
  43. Softdevices can take an optional bootloader file path as parameter.
  44. :param str source: The file path for the firmware
  45. :param str bootloader: Optional file path to bootloader firmware
  46. :return: None
  47. """
  48. super(nRFHex, self).__init__()
  49. self.file_format = 'hex'
  50. if source.endswith('.bin'):
  51. self.file_format = 'bin'
  52. self.loadfile(source, self.file_format)
  53. self._removeuicr()
  54. self.bootloaderhex = None
  55. if bootloader is not None:
  56. self.bootloaderhex = nRFHex(bootloader)
  57. def _removeuicr(self):
  58. uicr_start_address = 0x10000000
  59. maxaddress = self.maxaddr()
  60. if maxaddress >= uicr_start_address:
  61. for i in range(uicr_start_address, maxaddress + 1):
  62. self._buf.pop(i, 0)
  63. def address_has_magic_number(self, address):
  64. try:
  65. potential_magic_number = self.gets(address, 4)
  66. potential_magic_number = unpack('L', potential_magic_number)[0]
  67. return nRFHex.info_struct_magic_number == potential_magic_number
  68. except Exception:
  69. return False
  70. def get_softdevice_variant(self):
  71. potential_magic_number_address = nRFHex.info_struct_address_base + nRFHex.info_struct_magic_number_offset
  72. if self.address_has_magic_number(potential_magic_number_address):
  73. return "s1x0"
  74. for i in xrange(4):
  75. potential_magic_number_address += nRFHex.info_struct_address_offset
  76. if self.address_has_magic_number(potential_magic_number_address):
  77. return "s132"
  78. return "unknown"
  79. def get_mbr_end_address(self):
  80. softdevice_variant = self.get_softdevice_variant()
  81. if softdevice_variant == "s132":
  82. return nRFHex.s132_mbr_end_address
  83. else:
  84. return nRFHex.s1x0_mbr_end_address
  85. def minaddr(self):
  86. min_address = super(nRFHex, self).minaddr()
  87. # Lower addresses are reserved for master boot record
  88. if self.file_format != 'bin':
  89. min_address = max(self.get_mbr_end_address(), min_address)
  90. return min_address
  91. def size(self):
  92. """
  93. Returns the size of the source.
  94. :return: int
  95. """
  96. min_address = self.minaddr()
  97. max_address = self.maxaddr()
  98. size = max_address - min_address + 1
  99. # Round up to nearest word
  100. word_size = 4
  101. number_of_words = (size + (word_size - 1)) / word_size
  102. size = number_of_words * word_size
  103. return size
  104. def bootloadersize(self):
  105. """
  106. Returns the size of the bootloader.
  107. :return: int
  108. """
  109. if self.bootloaderhex is None:
  110. return 0
  111. return self.bootloaderhex.size()
  112. def tobinfile(self, fobj, start=None, end=None, pad=None, size=None):
  113. """
  114. Writes a binary version of source and bootloader respectivly to fobj which could be a
  115. file object or a file path.
  116. :param str fobj: File path or object the function writes to
  117. :return: None
  118. """
  119. # If there is a bootloader this will make the recursion call use the samme file object.
  120. if getattr(fobj, "write", None) is None:
  121. fobj = open(fobj, "wb")
  122. close_fd = True
  123. else:
  124. close_fd = False
  125. start_address = self.minaddr()
  126. size = self.size()
  127. super(nRFHex, self).tobinfile(fobj, start=start_address, size=size)
  128. if self.bootloaderhex is not None:
  129. self.bootloaderhex.tobinfile(fobj)
  130. if close_fd:
  131. fobj.close()