1# Copyright (c) 2019 Thomas Kupper <thomas.kupper@gmail.com>
2#
3# SPDX-License-Identifier: Apache-2.0
4
5'''Runner for flashing with stm32flash.'''
6
7from os import path
8import platform
9
10from runners.core import ZephyrBinaryRunner, RunnerCaps
11
12DEFAULT_DEVICE = '/dev/ttyUSB0'
13if platform.system() == 'Darwin':
14    DEFAULT_DEVICE = '/dev/tty.SLAB_USBtoUART'
15
16class Stm32flashBinaryRunner(ZephyrBinaryRunner):
17    '''Runner front-end for stm32flash.'''
18
19    def __init__(self, cfg, device, action='write', baud=57600,
20                 force_binary=False, start_addr=0, exec_addr=None,
21                 serial_mode='8e1', reset=False, verify=False):
22        super().__init__(cfg)
23
24        self.device = device
25        self.action = action
26        self.baud = baud
27        self.force_binary = force_binary
28        self.start_addr = start_addr
29        self.exec_addr = exec_addr
30        self.serial_mode = serial_mode
31        self.reset = reset
32        self.verify = verify
33
34    @classmethod
35    def name(cls):
36        return 'stm32flash'
37
38    @classmethod
39    def capabilities(cls):
40        return RunnerCaps(commands={'flash'}, reset=True)
41
42    @classmethod
43    def do_add_parser(cls, parser):
44
45        # required argument(s)
46        # none for now
47
48        # optional argument(s)
49        parser.add_argument('--device', default=DEFAULT_DEVICE, required=False,
50                            help='serial port to flash, default \'' + DEFAULT_DEVICE + '\'')
51
52        parser.add_argument('--action', default='write', required=False,
53                            choices=['erase', 'info', 'start', 'write'],
54                            help='erase / get device info / start execution / write flash')
55
56        parser.add_argument('--baud-rate', default='57600', required=False,
57                            choices=['1200', '1800', '2400', '4800', '9600', '19200',
58                            '38400', '57600', '115200', '230400', '256000', '460800',
59                            '500000', '576000', '921600', '1000000', '1500000', '2000000'],
60                            help='serial baud rate, default \'57600\'')
61
62        parser.add_argument('--force-binary', required=False, action='store_true',
63                            help='force the binary parser')
64
65        parser.add_argument('--start-addr', default=0, required=False,
66                            help='specify start address for write operation, default \'0\'')
67
68        parser.add_argument('--execution-addr', default=None, required=False,
69                            help='start execution at specified address, default \'0\' \
70                            which means start of flash')
71
72        parser.add_argument('--serial-mode', default='8e1', required=False,
73                            help='serial port mode, default \'8e1\'')
74
75        parser.add_argument('--verify', default=False, required=False, action='store_true',
76                            help='verify writes, default False')
77
78        parser.set_defaults(reset=False)
79
80    @classmethod
81    def do_create(cls, cfg, args):
82        return Stm32flashBinaryRunner(cfg, device=args.device, action=args.action,
83            baud=args.baud_rate, force_binary=args.force_binary,
84            start_addr=args.start_addr, exec_addr=args.execution_addr,
85            serial_mode=args.serial_mode, reset=args.reset, verify=args.verify)
86
87    def do_run(self, command, **kwargs):
88        self.require('stm32flash')
89        self.ensure_output('bin')
90
91        bin_name = self.cfg.bin_file
92        bin_size = path.getsize(bin_name)
93
94        cmd_flash = ['stm32flash', '-b', self.baud,
95            '-m', self.serial_mode]
96
97        action = self.action.lower()
98
99        if action == 'info':
100            # show device information and exit
101            msg_text = "get device info from {}".format(self.device)
102
103        elif action == 'erase':
104            # erase flash
105            #size_aligned = (int(bin_size)  >> 12) + 1 << 12
106            size_aligned = (int(bin_size) & 0xfffff000) + 4096
107            msg_text = "erase {} bit starting at {}".format(size_aligned, self.start_addr)
108            cmd_flash.extend([
109            '-S', str(self.start_addr) + ":" + str(size_aligned), '-o'])
110
111        elif action == 'start':
112            # start execution
113            msg_text = "start code execution at {}".format(self.exec_addr)
114            if self.exec_addr:
115                if self.exec_addr == 0 or self.exec_addr.lower() == '0x0':
116                    msg_text += " (flash start)"
117            else:
118                self.exec_addr = 0
119            cmd_flash.extend([
120            '-g', str(self.exec_addr)])
121
122        elif action == 'write':
123            # flash binary file
124            msg_text = "write {} bytes starting at {}".format(bin_size, self.start_addr)
125            cmd_flash.extend([
126            '-S', str(self.start_addr) + ":" + str(bin_size),
127            '-w', bin_name])
128
129            if self.exec_addr:
130                cmd_flash.extend(['-g', self.exec_addr])
131
132            if self.force_binary:
133                cmd_flash.extend(['-f'])
134
135            if self.reset:
136                cmd_flash.extend(['-R'])
137
138            if self.verify:
139                cmd_flash.extend(['-v'])
140
141        else:
142            msg_text = "invalid action \'{}\' passed!".format(action)
143            self.logger.error('Invalid action \'{}\' passed!'.format(action))
144            return -1
145
146        cmd_flash.extend([self.device])
147        self.logger.info("Board: " + msg_text)
148        self.check_call(cmd_flash)
149        self.logger.info('Board: finished \'{}\' .'.format(action))
150