Skip to content

Commit 4cc9ea1

Browse files
Replace esptool-ck.exe with Python equivalent
Remove need for binary esptool-ck.exe by implementing the same logic as esptool-ck uses in Python. Only image creation is supported, and only in the Arduino standard mode (with its custom bootloader and ROM layout).
1 parent dc66bb7 commit 4cc9ea1

File tree

1 file changed

+107
-0
lines changed

1 file changed

+107
-0
lines changed

tools/makebin.py

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#!/usr/bin/env python
2+
3+
# Generate an Arduino compatible BIN file from bootloader and sketch ELF
4+
# Replaces esptool-ck.exe and emulates its behavior.
5+
#
6+
# Copyright (C) 2019 - Earle F. Philhower, III
7+
#
8+
# This program is free software: you can redistribute it and/or modify
9+
# it under the terms of the GNU General Public License as published by
10+
# the Free Software Foundation, either version 3 of the License, or
11+
# (at your option) any later version.
12+
#
13+
# This program is distributed in the hope that it will be useful,
14+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
# GNU General Public License for more details.
17+
#
18+
# You should have received a copy of the GNU General Public License
19+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
20+
21+
import argparse
22+
import re
23+
import os
24+
import subprocess
25+
import tempfile
26+
27+
fmodeb = { 'DOUT': 3, 'DIO': 2, 'QOUT': 1, 'QIO': 0 }
28+
ffreqb = { '40': 0, '26': 1, '20': 2, '80': 15 }
29+
fsizeb = { '512K': 0, '256K': 1, '1M': 2, '2M': 3, '4M': 4, '8M': 8, '16M': 9 }
30+
31+
def get_elf_entry(elf, path):
32+
p = subprocess.Popen([path + "/xtensa-lx106-elf-readelf", '-h', elf], stdout=subprocess.PIPE)
33+
lines = p.stdout.readlines()
34+
for line in lines:
35+
if 'Entry point address' in line:
36+
words = re.split('\s+', line)
37+
entry_point = words[-2]
38+
return int(entry_point, 16)
39+
raise Exception('Unable to find entry point in file "' + elf + '"')
40+
41+
def get_segment_size_addr(elf, segment, path):
42+
p = subprocess.Popen([path + '/xtensa-lx106-elf-objdump', '-h', '-j', segment, elf], stdout=subprocess.PIPE)
43+
lines = p.stdout.readlines()
44+
for line in lines:
45+
if segment in line:
46+
words = re.split('\s+', line)
47+
size = int(words[3], 16)
48+
addr = int(words[4], 16)
49+
return [ size, addr ]
50+
raise Exception('Unable to find size and start point in file "' + elf + '" for "' + segment + '"')
51+
52+
def read_segment(elf, segment, path):
53+
tmpfile, dumpfile = tempfile.mkstemp()
54+
os.close(tmpfile)
55+
p = subprocess.check_call([path + "/xtensa-lx106-elf-objcopy", '-O', 'binary', '--only-section=' + segment, elf, dumpfile], stdout=subprocess.PIPE)
56+
binfile = open(dumpfile, "rb")
57+
raw = binfile.read()
58+
binfile.close()
59+
return raw
60+
61+
def write_bin(out, elf, segments, to_addr, flash_mode, flash_size, flash_freq, path):
62+
entry = int(get_elf_entry( elf, path ))
63+
header = [ 0xe9, len(segments), fmodeb[flash_mode], ffreqb[flash_freq] + 16 * fsizeb[flash_size],
64+
entry & 255, (entry>>8) & 255, (entry>>16) & 255, (entry>>24) & 255 ]
65+
out.write(bytearray(header))
66+
total_size = 8
67+
checksum = 0xef
68+
for segment in segments:
69+
[size, addr] = get_segment_size_addr(elf, segment, path)
70+
seghdr = [ addr & 255, (addr>>8) & 255, (addr>>16) & 255, (addr>>24) & 255,
71+
size & 255, (size>>8) & 255, (size>>16) & 255, (size>>24) & 255]
72+
out.write(bytearray(seghdr));
73+
total_size += 8;
74+
raw = read_segment(elf, segment, path)
75+
if len(raw) != size:
76+
raise Exception('Segment size doesn\'t match read data for "' + segment + '" in "' + elf + '"')
77+
out.write(raw)
78+
total_size += len(raw)
79+
for data in raw:
80+
checksum = checksum ^ ord(data)
81+
total_size += 1
82+
while total_size & 15:
83+
total_size += 1
84+
out.write(bytearray([0]))
85+
out.write(bytearray([checksum]))
86+
if to_addr != 0:
87+
while total_size < to_addr:
88+
out.write(bytearray([0xaa]))
89+
total_size += 1
90+
91+
parser = argparse.ArgumentParser(description='Create a BIN file from eboot.elf and Arduino sketch.elf for upload by esptool.py')
92+
parser.add_argument('-e', '--eboot', action='store', required=True, help='Path to the Arduino eboot.elf bootloader')
93+
parser.add_argument('-a', '--app', action='store', required=True, help='Path to the Arduino sketch ELF')
94+
parser.add_argument('-m', '--flash_mode', action='store', required=True, choices=['DOUT', 'DIO', 'QOUT', 'QIO'], help='SPI flash mode')
95+
parser.add_argument('-f', '--flash_freq', action='store', required=True, choices=['20', '26', '40', '80'], help='SPI flash speed')
96+
parser.add_argument('-s', '--flash_size', action='store', required=True, choices=['256K', '512K', '1M', '2M', '4M', '8M', '16M'], help='SPI flash size')
97+
parser.add_argument('-o', '--out', action='store', required=True, help='Output BIN filename')
98+
parser.add_argument('-p', '--path', action='store', required=True, help='Path to Xtensa toolchain binaries')
99+
100+
args = parser.parse_args()
101+
102+
print 'Creating BIN file "' + args.out + '" using "' + args.app + '"'
103+
104+
out = open(args.out, "wb")
105+
write_bin(out, args.eboot, ['.text'], 4096, args.flash_mode, args.flash_size, args.flash_freq, args.path)
106+
write_bin(out, args.app, ['.irom0.text', '.text', '.data', '.rodata'], 0, args.flash_mode, args.flash_size, args.flash_freq, args.path)
107+
out.close()

0 commit comments

Comments
 (0)