Arrow / Mbed OS DAPLink Reset
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers post_build_script.py Source File

post_build_script.py

00001 #
00002 # DAPLink Interface Firmware
00003 # Copyright (c) 2009-2019, ARM Limited, All Rights Reserved
00004 # SPDX-License-Identifier: Apache-2.0
00005 #
00006 # Licensed under the Apache License, Version 2.0 (the "License"); you may
00007 # not use this file except in compliance with the License.
00008 # You may obtain a copy of the License at
00009 #
00010 # http://www.apache.org/licenses/LICENSE-2.0
00011 #
00012 # Unless required by applicable law or agreed to in writing, software
00013 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00014 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015 # See the License for the specific language governing permissions and
00016 # limitations under the License.
00017 #
00018 
00019 from __future__ import absolute_import
00020 from __future__ import print_function
00021 
00022 import argparse
00023 import itertools
00024 import binascii
00025 import struct
00026 import intelhex
00027 import offset_update
00028 from os.path import dirname, join
00029 from flash_algo import PackFlashAlgo
00030 
00031 VECTOR_FMT = "<7I"
00032 CHECKSUM_FMT = "<1I"
00033 CHECKSUM_OFFSET = 0x1C
00034 TARGET_INFO_OFFSET = 13*4
00035 ALIGN_PADS = 4
00036 
00037 def ranges(i):
00038     for _, b in itertools.groupby(enumerate(i), lambda x_y: x_y[1] - x_y[0]):
00039         b = list(b)
00040         yield b[0][1], b[-1][1]
00041 
00042 def post_build_script(input_file, output_file, board_id=None, family_id=None, bin_offset=None, flash_algo_file=None, target_ram_start=None, target_ram_end=None, flash_blob_entry="0x20000000"):
00043     output_format_file = '-'.join(filter(None, (output_file, board_id, family_id, bin_offset)))
00044     print(output_format_file)
00045     output_file_hex = output_format_file + ".hex"
00046     output_file_binary = output_format_file + ".bin"
00047     output_file_txt = output_format_file + ".txt"
00048     output_file_c = output_format_file + ".c"
00049     output_file_c_generic = join(dirname(output_file), "bootloader_image.c")
00050     output_file_legacy = output_format_file + "_legacy_0x8000.bin"
00051     output_file_legacy_5000 = output_format_file + "_legacy_0x5000.bin"
00052     output_file_legacy_txt = output_format_file + "_legacy.txt"
00053 
00054     # Read in hex file
00055     new_hex_file = intelhex.IntelHex()
00056     new_hex_file.padding = 0xFF
00057 
00058     if input_file.lower().endswith('.bin'):
00059         if bin_offset is not None:
00060             new_hex_file.loadbin(input_file, offset=int(bin_offset, 16))
00061         else:
00062             new_hex_file.loadbin(input_file)
00063     else:   #always assume hex format
00064         new_hex_file.fromfile(input_file, format='hex')
00065 
00066 
00067     # Get the starting and ending address
00068     addresses = new_hex_file.addresses()
00069     addresses.sort()
00070     start_end_pairs = list(ranges(addresses))
00071     regions = len(start_end_pairs)
00072     assert regions == 1, ("Error - only 1 region allowed in "
00073                           "hex file %i found." % regions)
00074     start, end = start_end_pairs[0]
00075 
00076     pack_flash_algo = None
00077     if flash_algo_file is not None:
00078         with open(flash_algo_file, "rb") as file_handle:
00079             pack_flash_algo = PackFlashAlgo(file_handle.read())
00080 
00081     # Checksum the vector table
00082     #
00083     # Note this is only required for NXP devices but
00084     # it doesn't hurt to checksum all builds
00085 
00086     # Compute a checksum on the first 7 vector nvic vectors
00087     vector_size = struct.calcsize(VECTOR_FMT)
00088     vector_data = new_hex_file.tobinarray(start=start, size=vector_size)
00089     vectors = struct.unpack(VECTOR_FMT, vector_data)
00090     assert len(vectors) == 7, "Incorrect size of %i" % len(vectors)
00091     checksum = 0
00092     for vector in vectors:
00093         checksum += vector
00094     checksum = (~checksum + 1) & 0xFFFFFFFF  # Two's compliment
00095     # Write checksum back to hex
00096     csum_start = CHECKSUM_OFFSET + start
00097     csum_data = struct.pack(CHECKSUM_FMT, checksum)
00098     assert len(csum_data) == 4
00099     new_hex_file.puts(csum_start, csum_data)
00100 
00101     print("board_id", board_id)
00102     print("family_id", family_id)
00103     print("bin_offset", bin_offset)
00104     if board_id is not None or family_id is not None:
00105         target_info_addr = new_hex_file.gets(start + TARGET_INFO_OFFSET, 4)
00106         target_addr_unpack = struct.unpack("<1I",target_info_addr)[0]
00107         print("board_info offset: ",hex(target_addr_unpack - start))
00108         #family_id is in integer hex
00109         if family_id is not None:
00110             new_hex_file.puts(target_addr_unpack + 2,struct.pack('<1H',int(family_id, 16)))
00111         #board_id is in string hex
00112         if board_id is not None:
00113             new_hex_file.puts(target_addr_unpack + 4,struct.pack('4s',"%.04X" % int(board_id, 16)))
00114         if pack_flash_algo is not None:
00115             blob_header = (0xE00ABE00, 0x062D780D, 0x24084068, 0xD3000040, 0x1E644058, 0x1C49D1FA, 0x2A001E52, 0x4770D1F2)
00116             stack_size = 0x200
00117             region_info_fmt = '5I'
00118             region_info_total = 10
00119             target_cfg_fmt = '3I'+ region_info_fmt*region_info_total*2 + 'IHBB'
00120             sector_info_fmt = '2I'
00121             sector_info_len = len(pack_flash_algo.sector_sizes)
00122             program_target_fmt = '14I'
00123             flash_blob_entry = int(flash_blob_entry, 16)
00124             blob_pad_size = ((len(pack_flash_algo.algo_data) + ALIGN_PADS -1) // ALIGN_PADS * ALIGN_PADS) - len(pack_flash_algo.algo_data)
00125             blob_header_size = len(blob_header) * 4
00126             total_struct_size = blob_header_size + len(pack_flash_algo.algo_data) + blob_pad_size + sector_info_len*struct.calcsize(sector_info_fmt) + struct.calcsize(program_target_fmt) + struct.calcsize(target_cfg_fmt)
00127             flash_blob_addr = end + 1 - 4 -  total_struct_size #make room for crc
00128             print("flash_blob offset:", hex(flash_blob_addr - start))
00129             new_hex_file.puts(flash_blob_addr, struct.pack('<'+'I'*len(blob_header), *blob_header))
00130             new_hex_file.puts(flash_blob_addr + blob_header_size, pack_flash_algo.algo_data + "\x00" * blob_pad_size)
00131             sector_info_addr = flash_blob_addr+blob_header_size + len(pack_flash_algo.algo_data) + blob_pad_size
00132             sector_info_arr = []
00133             for flash_start, flash_size in pack_flash_algo.sector_sizes:
00134                 sector_info_arr.append(flash_start + pack_flash_algo.flash_start)
00135                 sector_info_arr.append(flash_size)
00136             print("sector_info offset:", hex(sector_info_addr - start))
00137             new_hex_file.puts(sector_info_addr,struct.pack('<' + 'I'*len(sector_info_arr), *sector_info_arr))
00138             program_target_addr = sector_info_addr + len(sector_info_arr)*4
00139             stack_pointer = (flash_blob_entry + blob_header_size + pack_flash_algo.rw_start + pack_flash_algo.rw_size + stack_size + 0x100 - 1) // 0x100 * 0x100
00140             print("program_target offset:", hex(program_target_addr - start))
00141             new_hex_file.puts(program_target_addr,struct.pack('<' + program_target_fmt,
00142                                                             pack_flash_algo.symbols['Init'] + blob_header_size + flash_blob_entry,
00143                                                             pack_flash_algo.symbols['UnInit'] + blob_header_size + flash_blob_entry,
00144                                                             pack_flash_algo.symbols['EraseChip'] + blob_header_size + flash_blob_entry if pack_flash_algo.symbols['EraseChip'] != 0xffffffff else 0,
00145                                                             pack_flash_algo.symbols['EraseSector'] + blob_header_size + flash_blob_entry,
00146                                                             pack_flash_algo.symbols['ProgramPage'] + blob_header_size + flash_blob_entry,
00147                                                             pack_flash_algo.symbols['Verify'] + blob_header_size + flash_blob_entry if pack_flash_algo.symbols['Verify'] != 0xffffffff else 0,
00148                                                             flash_blob_entry + 1, #BKPT : start of blob + 1
00149                                                             flash_blob_entry + blob_header_size + pack_flash_algo.rw_start, #RSB  : blob start + header + rw data offset
00150                                                             stack_pointer, #RSP  : stack pointer
00151                                                             flash_blob_entry + 0x00000A00, #mem buffer location
00152                                                             flash_blob_entry, #location to write prog_blob in target RAM
00153                                                             blob_header_size + len(pack_flash_algo.algo_data) + blob_pad_size, #prog_blob size
00154                                                             flash_blob_addr, #address of prog_blob
00155                                                             pack_flash_algo.page_size #ram_to_flash_bytes_to_be_written
00156                                                             ))
00157             target_cfg_addr = program_target_addr + struct.calcsize(program_target_fmt)
00158             print("target_cfg offset:", hex(target_cfg_addr - start))
00159             if target_ram_start is None or target_ram_end is None:
00160                  raise Exception("target_ram_start and target_ram_end should be defined!")
00161             first_flash_region = (pack_flash_algo.flash_start, pack_flash_algo.flash_start + pack_flash_algo.flash_size, 1, 0, program_target_addr)
00162             first_ram_region = (int(target_ram_start, 16), int(target_ram_end, 16), 0, 0, 0)
00163             emypty_region = (0, 0, 0, 0, 0) * (region_info_total -1)
00164             all_regions = first_flash_region + emypty_region + first_ram_region + emypty_region
00165             target_flags = ( 0, 0, 0, 0) #realtime board ID, family ID and erase reset flag
00166             regions_flags = all_regions + target_flags
00167             new_hex_file.puts(target_cfg_addr, struct.pack('<' + target_cfg_fmt,
00168                                                             0x1,                #script generated
00169                                                             sector_info_addr,   # Sector start and length list
00170                                                             sector_info_len,    #Sector start and length list total
00171                                                             *regions_flags
00172                                                             ))
00173             board_info_flag = 1 if pack_flash_algo.symbols['EraseSector'] != 0xffffffff else 0  #kEnablePageErase
00174             new_hex_file.puts(target_addr_unpack + 12,struct.pack('<1I', board_info_flag)) #always enable page erase EraseSector is a required symbol in flash algo
00175             new_hex_file.puts(target_addr_unpack + 16,struct.pack('<1I', target_cfg_addr))
00176 
00177     # CRC the entire image
00178     #
00179     # This is required for all builds
00180 
00181     # Compute checksum over the range (don't include data at location of crc)
00182     size = end - start + 1
00183     crc_size = size - 4
00184     data = new_hex_file.tobinarray(start=start, size=crc_size)
00185     crc32 = binascii.crc32(data) & 0xFFFFFFFF
00186 
00187     # Write CRC to the file in little endian
00188     new_hex_file[end - 3] = (crc32 >> 0) & 0xFF
00189     new_hex_file[end - 2] = (crc32 >> 8) & 0xFF
00190     new_hex_file[end - 1] = (crc32 >> 16) & 0xFF
00191     new_hex_file[end - 0] = (crc32 >> 24) & 0xFF
00192 
00193     # Write out file(s)
00194     new_hex_file.tofile(output_file_hex, 'hex')
00195     new_hex_file.tofile(output_file_binary, 'bin')
00196     with open(output_file_txt, 'w') as file_handle:
00197         file_handle.write("0x%08x\r\n" % crc32)
00198 
00199     # Write out data as a C array
00200     data = new_hex_file.tobinarray(start=start, size=size)
00201     data = list(bytearray(data))
00202     output_data = ('static const unsigned int image_start = 0x%08x;\n'
00203                     'static const unsigned int image_size = 0x%08x;\n'
00204                     'static const char image_data[0x%08x] = {\n    ' %
00205                     (start, size, size))
00206     for i, byte_val in enumerate(data):
00207         output_data += '0x%02x' % byte_val + ', '
00208         if ((i + 1) % 0x20) == 0:
00209             output_data += '\n    '
00210     output_data += '};\n'
00211     with open(output_file_c, 'w') as file_handle:
00212         file_handle.write(output_data)
00213     with open(output_file_c_generic, 'w') as file_handle:
00214         file_handle.write(output_data)
00215 
00216     # Print info on operation
00217     print("Start 0x%x, Length 0x%x, CRC32 0x%08x" % (start, size, crc32))
00218 
00219     if start == 0x8000 or start == 0x10000 or start == 0x88000 or start == 0x0800C000:
00220         if start == 0x0800C000:
00221             # Adjust for ST-Link
00222             pad_addr = start - 0x8000
00223         else:
00224             pad_addr = start - 0x3000
00225         legacy_zero = start + 7 * 4
00226         legacy_size = 4 * 4
00227         legacy_hex_file = intelhex.IntelHex(new_hex_file)
00228         for addr in range(legacy_zero, legacy_zero + legacy_size):
00229             legacy_hex_file[addr] = 0
00230         data = legacy_hex_file.tobinarray(start=start, size=crc_size)
00231         crc32 = binascii.crc32(data) & 0xFFFFFFFF
00232         # Write CRC to the file in little endian
00233         legacy_hex_file[end - 3] = (crc32 >> 0) & 0xFF
00234         legacy_hex_file[end - 2] = (crc32 >> 8) & 0xFF
00235         legacy_hex_file[end - 1] = (crc32 >> 16) & 0xFF
00236         legacy_hex_file[end - 0] = (crc32 >> 24) & 0xFF
00237         legacy_hex_file.tofile(output_file_legacy, 'bin')
00238         with open(output_file_legacy_txt, 'w') as file_handle:
00239             file_handle.write("0x%08x\r\n" % crc32)
00240         offset_update.create_padded_image(output_file_legacy,
00241                                           output_file_legacy_5000,
00242                                           start, pad_addr, 0x40)
00243 
00244 if __name__ == '__main__':
00245     parser = argparse.ArgumentParser(description='Post build tool for Board ID, Family ID, Target flash algo and CRC generation')
00246     parser.add_argument("input", type=str, help="Hex or bin file to read from.")
00247     parser.add_argument("output", type=str,
00248                         help="Output base file name to write crc, board_id and family_id.")
00249     parser.add_argument("--board-id", type=str,
00250                         help="board id to for the target in hex")
00251     parser.add_argument("--family-id", type=str,
00252                         help="family id to for the target in hex")
00253     parser.add_argument("--bin-offset", type=str,
00254                         help="binary offset in hex can be supplied if input is bin")
00255     parser.add_argument("--flash-algo-file", type=str,
00256                         help="Elf, axf, or flm to extract flash algo from")
00257     parser.add_argument("--target-ram-start", type=str,
00258                         help="Lowest address of target RAM for flash algo in hex")
00259     parser.add_argument("--target-ram-end", type=str,
00260                         help="Highest address of target RAM for flash algo in hex")
00261     parser.add_argument("--flash-blob-entry", type=str, default="0x20000000",
00262                          help="Entry point of flash algo in the target")
00263     args = parser.parse_args()
00264     post_build_script(args.input, args.output, args.board_id, args.family_id, args.bin_offset, args.flash_algo_file, args.target_ram_start, args.target_ram_end, args.flash_blob_entry)