• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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