1# Copyright (c) 2021 GOODIX. 2# Licensed under the Apache License, Version 2.0 (the "License"); 3# you may not use this file except in compliance with the License. 4# You may obtain a copy of the License at 5# 6# http://www.apache.org/licenses/LICENSE-2.0 7# 8# Unless required by applicable law or agreed to in writing, software 9# distributed under the License is distributed on an "AS IS" BASIS, 10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11# See the License for the specific language governing permissions and 12# limitations under the License. 13 14#!/usr/bin/env python 15# -*- coding: utf-8 -*- 16# author: zhangqf 17 18""" 19This module use arm-none-eabi-xxx function to deal the xxx.elf file. we recept two parameters, and the 20format of the usage: bin_create.py input_file.elf out_file.bin. Thanks for use this module 21""" 22 23import os 24from os import path 25import sys 26import sys 27import time 28import re 29import os 30import json 31 32CUSTOM_CONFIG = "../../../device/soc/goodix/gr551x/sdk_liteos/config/custom_config.h" 33 34BufSize = 1024 35PatternValue = 0x4744 36BleToolVersion = "v1.0" 37NumberOfOneLine = 0x10 38MaxCommentsLen = 12 39 40tool_usage = """\n 41BLE Tool Usage: 42after_build.py --mode=gen_fw --cfg=config_file --bin=xxx.bin --out_dir=example_dir --app_name=xxx [--dap]\n 43""" 44 45OPT_MODE_INVALID = 1 46OPT_MODE_GEN = 2 47OPT_MODE_MERGE_BIN = 3 48OPT_MODE_HEX2BIN = 4 49OPT_MODE_BIN2HEX = 5 50OPT_MODE_ENC = 6 51OPT_MODE_SPLIT_ROM = 7 52 53XIP_SPEED_64M = 0 54XIP_SPEED_48M = 1 55XIP_SPEED_32M = 2 56XIP_SPEED_24M = 3 57XIP_SPEED_16M = 4 58 59SYS_CLK_64M = 0 60SYS_CLK_48M = 1 61SYS_CLK_32M = 5 62SYS_CLK_24M = 3 63SYS_CLK_XO16M = 2 64SYS_CLK_16M = 4 65 66BIN2HEX_RES_OK = 0 67BIN2HEX_RES_BIN_FILE_NOT_EXIST = 1 68BIN2HEX_RES_HEX_FILE_PATH_ERROR = 2 69 70HEX2BIN_RES_OK = 0 71HEX2BIN_RES_DATA_TOO_LONG = 1 72HEX2BIN_RES_DATA_TOO_SHORT = 2 73HEX2BIN_RES_NO_COLON = 3 74HEX2BIN_RES_TYPE_ERROR = 4 75HEX2BIN_RES_LENGTH_ERROR = 5 76HEX2BIN_RES_CHECK_ERROR = 6 77HEX2BIN_RES_HEX_FILE_NOT_EXIST = 7 78HEX2BIN_RES_BIN_FILE_PATH_ERROR = 8 79HEX2BIN_RES_WRITE_ERROR = 9 80HEX2BIN_RES_HEX_FILE_NO_END = 10 81 82class BootInfo(): 83 def __init__(self): 84 self.bin_size = 0 85 self.check_sum = 0 86 self.load_addr = 0 87 self.run_addr = 0 88 self.xqspi_xip_cmd = 0 89 self.xqspi_speed = 0 # bit: 0..3 clock speed 90 self.code_copy_mode = 0 # bit: 4 code copy mode 91 self.boot_clk = 0 # bit: 5..7 reserved 92 self.check_image = 0 # bit: 8 check image 93 self.boot_delay = 0 94 self.is_dap_boot = 0 # bit:11 check if boot dap mode 95 self.reserved = 0 # bit: 24 reserved 96 97 def to_bytes(self): 98 boot_config = self.xqspi_speed ^ (self.code_copy_mode << 4) ^ (self.boot_clk << 5) ^ \ 99 (self.check_image << 8) ^ (self.boot_delay << 9) ^ \ 100 (self.is_dap_boot << 10) ^ (self.reserved << 11) 101 load_addr = int(self.load_addr, base=16) 102 run_addr = int(self.run_addr, base=16) 103 items = [self.bin_size, self.check_sum, load_addr, run_addr, self.xqspi_xip_cmd, boot_config] 104 boot_info_byte = list(map(lambda item: item.to_bytes(4, byteorder='little', signed=False), items)) 105 return boot_info_byte 106 107 108class ImgInfo(): 109 def __init__(self): 110 self.pattern = 0 111 self.version = 0 112 self.boot_info = BootInfo() 113 self.comments = [] 114 self.reserved = [] 115 116 def to_bytes(self): 117 img_info_byte = [] 118 img_info_byte.append(self.pattern.to_bytes(2, byteorder='little', signed=False)) 119 img_info_byte.append(int(self.version, base=16).to_bytes(2, byteorder='little', signed=False)) 120 boot_info_byte = self.boot_info.to_bytes() 121 img_info_byte += boot_info_byte 122 return img_info_byte 123 124class BootInfoEnc(): 125 def __init__(self): 126 self.CP = [0 for x in range(60)] 127 self.cfg = 0 128 self.swd = 0 129 self.enc = 0 130 self.CRC = 0 131 self.key_bin = [0 for x in range(168)] 132 133 def to_bytes(self): 134 boot_info_enc_byte = [] 135 boot_info_enc_byte += list(map(lambda item: item.to_bytes(1, byteorder='little', signed=False), self.CP)) 136 boot_info_enc_byte.append(self.cfg.to_bytes(4, byteorder='little', signed=False)) 137 boot_info_enc_byte.append(self.swd.to_bytes(2, byteorder='little', signed=False)) 138 boot_info_enc_byte.append(self.enc.to_bytes(2, byteorder='little', signed=False)) 139 boot_info_enc_byte.append(self.CRC.to_bytes(4, byteorder='little', signed=False)) 140 boot_info_enc_byte += list(map(lambda item: item.to_bytes(1, byteorder='little', signed=False), self.key_bin)) 141 return boot_info_enc_byte 142 143 144class UtilityFunc(): 145 def __init__(self): 146 pass 147 148 def get_spec_macro_arg_str(self, file_path, macro_str): 149 # print("get_spec_macro_arg_str") 150 with open(file_path, "r+", encoding="utf-8") as f_r: 151 lines = f_r.readlines() 152 # arg_cache = None 153 for line in lines: 154 if "#define" in line: 155 line = line[7:] 156 # print(line) 157 arg_head = line.split()[0] 158 # print(arg_head) 159 if arg_head == macro_str: 160 # arg_cache.append(line.split()[1]) 161 # print(arg_cache) 162 return line.split()[1] 163 else: 164 continue 165 else: 166 continue 167 # arg_cache.append('') 168 return '' 169 170 def do_pad_with_bin(self, bin_file): 171 if bin_file: 172 bin_size = os.path.getsize(bin_file) 173 add_size = 16 - bin_size % 16 if bin_size % 16 != 0 else 0 174 # print(bin_size, add_size) 175 176 with open(bin_file, "ab") as f_w: 177 for i in range(add_size): 178 fill_value = 0x00 179 f_w.write(fill_value.to_bytes(1, byteorder='little', signed=False)) 180 return True 181 return False 182 183 def check_image_sum(self, data, len): 184 check_sum = 0 185 for i in range(len): 186 check_sum += data[i] 187 return check_sum 188 189 190class GenFirmware(): 191 def __init__(self): 192 self.utilityFunc = UtilityFunc() 193 self.boot_info = BootInfo() 194 195 self.config_file = '' 196 self.input_bin = '' 197 self.out_dir = '' 198 self.app_name = '' 199 self.dap_mode = '' 200 201 def opt_gen_mode_handler(self): 202 # print("gen_firmware") 203 run_mode = 0x0b 204 hex_addr = 0x01000000 205 boot_clock = SYS_CLK_XO16M 206 207 xqspi_mode = 0 208 sdk_version = 0 209 is_dap_boot = 0 210 code_copy_mode = 0 211 212 macro_values = ["APP_CODE_RUN_ADDR", "APP_CODE_LOAD_ADDR", "VERSION", "BOOT_CLOCK", "BOOT_CHECK_IMAGE", 213 "BOOT_LONG_TIME", "COMMENTS"] 214 215 arg_caches = list(map(lambda x: self.utilityFunc.get_spec_macro_arg_str(self.config_file, x), macro_values)) 216 217 if arg_caches[3] != '': 218 boot_clock = arg_caches[3] 219 220 if boot_clock == 0: # 64MHz 221 xqspi_mode = XIP_SPEED_64M 222 elif boot_clock == 1: # 48MHz 223 xqspi_mode = XIP_SPEED_48M 224 elif boot_clock == 2 or boot_clock == 4: 225 xqspi_mode = XIP_SPEED_16M 226 elif boot_clock == 3: 227 xqspi_mode = XIP_SPEED_24M 228 elif boot_clock == 5: 229 xqspi_mode = XIP_SPEED_32M 230 231 # 16 bytes alignment 232 self.utilityFunc.do_pad_with_bin(self.input_bin) 233 234 if arg_caches[2] != '': 235 sdk_version = arg_caches[2] 236 237 # fill the boot info 238 self.boot_info.bin_size = 0x00000000 239 self.boot_info.check_sum = 0x00000000 240 if arg_caches[0] != '': 241 self.boot_info.run_addr = arg_caches[0] 242 if arg_caches[1] != '': 243 self.boot_info.load_addr = arg_caches[1] 244 self.boot_info.xqspi_xip_cmd = run_mode 245 self.boot_info.xqspi_speed = xqspi_mode & 0x0F 246 self.boot_info.code_copy_mode = code_copy_mode & 0x01 247 self.boot_info.boot_clk = boot_clock & 0x07 248 if arg_caches[4] != '': 249 self.boot_info.check_image = int(arg_caches[4]) & 0x01 250 if arg_caches[5] != '': 251 self.boot_info.boot_delay = int(arg_caches[5]) & 0x01 252 self.boot_info.is_dap_boot = int(is_dap_boot) & 0x01 253 254 info_bin_file = os.path.join(self.out_dir, "info.bin") 255 header_bin_file = os.path.join(self.out_dir, "header.bin") 256 257 if not self.gen_fw_info_file(self.input_bin, info_bin_file): 258 print("Generate info files fail\n") 259 return False 260 # fill the image info 261 self.img_info = ImgInfo() 262 self.img_info.pattern = PatternValue 263 self.img_info.version = sdk_version 264 self.img_info.boot_info = self.boot_info 265 if arg_caches[6] != '': 266 if arg_caches[6][0] == '"': 267 arg_caches[6] = arg_caches[6][1:] 268 if arg_caches[6][-1] == '"': 269 arg_caches[6] = arg_caches[6][:-2] 270 self.img_info.comments = arg_caches[6] 271 else: 272 self.img_info.comments = self.app_name[0:MaxCommentsLen] 273 # print(self.img_info.comments) 274 275 if not self.gen_fw_header_bin(header_bin_file, info_bin_file): 276 print("Generate header bin fail!\n") 277 return False 278 279 out_app_fw_bin = os.path.join(self.out_dir, "{}.bin".format(self.app_name)) # ? 280 self.gen_app_fw_bin(out_app_fw_bin, header_bin_file, self.input_bin) 281 282 # if os.path.exists(self.input_bin): 283 # os.remove(self.input_bin) 284 285 if os.path.exists(info_bin_file): 286 os.remove(info_bin_file) 287 288 if os.path.exists(header_bin_file): 289 os.remove(header_bin_file) 290 291 292 def gen_fw_info_file(self, input_bin, output_info_bin): 293 bin_size = os.path.getsize(input_bin) 294 295 with open(input_bin, "rb") as f_r: 296 buf = f_r.read() 297 298 self.boot_info.bin_size = bin_size 299 self.boot_info.check_sum = self.utilityFunc.check_image_sum(buf, bin_size) 300 301 print("---------------------------------------------------------------") 302 print("bin_size = 0x%08x," %(self.boot_info.bin_size)) 303 print("check_sum = 0x%08x," %(self.boot_info.check_sum)) 304 print("load_addr = {},".format(self.boot_info.load_addr)) 305 print("run_addr = {},".format(self.boot_info.run_addr)) 306 print("xqspi_xip_cmd = 0x%02x," %(self.boot_info.xqspi_xip_cmd)) 307 print("xqspi_speed = 0x%02x," %(self.boot_info.xqspi_speed)) 308 print("code_copy_mode = 0x%02x," %(self.boot_info.code_copy_mode)) 309 print("boot_clk = 0x%02x," %(self.boot_info.boot_clk)) 310 print("check_image = 0x%02x," %(self.boot_info.check_image)) 311 print("boot_delay = 0x%02x," %(self.boot_info.boot_delay)) 312 print("---------------------------------------------------------------") 313 314 with open(output_info_bin, "wb") as f_w: 315 bin_info_byte = self.boot_info.to_bytes() 316 for x in bin_info_byte: 317 f_w.write(x) 318 return True 319 320 def gen_fw_header_bin(self, header_bin, info_bin): 321 with open(header_bin, "wb") as f_w: 322 img_info_byte = self.img_info.to_bytes() 323 for x in img_info_byte: 324 f_w.write(x) 325 for i in range(len(self.img_info.comments)): 326 f_w.write(self.img_info.comments[i].encode('utf-8')) 327 if (len(self.img_info.comments) < MaxCommentsLen): 328 for i in range(MaxCommentsLen - len(self.img_info.comments)): 329 pad=' '; 330 f_w.write(str(pad).encode('utf-8')) 331 for i in range(8): 332 a = 0 333 f_w.write(a.to_bytes(1, byteorder='little', signed=False)) 334 return True 335 return False 336 337 def gen_app_fw_bin(self, app_fw_bin, header_bin, ori_app_bin): 338 ori_app_bin_size = os.path.getsize(ori_app_bin) 339 340 with open(app_fw_bin, "wb") as f_w: 341 with open(ori_app_bin, "rb") as f_r_1: 342 temp_buf_1 = f_r_1.read() 343 f_w.write(temp_buf_1) 344 if ori_app_bin_size % 16: 345 for i in range(16 - ori_app_bin_size % 16): 346 fill_value = 0x00 347 f_w.write(fill_value.to_bytes(1, byteorder='little', signed=False)) 348 349 with open(header_bin, "rb") as f_r_2: 350 temp_buf_3 = f_r_2.read() 351 f_w.write(temp_buf_3) 352 353class MainFunc(): 354 def __init__(self, argc, argv): 355 self.argc = argc 356 self.argv = argv 357 358 self.utilityFunc = UtilityFunc() 359 360 def deal_input_cmd(self, opt_mode, input): 361 if opt_mode == OPT_MODE_GEN: 362 363 out_dir = os.path.dirname(input) 364 bin_file_name = os.path.basename(input) 365 tmp = bin_file_name.split('.') 366 targe_name = tmp[0] + "_fw" 367 self.opt_gen_param = GenFirmware() 368 self.opt_gen_param.config_file = CUSTOM_CONFIG 369 self.opt_gen_param.input_bin = input 370 self.opt_gen_param.out_dir = out_dir 371 self.opt_gen_param.app_name = targe_name 372 return self.opt_gen_param.opt_gen_mode_handler() 373 374 375def main(input = ""): 376 TIME = time.strftime("%H:%M:%S") 377 DATE = time.strftime("%d %h %Y") 378 print("Goodix BLE Tools {} [build time: {}, {}]".format(BleToolVersion, TIME, DATE)) 379 380 mainFunc = MainFunc(0, 0) 381 opt_mode = OPT_MODE_GEN 382 383 if opt_mode == OPT_MODE_INVALID: 384 print(tool_usage) 385 sys.exit(0) 386 387 mainFunc.deal_input_cmd(opt_mode, input) 388 389def make_bin(input_file = "", output_file = "", list_file=""): 390 shell_script = '''arm-none-eabi-objcopy -O binary -S {src_file} {dst_file}'''.format(src_file = input_file, dst_file = output_file) 391 cmd_output = os.system(shell_script) 392 393 main(output_file) 394 395 # shell_script = '''arm-none-eabi-size {src_file} 396 # arm-none-eabi-objdump -D {src_file} > {list_file}'''.format(src_file = input_file, dst_file = output_file, 397 # list_file=list_file) 398 # cmd_output = os.system(shell_script) 399 400 return 401 402if __name__ == "__main__": 403 input_file = sys.argv[1] 404 output_file = sys.argv[2] 405 list_file = sys.argv[3] 406 make_bin(input_file, output_file, list_file) 407