CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
Ardupilot

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: Ardupilot/ardupilot
Path: blob/master/Tools/ardupilotwaf/chibios.py
Views: 1798
1
# encoding: utf-8
2
3
"""
4
Waf tool for ChibiOS build
5
"""
6
7
from waflib import Errors, Logs, Task, Utils, Context
8
from waflib.TaskGen import after_method, before_method, feature
9
10
import os
11
import shutil
12
import sys
13
import re
14
import pickle
15
import struct
16
import base64
17
import subprocess
18
19
_dynamic_env_data = {}
20
def _load_dynamic_env_data(bld):
21
bldnode = bld.bldnode.make_node('modules/ChibiOS')
22
include_dirs_node = bldnode.find_node('include_dirs')
23
if include_dirs_node is None:
24
_dynamic_env_data['include_dirs'] = []
25
return
26
tmp_str = include_dirs_node.read()
27
tmp_str = tmp_str.replace(';\n','')
28
tmp_str = tmp_str.replace('-I','') #remove existing -I flags
29
# split, coping with separator
30
idirs = re.split('; ', tmp_str)
31
32
# create unique list, coping with relative paths
33
idirs2 = []
34
for d in idirs:
35
if d.startswith('../'):
36
# relative paths from the make build are relative to BUILDROOT
37
d = os.path.join(bld.env.BUILDROOT, d)
38
d = os.path.normpath(d)
39
if d not in idirs2:
40
idirs2.append(d)
41
_dynamic_env_data['include_dirs'] = idirs2
42
43
@feature('ch_ap_library', 'ch_ap_program')
44
@before_method('process_source')
45
def ch_dynamic_env(self):
46
# The generated files from configuration possibly don't exist if it's just
47
# a list command (TODO: figure out a better way to address that).
48
if self.bld.cmd == 'list':
49
return
50
51
if not _dynamic_env_data:
52
_load_dynamic_env_data(self.bld)
53
self.use += ' ch'
54
self.env.append_value('INCLUDES', _dynamic_env_data['include_dirs'])
55
56
57
class upload_fw(Task.Task):
58
color='BLUE'
59
always_run = True
60
def run(self):
61
import platform
62
upload_tools = self.env.get_flat('UPLOAD_TOOLS')
63
upload_port = self.generator.bld.options.upload_port
64
src = self.inputs[0]
65
# Refer Tools/scripts/macos_remote_upload.sh for details
66
if 'AP_OVERRIDE_UPLOAD_CMD' in os.environ:
67
cmd = "{} '{}'".format(os.environ['AP_OVERRIDE_UPLOAD_CMD'], src.abspath())
68
elif "microsoft-standard-WSL2" in platform.release():
69
if not self.wsl2_prereq_checks():
70
return
71
print("If this takes takes too long here, try power-cycling your hardware\n")
72
cmd = "{} -u '{}/uploader.py' '{}'".format('python.exe', upload_tools, src.abspath())
73
else:
74
cmd = "{} '{}/uploader.py' '{}'".format(self.env.get_flat('PYTHON'), upload_tools, src.abspath())
75
if upload_port is not None:
76
cmd += " '--port' '%s'" % upload_port
77
if self.generator.bld.options.upload_force:
78
cmd += " '--force'"
79
return self.exec_command(cmd)
80
81
def wsl2_prereq_checks(self):
82
# As of July 2022 WSL2 does not support native USB support. The workaround from Microsoft
83
# using 'usbipd' does not work due to the following workflow:
84
#
85
# 1) connect USB device to Windows computer running WSL2
86
# 2) device boots into app
87
# 3) use 'usbipd' from Windows Cmd/PowerShell to determine busid, this is very hard to automate on Windows
88
# 4) use 'usbipd' from Windows Cmd/PowerShell to attach, this is very hard to automate on Windows
89
# -- device is now viewable via 'lsusb' but you need sudo to read from it.
90
# either run 'chmod666 /dev/ttyACM*' or use udev to automate chmod on device connect
91
# 5) uploader.py detects device, sends reboot command which disconnects the USB port and reboots into
92
# bootloader (different USB device)
93
# 6) manually repeat steps 3 & 4
94
# 7) doing steps 3 and 4 will most likely take several seconds and in many cases the bootloader has
95
# moved on into the app
96
#
97
# Solution: simply call "python.exe" instead of 'python' which magically calls it from the windows
98
# system using the same absolute path back into the WSL2's user's directory
99
# Requirements: Windows must have Python3.9.x (NTO 3.10.x) installed and a few packages.
100
import subprocess
101
try:
102
where_python = subprocess.check_output('where.exe python.exe', shell=True, text=True)
103
except subprocess.CalledProcessError:
104
#if where.exe can't find the file it returns a non-zero result which throws this exception
105
where_python = ""
106
if "python.exe" not in where_python:
107
print(self.get_full_wsl2_error_msg("Windows python.exe not found"))
108
return False
109
return True
110
111
def get_full_wsl2_error_msg(self, error_msg):
112
return ("""
113
****************************************
114
****************************************
115
WSL2 firmware uploads use the host's Windows Python.exe so it has access to the COM ports.
116
117
%s
118
Please download Windows Installer 3.9.x (not 3.10) from https://www.python.org/downloads/
119
and make sure to add it to your path during the installation. Once installed, run this
120
command in Powershell or Command Prompt to install some packages:
121
122
pip.exe install empy==3.3.4 pyserial
123
****************************************
124
****************************************
125
""" % error_msg)
126
127
def exec_command(self, cmd, **kw):
128
kw['stdout'] = sys.stdout
129
return super(upload_fw, self).exec_command(cmd, **kw)
130
131
def keyword(self):
132
return "Uploading"
133
134
class set_default_parameters(Task.Task):
135
color='CYAN'
136
always_run = True
137
def keyword(self):
138
return "apj_tool"
139
def run(self):
140
rel_default_parameters = self.env.get_flat('DEFAULT_PARAMETERS').replace("'", "")
141
abs_default_parameters = os.path.join(self.env.SRCROOT, rel_default_parameters)
142
apj_tool = self.env.APJ_TOOL
143
sys.path.append(os.path.dirname(apj_tool))
144
from apj_tool import embedded_defaults
145
defaults = embedded_defaults(self.inputs[0].abspath())
146
if defaults.find():
147
defaults.set_file(abs_default_parameters)
148
defaults.save()
149
150
151
class generate_bin(Task.Task):
152
color='CYAN'
153
# run_str="${OBJCOPY} -O binary ${SRC} ${TGT}"
154
always_run = True
155
EXTF_MEMORY_START = 0x90000000
156
EXTF_MEMORY_END = 0x90FFFFFF
157
INTF_MEMORY_START = 0x08000000
158
INTF_MEMORY_END = 0x08FFFFFF
159
def keyword(self):
160
return "Generating"
161
def run(self):
162
if self.env.HAS_EXTERNAL_FLASH_SECTIONS:
163
ret = self.split_sections()
164
if (ret < 0):
165
return ret
166
return ret
167
else:
168
cmd = [self.env.get_flat('OBJCOPY'), '-O', 'binary', self.inputs[0].relpath(), self.outputs[0].relpath()]
169
self.exec_command(cmd)
170
171
'''list sections and split into two binaries based on section's location in internal, external or in ram'''
172
def split_sections(self):
173
# get a list of sections
174
cmd = "'{}' -A -x {}".format(self.env.get_flat('SIZE'), self.inputs[0].relpath())
175
out = self.generator.bld.cmd_and_log(cmd, quiet=Context.BOTH, cwd=self.env.get_flat('BUILDROOT'))
176
extf_sections = []
177
intf_sections = []
178
is_text_in_extf = False
179
found_text_section = False
180
ramsections = []
181
for line in out.splitlines():
182
section_line = line.split()
183
if (len(section_line) < 3):
184
continue
185
try:
186
if int(section_line[2], 0) == 0:
187
continue
188
else:
189
addr = int(section_line[2], 0)
190
except ValueError:
191
continue
192
if (addr >= self.EXTF_MEMORY_START) and (addr <= self.EXTF_MEMORY_END):
193
extf_sections.append("--only-section=%s" % section_line[0])
194
if section_line[0] == '.text':
195
is_text_in_extf = True
196
found_text_section = True
197
elif (addr >= self.INTF_MEMORY_START) and (addr <= self.INTF_MEMORY_END):
198
intf_sections.append("--only-section=%s" % section_line[0])
199
if section_line[0] == '.text':
200
is_text_in_extf = False
201
found_text_section = True
202
else: # most likely RAM data, we place it in the same bin as text
203
ramsections.append(section_line[0])
204
205
if found_text_section:
206
for section in ramsections:
207
if is_text_in_extf:
208
extf_sections.append("--only-section=%s" % section)
209
else:
210
intf_sections.append("--only-section=%s" % section)
211
else:
212
Logs.error("Couldn't find .text section")
213
# create intf binary
214
if len(intf_sections):
215
cmd = "'{}' {} -O binary {} {}".format(self.env.get_flat('OBJCOPY'),
216
' '.join(intf_sections), self.inputs[0].relpath(), self.outputs[0].relpath())
217
else:
218
cmd = "cp /dev/null {}".format(self.outputs[0].relpath())
219
ret = self.exec_command(cmd)
220
if (ret < 0):
221
return ret
222
# create extf binary
223
cmd = "'{}' {} -O binary {} {}".format(self.env.get_flat('OBJCOPY'),
224
' '.join(extf_sections), self.inputs[0].relpath(), self.outputs[1].relpath())
225
return self.exec_command(cmd)
226
227
def __str__(self):
228
return self.outputs[0].path_from(self.generator.bld.bldnode)
229
230
def to_unsigned(i):
231
'''convert a possibly signed integer to unsigned'''
232
if i < 0:
233
i += 2**32
234
return i
235
236
def sign_firmware(image, private_keyfile):
237
'''sign firmware with private key'''
238
try:
239
import monocypher
240
except ImportError:
241
Logs.error("Please install monocypher with: python3 -m pip install pymonocypher==3.1.3.2")
242
return None
243
244
if monocypher.__version__ != "3.1.3.2":
245
Logs.error("must use monocypher 3.1.3.2, please run: python3 -m pip install pymonocypher==3.1.3.2")
246
return None
247
248
try:
249
key = open(private_keyfile, 'r').read()
250
except Exception as ex:
251
Logs.error("Failed to open %s" % private_keyfile)
252
return None
253
keytype = "PRIVATE_KEYV1:"
254
if not key.startswith(keytype):
255
Logs.error("Bad private key file %s" % private_keyfile)
256
return None
257
key = base64.b64decode(key[len(keytype):])
258
sig = monocypher.signature_sign(key, image)
259
sig_len = len(sig)
260
sig_version = 30437
261
return struct.pack("<IQ64s", sig_len+8, sig_version, sig)
262
263
264
class set_app_descriptor(Task.Task):
265
'''setup app descriptor in bin file'''
266
color='BLUE'
267
always_run = True
268
def keyword(self):
269
return "app_descriptor"
270
def run(self):
271
if self.generator.bld.env.AP_SIGNED_FIRMWARE:
272
descriptor = b'\x41\xa3\xe5\xf2\x65\x69\x92\x07'
273
else:
274
descriptor = b'\x40\xa2\xe4\xf1\x64\x68\x91\x06'
275
276
elf_file = self.inputs[0].abspath()
277
bin_file = self.inputs[1].abspath()
278
img = open(bin_file, 'rb').read()
279
offset = img.find(descriptor)
280
if offset == -1:
281
Logs.info("No APP_DESCRIPTOR found")
282
return
283
offset += len(descriptor)
284
# next 8 bytes is 64 bit CRC. We set first 4 bytes to
285
# CRC32 of image before descriptor and 2nd 4 bytes
286
# to CRC32 of image after descriptor. This is very efficient
287
# for bootloader to calculate
288
# after CRC comes image length and 32 bit git hash
289
upload_tools = self.env.get_flat('UPLOAD_TOOLS')
290
sys.path.append(upload_tools)
291
from uploader import crc32
292
if self.generator.bld.env.AP_SIGNED_FIRMWARE:
293
desc_len = 92
294
else:
295
desc_len = 16
296
img1 = bytearray(img[:offset])
297
img2 = bytearray(img[offset+desc_len:])
298
crc1 = to_unsigned(crc32(img1))
299
crc2 = to_unsigned(crc32(img2))
300
githash = to_unsigned(int('0x' + os.environ.get('GIT_VERSION', self.generator.bld.git_head_hash(short=True)),16))
301
if self.generator.bld.env.AP_SIGNED_FIRMWARE:
302
sig = bytearray([0 for i in range(76)])
303
if self.generator.bld.env.PRIVATE_KEY:
304
sig_signed = sign_firmware(img1+img2, self.generator.bld.env.PRIVATE_KEY)
305
if sig_signed:
306
Logs.info("Signed firmware")
307
sig = sig_signed
308
else:
309
self.generator.bld.fatal("Signing failed")
310
desc = struct.pack('<IIII76s', crc1, crc2, len(img), githash, sig)
311
else:
312
desc = struct.pack('<IIII', crc1, crc2, len(img), githash)
313
img = img[:offset] + desc + img[offset+desc_len:]
314
Logs.info("Applying APP_DESCRIPTOR %08x%08x" % (crc1, crc2))
315
open(bin_file, 'wb').write(img)
316
317
elf_img = open(elf_file,'rb').read()
318
zero_descriptor = descriptor + struct.pack("<IIII",0,0,0,0)
319
elf_ofs = elf_img.find(zero_descriptor)
320
if elf_ofs == -1:
321
Logs.info("No APP_DESCRIPTOR found in elf file")
322
return
323
elf_ofs += len(descriptor)
324
elf_img = elf_img[:elf_ofs] + desc + elf_img[elf_ofs+desc_len:]
325
Logs.info("Applying APP_DESCRIPTOR %08x%08x to elf" % (crc1, crc2))
326
open(elf_file, 'wb').write(elf_img)
327
328
329
class generate_apj(Task.Task):
330
'''generate an apj firmware file'''
331
color='CYAN'
332
always_run = True
333
def keyword(self):
334
return "apj_gen"
335
def run(self):
336
import json, time, base64, zlib
337
intf_img = open(self.inputs[0].abspath(),'rb').read()
338
if self.env.HAS_EXTERNAL_FLASH_SECTIONS:
339
extf_img = open(self.inputs[1].abspath(),'rb').read()
340
else:
341
extf_img = b""
342
d = {
343
"board_id": int(self.env.APJ_BOARD_ID),
344
"magic": "APJFWv1",
345
"description": "Firmware for a %s board" % self.env.APJ_BOARD_TYPE,
346
"image": base64.b64encode(zlib.compress(intf_img,9)).decode('utf-8'),
347
"extf_image": base64.b64encode(zlib.compress(extf_img,9)).decode('utf-8'),
348
"summary": self.env.BOARD,
349
"version": "0.1",
350
"image_size": len(intf_img),
351
"extf_image_size": len(extf_img),
352
"flash_total": int(self.env.FLASH_TOTAL),
353
"image_maxsize": int(self.env.FLASH_TOTAL),
354
"flash_free": int(self.env.FLASH_TOTAL) - len(intf_img),
355
"extflash_total": int(self.env.EXT_FLASH_SIZE_MB * 1024 * 1024),
356
"extflash_free": int(self.env.EXT_FLASH_SIZE_MB * 1024 * 1024) - len(extf_img),
357
"git_identity": self.generator.bld.git_head_hash(short=True),
358
"board_revision": 0,
359
"USBID": self.env.USBID
360
}
361
if self.env.MANUFACTURER:
362
d["manufacturer"] = self.env.MANUFACTURER
363
if self.env.BRAND_NAME:
364
d["brand_name"] = self.env.BRAND_NAME
365
if self.env.build_dates:
366
# we omit build_time when we don't have build_dates so that apj
367
# file is idential for same git hash and compiler
368
d["build_time"] = int(time.time())
369
apj_file = self.outputs[0].abspath()
370
f = open(apj_file, "w")
371
f.write(json.dumps(d, indent=4))
372
f.close()
373
374
class build_abin(Task.Task):
375
'''build an abin file for skyviper firmware upload via web UI'''
376
color='CYAN'
377
run_str='${TOOLS_SCRIPTS}/make_abin.sh ${SRC} ${TGT}'
378
always_run = True
379
def keyword(self):
380
return "Generating"
381
def __str__(self):
382
return self.outputs[0].path_from(self.generator.bld.bldnode)
383
384
class build_normalized_bins(Task.Task):
385
'''Move external flash binaries to regular location if regular bin is zero length'''
386
color='CYAN'
387
always_run = True
388
def run(self):
389
if self.env.HAS_EXTERNAL_FLASH_SECTIONS and os.path.getsize(self.inputs[0].abspath()) == 0:
390
os.remove(self.inputs[0].abspath())
391
shutil.move(self.inputs[1].abspath(), self.inputs[0].abspath())
392
393
def keyword(self):
394
return "bin cleanup"
395
396
class build_intel_hex(Task.Task):
397
'''build an intel hex file for upload with DFU'''
398
color='CYAN'
399
run_str='${TOOLS_SCRIPTS}/make_intel_hex.py ${SRC} ${FLASH_RESERVE_START_KB}'
400
always_run = True
401
def keyword(self):
402
return "Generating"
403
def __str__(self):
404
return self.outputs[0].path_from(self.generator.bld.bldnode)
405
406
@feature('ch_ap_program')
407
@after_method('process_source')
408
def chibios_firmware(self):
409
self.link_task.always_run = True
410
411
link_output = self.link_task.outputs[0]
412
hex_task = None
413
414
if self.bld.env.HAS_EXTERNAL_FLASH_SECTIONS:
415
bin_target = [self.bld.bldnode.find_or_declare('bin/' + link_output.change_ext('.bin').name),
416
self.bld.bldnode.find_or_declare('bin/' + link_output.change_ext('_extf.bin').name)]
417
else:
418
bin_target = [self.bld.bldnode.find_or_declare('bin/' + link_output.change_ext('.bin').name)]
419
apj_target = self.bld.bldnode.find_or_declare('bin/' + link_output.change_ext('.apj').name)
420
421
generate_bin_task = self.create_task('generate_bin', src=link_output, tgt=bin_target)
422
generate_bin_task.set_run_after(self.link_task)
423
424
generate_apj_task = self.create_task('generate_apj', src=bin_target, tgt=apj_target)
425
generate_apj_task.set_run_after(generate_bin_task)
426
427
if self.env.BUILD_ABIN:
428
abin_target = self.bld.bldnode.find_or_declare('bin/' + link_output.change_ext('.abin').name)
429
abin_task = self.create_task('build_abin', src=bin_target, tgt=abin_target)
430
abin_task.set_run_after(generate_apj_task)
431
432
cleanup_task = self.create_task('build_normalized_bins', src=bin_target)
433
cleanup_task.set_run_after(generate_apj_task)
434
435
bootloader_bin = self.bld.srcnode.make_node("Tools/bootloaders/%s_bl.bin" % self.env.BOARD)
436
if self.bld.env.HAVE_INTEL_HEX:
437
if os.path.exists(bootloader_bin.abspath()):
438
if int(self.bld.env.FLASH_RESERVE_START_KB) > 0:
439
hex_target = self.bld.bldnode.find_or_declare('bin/' + link_output.change_ext('_with_bl.hex').name)
440
else:
441
hex_target = self.bld.bldnode.find_or_declare('bin/' + link_output.change_ext('.hex').name)
442
hex_task = self.create_task('build_intel_hex', src=[bin_target[0], bootloader_bin], tgt=hex_target)
443
hex_task.set_run_after(cleanup_task)
444
else:
445
print("Not embedding bootloader; %s does not exist" % bootloader_bin)
446
447
if self.env.DEFAULT_PARAMETERS:
448
default_params_task = self.create_task('set_default_parameters',
449
src=link_output)
450
default_params_task.set_run_after(self.link_task)
451
generate_bin_task.set_run_after(default_params_task)
452
453
# we need to setup the app descriptor so the bootloader can validate the firmware
454
if not self.bld.env.BOOTLOADER:
455
app_descriptor_task = self.create_task('set_app_descriptor', src=[link_output,bin_target[0]])
456
app_descriptor_task.set_run_after(generate_bin_task)
457
generate_apj_task.set_run_after(app_descriptor_task)
458
if hex_task is not None:
459
hex_task.set_run_after(app_descriptor_task)
460
else:
461
generate_apj_task.set_run_after(generate_bin_task)
462
if hex_task is not None:
463
hex_task.set_run_after(generate_bin_task)
464
465
if self.bld.options.upload:
466
_upload_task = self.create_task('upload_fw', src=apj_target)
467
_upload_task.set_run_after(generate_apj_task)
468
469
if self.bld.options.upload_blueos:
470
_upload_task = self.create_task('upload_fw_blueos', src=link_output)
471
_upload_task.set_run_after(generate_apj_task)
472
473
def setup_canmgr_build(cfg):
474
'''enable CANManager build. By doing this here we can auto-enable CAN in
475
the build based on the presence of CAN pins in hwdef.dat except for AP_Periph builds'''
476
env = cfg.env
477
env.AP_LIBRARIES += [
478
'AP_DroneCAN',
479
'modules/DroneCAN/libcanard/*.c',
480
]
481
env.INCLUDES += [
482
cfg.srcnode.find_dir('modules/DroneCAN/libcanard').abspath(),
483
]
484
env.CFLAGS += ['-DHAL_CAN_IFACES=2']
485
486
if not env.AP_PERIPH:
487
env.DEFINES += [
488
'DRONECAN_CXX_WRAPPERS=1',
489
'USE_USER_HELPERS=1',
490
'CANARD_ENABLE_DEADLINE=1',
491
'CANARD_MULTI_IFACE=1',
492
'CANARD_ALLOCATE_SEM=1'
493
]
494
495
cfg.get_board().with_can = True
496
497
def setup_canperiph_build(cfg):
498
'''enable CAN build for peripherals'''
499
env = cfg.env
500
env.DEFINES += [
501
'CANARD_ENABLE_DEADLINE=1',
502
]
503
504
cfg.get_board().with_can = True
505
506
def load_env_vars(env):
507
'''optionally load extra environment variables from env.py in the build directory'''
508
print("Checking for env.py")
509
env_py = os.path.join(env.BUILDROOT, 'env.py')
510
if not os.path.exists(env_py):
511
print("No env.py found")
512
return
513
e = pickle.load(open(env_py, 'rb'))
514
for k in e.keys():
515
v = e[k]
516
if k == 'ROMFS_FILES':
517
env.ROMFS_FILES += v
518
continue
519
if k in env:
520
if isinstance(env[k], dict):
521
a = v.split('=')
522
env[k][a[0]] = '='.join(a[1:])
523
print("env updated %s=%s" % (k, v))
524
elif isinstance(env[k], list):
525
env[k].append(v)
526
print("env appended %s=%s" % (k, v))
527
else:
528
env[k] = v
529
print("env added %s=%s" % (k, v))
530
else:
531
env[k] = v
532
print("env set %s=%s" % (k, v))
533
if env.DEBUG or env.DEBUG_SYMBOLS:
534
env.CHIBIOS_BUILD_FLAGS += ' ENABLE_DEBUG_SYMBOLS=yes'
535
if env.ENABLE_ASSERTS:
536
env.CHIBIOS_BUILD_FLAGS += ' ENABLE_ASSERTS=yes'
537
if env.ENABLE_MALLOC_GUARD:
538
env.CHIBIOS_BUILD_FLAGS += ' ENABLE_MALLOC_GUARD=yes'
539
if env.ENABLE_STATS:
540
env.CHIBIOS_BUILD_FLAGS += ' ENABLE_STATS=yes'
541
if env.ENABLE_DFU_BOOT and env.BOOTLOADER:
542
env.CHIBIOS_BUILD_FLAGS += ' USE_ASXOPT=-DCRT0_ENTRY_HOOK=TRUE'
543
if env.AP_BOARD_START_TIME:
544
env.CHIBIOS_BUILD_FLAGS += ' AP_BOARD_START_TIME=0x%x' % env.AP_BOARD_START_TIME
545
546
547
def setup_optimization(env):
548
'''setup optimization flags for build'''
549
if env.DEBUG:
550
OPTIMIZE = "-Og"
551
elif env.OPTIMIZE:
552
OPTIMIZE = env.OPTIMIZE
553
else:
554
OPTIMIZE = "-Os"
555
env.CFLAGS += [ OPTIMIZE ]
556
env.CXXFLAGS += [ OPTIMIZE ]
557
env.CHIBIOS_BUILD_FLAGS += ' USE_COPT=%s' % OPTIMIZE
558
559
def configure(cfg):
560
cfg.find_program('make', var='MAKE')
561
#cfg.objcopy = cfg.find_program('%s-%s'%(cfg.env.TOOLCHAIN,'objcopy'), var='OBJCOPY', mandatory=True)
562
cfg.find_program('arm-none-eabi-objcopy', var='OBJCOPY')
563
env = cfg.env
564
bldnode = cfg.bldnode.make_node(cfg.variant)
565
def srcpath(path):
566
return cfg.srcnode.make_node(path).abspath()
567
568
def bldpath(path):
569
return bldnode.make_node(path).abspath()
570
env.AP_PROGRAM_FEATURES += ['ch_ap_program']
571
572
kw = env.AP_LIBRARIES_OBJECTS_KW
573
kw['features'] = Utils.to_list(kw.get('features', [])) + ['ch_ap_library']
574
575
env.CH_ROOT = srcpath('modules/ChibiOS')
576
env.CC_ROOT = srcpath('modules/CrashDebug/CrashCatcher')
577
env.AP_HAL_ROOT = srcpath('libraries/AP_HAL_ChibiOS')
578
env.BUILDDIR = bldpath('modules/ChibiOS')
579
env.BUILDROOT = bldpath('')
580
env.SRCROOT = srcpath('')
581
env.PT_DIR = srcpath('Tools/ardupilotwaf/chibios/image')
582
env.MKFW_TOOLS = srcpath('Tools/ardupilotwaf')
583
env.UPLOAD_TOOLS = srcpath('Tools/scripts')
584
env.CHIBIOS_SCRIPTS = srcpath('libraries/AP_HAL_ChibiOS/hwdef/scripts')
585
env.TOOLS_SCRIPTS = srcpath('Tools/scripts')
586
env.APJ_TOOL = srcpath('Tools/scripts/apj_tool.py')
587
env.SERIAL_PORT = srcpath('/dev/serial/by-id/*_STLink*')
588
589
# relative paths to pass to make, relative to directory that make is run from
590
env.CH_ROOT_REL = os.path.relpath(env.CH_ROOT, env.BUILDROOT)
591
env.CC_ROOT_REL = os.path.relpath(env.CC_ROOT, env.BUILDROOT)
592
env.AP_HAL_REL = os.path.relpath(env.AP_HAL_ROOT, env.BUILDROOT)
593
env.BUILDDIR_REL = os.path.relpath(env.BUILDDIR, env.BUILDROOT)
594
595
mk_custom = srcpath('libraries/AP_HAL_ChibiOS/hwdef/%s/chibios_board.mk' % env.BOARD)
596
mk_common = srcpath('libraries/AP_HAL_ChibiOS/hwdef/common/chibios_board.mk')
597
# see if there is a board specific make file
598
if os.path.exists(mk_custom):
599
env.BOARD_MK = mk_custom
600
else:
601
env.BOARD_MK = mk_common
602
603
if cfg.options.default_parameters:
604
cfg.msg('Default parameters', cfg.options.default_parameters, color='YELLOW')
605
env.DEFAULT_PARAMETERS = cfg.options.default_parameters
606
607
try:
608
ret = generate_hwdef_h(env)
609
except Exception:
610
cfg.fatal("Failed to process hwdef.dat")
611
if ret != 0:
612
cfg.fatal("Failed to process hwdef.dat ret=%d" % ret)
613
load_env_vars(cfg.env)
614
if env.HAL_NUM_CAN_IFACES and not env.AP_PERIPH:
615
setup_canmgr_build(cfg)
616
if env.HAL_NUM_CAN_IFACES and env.AP_PERIPH and not env.BOOTLOADER:
617
setup_canperiph_build(cfg)
618
if env.HAL_NUM_CAN_IFACES and env.AP_PERIPH and int(env.HAL_NUM_CAN_IFACES)>1 and not env.BOOTLOADER:
619
env.DEFINES += [ 'CANARD_MULTI_IFACE=1' ]
620
setup_optimization(cfg.env)
621
622
def generate_hwdef_h(env):
623
'''run chibios_hwdef.py'''
624
import subprocess
625
if env.BOOTLOADER:
626
if len(env.HWDEF) == 0:
627
env.HWDEF = os.path.join(env.SRCROOT, 'libraries/AP_HAL_ChibiOS/hwdef/%s/hwdef-bl.dat' % env.BOARD)
628
else:
629
# update to using hwdef-bl.dat
630
env.HWDEF = env.HWDEF.replace('hwdef.dat', 'hwdef-bl.dat')
631
env.BOOTLOADER_OPTION="--bootloader"
632
else:
633
if len(env.HWDEF) == 0:
634
env.HWDEF = os.path.join(env.SRCROOT, 'libraries/AP_HAL_ChibiOS/hwdef/%s/hwdef.dat' % env.BOARD)
635
env.BOOTLOADER_OPTION=""
636
637
if env.AP_SIGNED_FIRMWARE:
638
print(env.BOOTLOADER_OPTION)
639
env.BOOTLOADER_OPTION += " --signed-fw"
640
print(env.BOOTLOADER_OPTION)
641
hwdef_script = os.path.join(env.SRCROOT, 'libraries/AP_HAL_ChibiOS/hwdef/scripts/chibios_hwdef.py')
642
hwdef_out = env.BUILDROOT
643
if not os.path.exists(hwdef_out):
644
os.mkdir(hwdef_out)
645
python = sys.executable
646
cmd = "{0} '{1}' -D '{2}' --params '{3}' '{4}'".format(python, hwdef_script, hwdef_out, env.DEFAULT_PARAMETERS, env.HWDEF)
647
if env.HWDEF_EXTRA:
648
cmd += " '{0}'".format(env.HWDEF_EXTRA)
649
if env.BOOTLOADER_OPTION:
650
cmd += " " + env.BOOTLOADER_OPTION
651
return subprocess.call(cmd, shell=True)
652
653
def pre_build(bld):
654
'''pre-build hook to change dynamic sources'''
655
load_env_vars(bld.env)
656
if bld.env.HAL_NUM_CAN_IFACES:
657
bld.get_board().with_can = True
658
hwdef_h = os.path.join(bld.env.BUILDROOT, 'hwdef.h')
659
if not os.path.exists(hwdef_h):
660
print("Generating hwdef.h")
661
try:
662
ret = generate_hwdef_h(bld.env)
663
except Exception:
664
bld.fatal("Failed to process hwdef.dat")
665
if ret != 0:
666
bld.fatal("Failed to process hwdef.dat ret=%d" % ret)
667
setup_optimization(bld.env)
668
669
def build(bld):
670
671
672
hwdef_rule="%s '%s/hwdef/scripts/chibios_hwdef.py' -D '%s' --params '%s' '%s'" % (
673
bld.env.get_flat('PYTHON'),
674
bld.env.AP_HAL_ROOT,
675
bld.env.BUILDROOT,
676
bld.env.default_parameters,
677
bld.env.HWDEF)
678
if bld.env.HWDEF_EXTRA:
679
hwdef_rule += " " + bld.env.HWDEF_EXTRA
680
if bld.env.BOOTLOADER_OPTION:
681
hwdef_rule += " " + bld.env.BOOTLOADER_OPTION
682
bld(
683
# build hwdef.h from hwdef.dat. This is needed after a waf clean
684
source=bld.path.ant_glob(bld.env.HWDEF),
685
rule=hwdef_rule,
686
group='dynamic_sources',
687
target=[bld.bldnode.find_or_declare('hwdef.h'),
688
bld.bldnode.find_or_declare('ldscript.ld'),
689
bld.bldnode.find_or_declare('hw.dat')]
690
)
691
692
bld(
693
# create the file modules/ChibiOS/include_dirs
694
rule="touch Makefile && BUILDDIR=${BUILDDIR_REL} BUILDROOT=${BUILDROOT} CRASHCATCHER=${CC_ROOT_REL} CHIBIOS=${CH_ROOT_REL} AP_HAL=${AP_HAL_REL} ${CHIBIOS_BUILD_FLAGS} ${CHIBIOS_BOARD_NAME} ${MAKE} pass -f '${BOARD_MK}'",
695
group='dynamic_sources',
696
target=bld.bldnode.find_or_declare('modules/ChibiOS/include_dirs')
697
)
698
699
bld(
700
# create the file modules/ChibiOS/include_dirs
701
rule="echo // BUILD_FLAGS: ${BUILDDIR_REL} ${BUILDROOT} ${CC_ROOT_REL} ${CH_ROOT_REL} ${AP_HAL_REL} ${CHIBIOS_BUILD_FLAGS} ${CHIBIOS_BOARD_NAME} ${HAL_MAX_STACK_FRAME_SIZE} > chibios_flags.h",
702
group='dynamic_sources',
703
target=bld.bldnode.find_or_declare('chibios_flags.h')
704
)
705
706
common_src = [bld.bldnode.find_or_declare('hwdef.h'),
707
bld.bldnode.find_or_declare('hw.dat'),
708
bld.bldnode.find_or_declare('ldscript.ld'),
709
bld.bldnode.find_or_declare('common.ld'),
710
bld.bldnode.find_or_declare('modules/ChibiOS/include_dirs')]
711
common_src += bld.path.ant_glob('libraries/AP_HAL_ChibiOS/hwdef/common/*.[ch]')
712
common_src += bld.path.ant_glob('libraries/AP_HAL_ChibiOS/hwdef/common/*.mk')
713
common_src += bld.path.ant_glob('modules/ChibiOS/os/hal/**/*.[ch]')
714
common_src += bld.path.ant_glob('modules/ChibiOS/os/hal/**/*.mk')
715
if bld.env.ROMFS_FILES:
716
common_src += [bld.bldnode.find_or_declare('ap_romfs_embedded.h')]
717
718
if bld.env.ENABLE_CRASHDUMP:
719
ch_task = bld(
720
# build libch.a from ChibiOS sources and hwdef.h
721
rule="BUILDDIR='${BUILDDIR_REL}' BUILDROOT='${BUILDROOT}' CRASHCATCHER='${CC_ROOT_REL}' CHIBIOS='${CH_ROOT_REL}' AP_HAL=${AP_HAL_REL} ${CHIBIOS_BUILD_FLAGS} ${CHIBIOS_BOARD_NAME} ${HAL_MAX_STACK_FRAME_SIZE} '${MAKE}' -j%u lib -f '${BOARD_MK}'" % bld.options.jobs,
722
group='dynamic_sources',
723
source=common_src,
724
target=[bld.bldnode.find_or_declare('modules/ChibiOS/libch.a'), bld.bldnode.find_or_declare('modules/ChibiOS/libcc.a')]
725
)
726
else:
727
ch_task = bld(
728
# build libch.a from ChibiOS sources and hwdef.h
729
rule="BUILDDIR='${BUILDDIR_REL}' BUILDROOT='${BUILDROOT}' CHIBIOS='${CH_ROOT_REL}' AP_HAL=${AP_HAL_REL} ${CHIBIOS_BUILD_FLAGS} ${CHIBIOS_BOARD_NAME} ${HAL_MAX_STACK_FRAME_SIZE} '${MAKE}' -j%u lib -f '${BOARD_MK}'" % bld.options.jobs,
730
group='dynamic_sources',
731
source=common_src,
732
target=bld.bldnode.find_or_declare('modules/ChibiOS/libch.a')
733
)
734
ch_task.name = "ChibiOS_lib"
735
DSP_LIBS = {
736
'cortex-m4' : 'libarm_cortexM4lf_math.a',
737
'cortex-m7' : 'libarm_cortexM7lfdp_math.a',
738
}
739
if bld.env.CORTEX in DSP_LIBS:
740
libname = DSP_LIBS[bld.env.CORTEX]
741
# we need to copy the library on cygwin as it doesn't handle linking outside build tree
742
shutil.copyfile(os.path.join(bld.env.SRCROOT,'libraries/AP_GyroFFT/CMSIS_5/lib',libname),
743
os.path.join(bld.env.BUILDROOT,'modules/ChibiOS/libDSP.a'))
744
bld.env.LIB += ['DSP']
745
bld.env.LIB += ['ch']
746
bld.env.LIBPATH += ['modules/ChibiOS/']
747
if bld.env.ENABLE_CRASHDUMP:
748
bld.env.LINKFLAGS += ['-Wl,-whole-archive', 'modules/ChibiOS/libcc.a', '-Wl,-no-whole-archive']
749
# list of functions that will be wrapped to move them out of libc into our
750
# own code
751
wraplist = ['sscanf', 'fprintf', 'snprintf', 'vsnprintf', 'vasprintf', 'asprintf', 'vprintf', 'scanf', 'printf']
752
753
# list of functions that we will give a link error for if they are
754
# used. This is to prevent accidential use of these functions
755
blacklist = ['_sbrk', '_sbrk_r', '_malloc_r', '_calloc_r', '_free_r', 'ftell',
756
'fopen', 'fflush', 'fwrite', 'fread', 'fputs', 'fgets',
757
'clearerr', 'fseek', 'ferror', 'fclose', 'tmpfile', 'getc', 'ungetc', 'feof',
758
'ftell', 'freopen', 'remove', 'vfprintf', 'vfprintf_r', 'fscanf',
759
'_gettimeofday', '_times', '_times_r', '_gettimeofday_r', 'time', 'clock']
760
761
# these functions use global state that is not thread safe
762
blacklist += ['gmtime']
763
764
for w in wraplist + blacklist:
765
bld.env.LINKFLAGS += ['-Wl,--wrap,%s' % w]
766
767