1# Copyright (c) 2017 Linaro Limited.
2# Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd.
3#
4# SPDX-License-Identifier: Apache-2.0
5
6'''Runner for flashing ESP32 devices with esptool/espidf.'''
7
8import os
9import sys
10from os import path
11
12from runners.core import RunnerCaps, ZephyrBinaryRunner
13
14
15class Esp32BinaryRunner(ZephyrBinaryRunner):
16    '''Runner front-end for espidf.'''
17
18    def __init__(self, cfg, device, boot_address, part_table_address,
19                 app_address, erase=False, reset=False, baud=921600,
20                 flash_size='detect', flash_freq='40m', flash_mode='dio',
21                 espidf='espidf', bootloader_bin=None, partition_table_bin=None,
22                 encrypt=False, no_stub=False):
23        super().__init__(cfg)
24        self.elf = cfg.elf_file
25        self.app_bin = cfg.bin_file
26        self.erase = bool(erase)
27        self.reset = bool(reset)
28        self.device = device
29        self.boot_address = boot_address
30        self.part_table_address = part_table_address
31        self.app_address = app_address
32        self.baud = baud
33        self.flash_size = flash_size
34        self.flash_freq = flash_freq
35        self.flash_mode = flash_mode
36        self.espidf = espidf
37        self.bootloader_bin = bootloader_bin
38        self.partition_table_bin = partition_table_bin
39        self.encrypt = encrypt
40        self.no_stub = no_stub
41
42    @classmethod
43    def name(cls):
44        return 'esp32'
45
46    @classmethod
47    def capabilities(cls):
48        return RunnerCaps(commands={'flash'}, erase=True, reset=True)
49
50    @classmethod
51    def do_add_parser(cls, parser):
52        # Required
53        parser.add_argument('--esp-idf-path', required=True,
54                            help='path to ESP-IDF')
55        # Optional
56        parser.add_argument('--esp-boot-address', default='0x1000',
57                            help='bootloader load address')
58        parser.add_argument('--esp-partition-table-address', default='0x8000',
59                            help='partition table load address')
60        parser.add_argument('--esp-app-address', default='0x10000',
61                            help='application load address')
62        parser.add_argument('--esp-device', default=os.environ.get('ESPTOOL_PORT', None),
63                            help='serial port to flash')
64        parser.add_argument('--esp-baud-rate', default='921600',
65                            help='serial baud rate, default 921600')
66        parser.add_argument('--esp-monitor-baud', default='115200',
67                            help='serial monitor baud rate, default 115200')
68        parser.add_argument('--esp-flash-size', default='detect',
69                            help='flash size, default "detect"')
70        parser.add_argument('--esp-flash-freq', default='40m',
71                            help='flash frequency, default "40m"')
72        parser.add_argument('--esp-flash-mode', default='dio',
73                            help='flash mode, default "dio"')
74        parser.add_argument(
75            '--esp-tool',
76            help='''if given, complete path to espidf. default is to search for
77            it in [ESP_IDF_PATH]/tools/esptool_py/esptool.py''')
78        parser.add_argument('--esp-flash-bootloader',
79                            help='Bootloader image to flash')
80        parser.add_argument('--esp-flash-partition_table',
81                            help='Partition table to flash')
82        parser.add_argument('--esp-encrypt', default=False, action='store_true',
83                            help='Encrypt firmware while flashing (correct efuses required)')
84        parser.add_argument('--esp-no-stub', default=False, action='store_true',
85                            help='Disable launching the flasher stub, only talk to ROM bootloader')
86
87        parser.set_defaults(reset=True)
88
89    @classmethod
90    def do_create(cls, cfg, args):
91        if args.esp_tool:
92            espidf = args.esp_tool
93        else:
94            espidf = path.join(args.esp_idf_path, 'tools', 'esptool_py',
95                               'esptool.py')
96
97        return Esp32BinaryRunner(
98            cfg, args.esp_device, boot_address=args.esp_boot_address,
99            part_table_address=args.esp_partition_table_address,
100            app_address=args.esp_app_address, erase=args.erase, reset=args.reset,
101            baud=args.esp_baud_rate, flash_size=args.esp_flash_size,
102            flash_freq=args.esp_flash_freq, flash_mode=args.esp_flash_mode,
103            espidf=espidf, bootloader_bin=args.esp_flash_bootloader,
104            partition_table_bin=args.esp_flash_partition_table,
105            encrypt=args.esp_encrypt, no_stub=args.esp_no_stub)
106
107    def do_run(self, command, **kwargs):
108        self.require(self.espidf)
109
110        # Add Python interpreter
111        cmd_flash = [sys.executable, self.espidf, '--chip', 'auto']
112
113        if self.device is not None:
114            cmd_flash.extend(['--port', self.device])
115
116        if self.erase is True:
117            cmd_erase = cmd_flash + ['erase_flash']
118            self.check_call(cmd_erase)
119
120        if self.no_stub is True:
121            cmd_flash.extend(['--no-stub'])
122        cmd_flash.extend(['--baud', self.baud])
123        cmd_flash.extend(['--before', 'default_reset'])
124        if self.reset:
125            cmd_flash.extend(['--after', 'hard_reset', 'write_flash', '-u'])
126        cmd_flash.extend(['--flash_mode', self.flash_mode])
127        cmd_flash.extend(['--flash_freq', self.flash_freq])
128        cmd_flash.extend(['--flash_size', self.flash_size])
129
130        if self.encrypt:
131            cmd_flash.extend(['--encrypt'])
132
133        if self.bootloader_bin:
134            cmd_flash.extend([self.boot_address, self.bootloader_bin])
135            if self.partition_table_bin:
136                cmd_flash.extend([self.part_table_address, self.partition_table_bin])
137                cmd_flash.extend([self.app_address, self.app_bin])
138        else:
139            cmd_flash.extend([self.app_address, self.app_bin])
140
141        self.logger.info(f"Flashing esp32 chip on {self.device} ({self.baud}bps)")
142        self.check_call(cmd_flash)
143