Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
__init__.py
00001 """ 00002 mbed SDK 00003 Copyright (c) 2011-2016 ARM Limited 00004 00005 Licensed under the Apache License, Version 2.0 (the "License"); 00006 you may not use this file except in compliance with the License. 00007 You may obtain a copy of the License at 00008 00009 http://www.apache.org/licenses/LICENSE-2.0 00010 00011 Unless required by applicable law or agreed to in writing, software 00012 distributed under the License is distributed on an "AS IS" BASIS, 00013 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00014 See the License for the specific language governing permissions and 00015 limitations under the License. 00016 """ 00017 from __future__ import print_function 00018 00019 import os 00020 import binascii 00021 import struct 00022 import shutil 00023 import inspect 00024 import sys 00025 from copy import copy 00026 from inspect import getmro 00027 from collections import namedtuple, Mapping 00028 from tools.resources import FileType 00029 from tools.targets.LPC import patch 00030 from tools.paths import TOOLS_BOOTLOADERS 00031 from tools.utils import json_file_to_dict 00032 00033 __all__ = ["target", "TARGETS", "TARGET_MAP", "TARGET_NAMES", "CORE_LABELS", 00034 "CORE_ARCH", "HookError", "generate_py_target", "Target", 00035 "CUMULATIVE_ATTRIBUTES", "get_resolution_order"] 00036 00037 CORE_LABELS = { 00038 "Cortex-M0": ["M0", "CORTEX_M", "LIKE_CORTEX_M0", "CORTEX"], 00039 "Cortex-M0+": ["M0P", "CORTEX_M", "LIKE_CORTEX_M0", "CORTEX"], 00040 "Cortex-M1": ["M1", "CORTEX_M", "LIKE_CORTEX_M1", "CORTEX"], 00041 "Cortex-M3": ["M3", "CORTEX_M", "LIKE_CORTEX_M3", "CORTEX"], 00042 "Cortex-M4": ["M4", "CORTEX_M", "RTOS_M4_M7", "LIKE_CORTEX_M4", "CORTEX"], 00043 "Cortex-M4F": ["M4", "CORTEX_M", "RTOS_M4_M7", "LIKE_CORTEX_M4", "CORTEX"], 00044 "Cortex-M7": ["M7", "CORTEX_M", "RTOS_M4_M7", "LIKE_CORTEX_M7", "CORTEX"], 00045 "Cortex-M7F": ["M7", "CORTEX_M", "RTOS_M4_M7", "LIKE_CORTEX_M7", "CORTEX"], 00046 "Cortex-M7FD": ["M7", "CORTEX_M", "RTOS_M4_M7", "LIKE_CORTEX_M7", "CORTEX"], 00047 "Cortex-A9": ["A9", "CORTEX_A", "LIKE_CORTEX_A9", "CORTEX"], 00048 "Cortex-M23": ["M23", "CORTEX_M", "LIKE_CORTEX_M23", "CORTEX"], 00049 "Cortex-M23-NS": ["M23", "M23_NS", "CORTEX_M", "LIKE_CORTEX_M23", "CORTEX"], 00050 "Cortex-M33": ["M33", "CORTEX_M", "LIKE_CORTEX_M33", "CORTEX"], 00051 "Cortex-M33-NS": ["M33", "M33_NS", "CORTEX_M", "LIKE_CORTEX_M33", "CORTEX"] 00052 } 00053 00054 CORE_ARCH = { 00055 "Cortex-M0": 6, 00056 "Cortex-M0+": 6, 00057 "Cortex-M1": 6, 00058 "Cortex-M3": 7, 00059 "Cortex-M4": 7, 00060 "Cortex-M4F": 7, 00061 "Cortex-M7": 7, 00062 "Cortex-M7F": 7, 00063 "Cortex-M7FD": 7, 00064 "Cortex-A9": 7, 00065 "Cortex-M23": 8, 00066 "Cortex-M23-NS": 8, 00067 "Cortex-M33": 8, 00068 "Cortex-M33-NS": 8, 00069 } 00070 00071 ################################################################################ 00072 # Generic Target class that reads and interprets the data in targets.json 00073 00074 class HookError(Exception): 00075 """ A simple class that represents all the exceptions associated with 00076 hooking 00077 """ 00078 pass 00079 00080 CACHES = {} 00081 def cached (func): 00082 """A simple decorator used for automatically caching data returned by a 00083 function 00084 """ 00085 def wrapper(*args, **kwargs): 00086 """The wrapped function itself""" 00087 if (func.__name__, args) not in CACHES: 00088 CACHES[(func.__name__, args)] = func(*args, **kwargs) 00089 return CACHES[(func.__name__, args)] 00090 return wrapper 00091 00092 00093 # Cumulative attributes can have values appended to them, so they 00094 # need to be computed differently than regular attributes 00095 CUMULATIVE_ATTRIBUTES = ['extra_labels', 'macros', 'device_has', 'features', 'components'] 00096 00097 00098 def get_resolution_order (json_data, target_name, order, level=0): 00099 """ Return the order in which target descriptions are searched for 00100 attributes. This mimics the Python 2.2 method resolution order, which 00101 is what the old targets.py module used. For more details, check 00102 http://makina-corpus.com/blog/metier/2014/python-tutorial-understanding-python-mro-class-search-path 00103 The resolution order contains (name, level) tuples, where "name" is the 00104 name of the class and "level" is the level in the inheritance hierarchy 00105 (the target itself is at level 0, its first parent at level 1, its 00106 parent's parent at level 2 and so on) 00107 """ 00108 # the resolution order can't contain duplicate target names 00109 if target_name not in [l[0] for l in order]: 00110 order.append((target_name, level)) 00111 parents = json_data[target_name].get("inherits", []) 00112 for par in parents: 00113 order = get_resolution_order(json_data, par, order, level + 1) 00114 return order 00115 00116 00117 def target (name, json_data): 00118 """Construct a target object""" 00119 resolution_order = get_resolution_order(json_data, name, []) 00120 resolution_order_names = [tgt for tgt, _ in resolution_order] 00121 return Target(name=name, 00122 json_data={key: value for key, value in json_data.items() 00123 if key in resolution_order_names}, 00124 resolution_order=resolution_order, 00125 resolution_order_names=resolution_order_names) 00126 00127 def generate_py_target (new_targets, name): 00128 """Add one or more new target(s) represented as a Python dictionary 00129 in 'new_targets'. It is an error to add a target with a name that 00130 already exists. 00131 """ 00132 base_targets = Target.get_json_target_data() 00133 for new_target in new_targets.keys(): 00134 if new_target in base_targets: 00135 raise Exception("Attempt to add target '%s' that already exists" 00136 % new_target) 00137 total_data = {} 00138 total_data.update(new_targets) 00139 total_data.update(base_targets) 00140 return target(name, total_data) 00141 00142 class Target (namedtuple("Target", "name json_data resolution_order resolution_order_names")): 00143 """An object to represent a Target (MCU/Board)""" 00144 00145 # Default location of the 'targets.json' file 00146 __targets_json_location_default = os.path.join( 00147 os.path.dirname(os.path.abspath(__file__)), '..', '..', 'targets', 'targets.json') 00148 00149 # Current/new location of the 'targets.json' file 00150 __targets_json_location = None 00151 00152 # Extra custom targets files 00153 __extra_target_json_files = [] 00154 00155 @staticmethod 00156 @cached 00157 def get_json_target_data (): 00158 """Load the description of JSON target data""" 00159 targets = json_file_to_dict(Target.__targets_json_location or 00160 Target.__targets_json_location_default) 00161 00162 for extra_target in Target.__extra_target_json_files: 00163 for k, v in json_file_to_dict(extra_target).items(): 00164 if k in targets: 00165 print('WARNING: Custom target "%s" cannot replace existing ' 00166 'target.' % k) 00167 else: 00168 targets[k] = v 00169 00170 return targets 00171 00172 @staticmethod 00173 def add_extra_targets(source_dir): 00174 extra_targets_file = os.path.join(source_dir, "custom_targets.json") 00175 if os.path.exists(extra_targets_file): 00176 Target.__extra_target_json_files.append(extra_targets_file) 00177 CACHES.clear() 00178 00179 @staticmethod 00180 def set_targets_json_location (location=None): 00181 """Set the location of the targets.json file""" 00182 Target.__targets_json_location = (location or 00183 Target.__targets_json_location_default) 00184 Target.__extra_target_json_files = [] 00185 # Invalidate caches, since the location of the JSON file changed 00186 CACHES.clear() 00187 00188 @staticmethod 00189 @cached 00190 def get_module_data (): 00191 """Get the members of this module using Python's "inspect" module""" 00192 return dict([(m[0], m[1]) for m in 00193 inspect.getmembers(sys.modules[__name__])]) 00194 00195 @staticmethod 00196 def __add_paths_to_progen(data): 00197 """Modify the exporter specification ("progen") by changing all 00198 "template" keys to full paths 00199 """ 00200 out = {} 00201 for key, val in data.items(): 00202 if isinstance(val, dict): 00203 out[key] = Target.__add_paths_to_progen(val) 00204 elif key == "template": 00205 out[key] = [os.path.join(os.path.dirname(__file__), 'export', v) 00206 for v in val] 00207 else: 00208 out[key] = val 00209 return out 00210 00211 def __getattr_cumulative(self, attrname): 00212 """Look for the attribute in the class and its parents, as defined by 00213 the resolution order 00214 """ 00215 tdata = self.json_data 00216 # For a cumulative attribute, figure out when it was defined the 00217 # last time (in attribute resolution order) then follow the "_add" 00218 # and "_remove" data fields 00219 for idx, tgt in enumerate(self.resolution_order): 00220 # the attribute was defined at this level in the resolution 00221 # order 00222 if attrname in tdata[tgt[0]]: 00223 def_idx = idx 00224 break 00225 else: 00226 return [] 00227 # Get the starting value of the attribute 00228 starting_value = (tdata[self.resolution_order[def_idx][0]][attrname] 00229 or [])[:] 00230 # Traverse the resolution list in high inheritance to low 00231 # inheritance level, left to right order to figure out all the 00232 # other classes that change the definition by adding or removing 00233 # elements 00234 for idx in range(self.resolution_order[def_idx][1] - 1, -1, -1): 00235 same_level_targets = [tar[0] for tar in self.resolution_order 00236 if tar[1] == idx] 00237 for tar in same_level_targets: 00238 data = tdata[tar] 00239 # Do we have anything to add ? 00240 if (attrname + "_add") in data: 00241 starting_value.extend(data[attrname + "_add"]) 00242 # Do we have anything to remove ? 00243 if (attrname + "_remove") in data: 00244 # Macros can be defined either without a value (MACRO) 00245 # or with a value (MACRO=10). When removing, we specify 00246 # only the name of the macro, without the value. So we 00247 # need to create a mapping between the macro name and 00248 # its value. This will work for extra_labels and other 00249 # type of arrays as well, since they fall into the 00250 # "macros without a value" category (simple definitions 00251 # without a value). 00252 name_def_map = {} 00253 for crtv in starting_value: 00254 if crtv.find('=') != -1: 00255 temp = crtv.split('=') 00256 if len(temp) != 2: 00257 raise ValueError( 00258 "Invalid macro definition '%s'" % crtv) 00259 name_def_map[temp[0]] = crtv 00260 else: 00261 name_def_map[crtv] = crtv 00262 for element in data[attrname + "_remove"]: 00263 if element not in name_def_map: 00264 raise ValueError( 00265 ("Unable to remove '%s' in '%s.%s' since " 00266 % (element, self.name, attrname)) + 00267 "it doesn't exist") 00268 starting_value.remove(name_def_map[element]) 00269 return starting_value 00270 00271 def __getattr_helper(self, attrname): 00272 """Compute the value of a given target attribute""" 00273 if attrname in CUMULATIVE_ATTRIBUTES: 00274 return self.__getattr_cumulative (attrname) 00275 else: 00276 tdata = self.json_data 00277 starting_value = None 00278 for tgt in self.resolution_order: 00279 data = tdata[tgt[0]] 00280 try: 00281 return data[attrname] 00282 except KeyError: 00283 pass 00284 else: # Attribute not found 00285 raise AttributeError( 00286 "Attribute '%s' not found in target '%s'" 00287 % (attrname, self.name)) 00288 00289 def __getattr__ (self, attrname): 00290 """ Return the value of an attribute. This function only computes the 00291 attribute's value once, then adds it to the instance attributes (in 00292 __dict__), so the next time it is returned directly 00293 """ 00294 result = self.__getattr_helper (attrname) 00295 self.__dict__[attrname] = result 00296 return result 00297 00298 @staticmethod 00299 @cached 00300 def get_target (target_name): 00301 """ Return the target instance starting from the target name """ 00302 return target(target_name, Target.get_json_target_data()) 00303 00304 00305 @property 00306 def program_cycle_s (self): 00307 """Special override for program_cycle_s as it's default value depends 00308 upon is_disk_virtual 00309 """ 00310 try: 00311 return self.__getattr__ ("program_cycle_s") 00312 except AttributeError: 00313 return 4 if self.is_disk_virtual else 1.5 00314 00315 @property 00316 def labels (self): 00317 """Get all possible labels for this target""" 00318 names = copy(self.resolution_order_names) 00319 if "Target" in names: 00320 names.remove("Target") 00321 labels = (names + CORE_LABELS[self.core] + self.extra_labels) 00322 return labels 00323 00324 def init_hooks (self, hook, toolchain): 00325 """Initialize the post-build hooks for a toolchain. For now, this 00326 function only allows "post binary" hooks (hooks that are executed 00327 after the binary image is extracted from the executable file) 00328 00329 Positional Arguments: 00330 hook - the hook object to add post-binary-hooks to 00331 toolchain - the toolchain object for inspection 00332 """ 00333 00334 # If there's no hook, simply return 00335 try: 00336 hook_data = self.post_binary_hook 00337 except AttributeError: 00338 return 00339 # A hook was found. The hook's name is in the format 00340 # "classname.functionname" 00341 temp = hook_data["function"].split(".") 00342 if len(temp) != 2: 00343 raise HookError( 00344 ("Invalid format for hook '%s' in target '%s'" 00345 % (hook_data["function"], self.name)) + 00346 " (must be 'class_name.function_name')") 00347 class_name, function_name = temp 00348 # "class_name" must refer to a class in this file, so check if the 00349 # class exists 00350 mdata = self.get_module_data () 00351 if class_name not in mdata or \ 00352 not inspect.isclass(mdata[class_name]): 00353 raise HookError( 00354 ("Class '%s' required by '%s' in target '%s'" 00355 % (class_name, hook_data["function"], self.name)) + 00356 " not found in targets.py") 00357 # "function_name" must refer to a static function inside class 00358 # "class_name" 00359 cls = mdata[class_name] 00360 if (not hasattr(cls, function_name)) or \ 00361 (not inspect.isfunction(getattr(cls, function_name))): 00362 raise HookError( 00363 ("Static function '%s' " % function_name) + 00364 ("required by '%s' " % hook_data["function"]) + 00365 ("in target '%s' " % self.name) + 00366 ("not found in class '%s'" % class_name)) 00367 # Check if the hook specification also has toolchain restrictions 00368 toolchain_restrictions = set(hook_data.get("toolchains", [])) 00369 toolchain_labels = set(c.__name__ for c in getmro(toolchain.__class__)) 00370 if toolchain_restrictions and \ 00371 not toolchain_labels.intersection(toolchain_restrictions): 00372 return 00373 # Finally, hook the requested function 00374 hook.hook_add_binary("post", getattr(cls, function_name)) 00375 00376 ################################################################################ 00377 # Target specific code goes in this section 00378 # This code can be invoked from the target description using the 00379 # "post_binary_hook" key 00380 00381 class LPCTargetCode (object): 00382 """General LPC Target patching code""" 00383 @staticmethod 00384 def lpc_patch (t_self, resources, elf, binf): 00385 """Patch an elf file""" 00386 t_self.notify.debug("LPC Patch: %s" % os.path.split(binf)[1]) 00387 patch(binf) 00388 00389 class LPC4088Code (object): 00390 """Code specific to the LPC4088""" 00391 @staticmethod 00392 def binary_hook (t_self, resources, elf, binf): 00393 """Hook to be run after an elf file is built""" 00394 if not os.path.isdir(binf): 00395 # Regular binary file, nothing to do 00396 LPCTargetCode.lpc_patch(t_self, resources, elf, binf) 00397 return 00398 outbin = open(binf + ".temp", "wb") 00399 partf = open(os.path.join(binf, "ER_IROM1"), "rb") 00400 # Pad the fist part (internal flash) with 0xFF to 512k 00401 data = partf.read() 00402 outbin.write(data) 00403 outbin.write('\xFF' * (512*1024 - len(data))) 00404 partf.close() 00405 # Read and append the second part (external flash) in chunks of fixed 00406 # size 00407 chunksize = 128 * 1024 00408 partf = open(os.path.join(binf, "ER_IROM2"), "rb") 00409 while True: 00410 data = partf.read(chunksize) 00411 outbin.write(data) 00412 if len(data) < chunksize: 00413 break 00414 partf.close() 00415 outbin.close() 00416 # Remove the directory with the binary parts and rename the temporary 00417 # file to 'binf' 00418 shutil.rmtree(binf, True) 00419 os.rename(binf + '.temp', binf) 00420 t_self.notify.debug("Generated custom binary file (internal flash + SPIFI)") 00421 LPCTargetCode.lpc_patch(t_self, resources, elf, binf) 00422 00423 class TEENSY3_1Code (object): 00424 """Hooks for the TEENSY3.1""" 00425 @staticmethod 00426 def binary_hook (t_self, resources, elf, binf): 00427 """Hook that is run after elf is generated""" 00428 # This function is referenced by old versions of targets.json and should 00429 # be kept for backwards compatibility. 00430 pass 00431 00432 class MTSCode(object): 00433 """Generic MTS code""" 00434 @staticmethod 00435 def _combine_bins_helper(target_name, binf): 00436 """combine bins with the bootloader for a particular target""" 00437 loader = os.path.join(TOOLS_BOOTLOADERS, target_name, "bootloader.bin") 00438 target = binf + ".tmp" 00439 if not os.path.exists(loader): 00440 print("Can't find bootloader binary: " + loader) 00441 return 00442 outbin = open(target, 'w+b') 00443 part = open(loader, 'rb') 00444 data = part.read() 00445 outbin.write(data) 00446 outbin.write('\xFF' * (64*1024 - len(data))) 00447 part.close() 00448 part = open(binf, 'rb') 00449 data = part.read() 00450 outbin.write(data) 00451 part.close() 00452 outbin.seek(0, 0) 00453 data = outbin.read() 00454 outbin.seek(0, 1) 00455 crc = struct.pack('<I', binascii.crc32(data) & 0xFFFFFFFF) 00456 outbin.write(crc) 00457 outbin.close() 00458 os.remove(binf) 00459 os.rename(target, binf) 00460 00461 @staticmethod 00462 def combine_bins_mts_dot (t_self, resources, elf, binf): 00463 """A hook for the MTS MDOT""" 00464 MTSCode._combine_bins_helper("MTS_MDOT_F411RE", binf) 00465 00466 @staticmethod 00467 def combine_bins_mts_dragonfly (t_self, resources, elf, binf): 00468 """A hoof for the MTS Dragonfly""" 00469 MTSCode._combine_bins_helper("MTS_DRAGONFLY_F411RE", binf) 00470 00471 @staticmethod 00472 def combine_bins_mtb_mts_dragonfly (t_self, resources, elf, binf): 00473 """A hook for the MTB MTS Dragonfly""" 00474 MTSCode._combine_bins_helper("MTB_MTS_DRAGONFLY", binf) 00475 00476 class MCU_NRF51Code (object): 00477 """NRF51 Hooks""" 00478 @staticmethod 00479 def binary_hook (t_self, resources, _, binf): 00480 """Hook that merges the soft device with the bin file""" 00481 # Scan to find the actual paths of soft device 00482 sdf = None 00483 for softdevice_and_offset_entry\ 00484 in t_self.target.EXPECTED_SOFTDEVICES_WITH_OFFSETS: 00485 for hexf in resources.get_file_paths(FileType.HEX): 00486 if hexf.find(softdevice_and_offset_entry['name']) != -1: 00487 t_self.notify.debug("SoftDevice file found %s." 00488 % softdevice_and_offset_entry['name']) 00489 sdf = hexf 00490 00491 if sdf is not None: 00492 break 00493 if sdf is not None: 00494 break 00495 00496 if sdf is None: 00497 t_self.notify.debug("Hex file not found. Aborting.") 00498 return 00499 00500 # Look for bootloader file that matches this soft device or bootloader 00501 # override image 00502 blf = None 00503 if t_self.target.MERGE_BOOTLOADER is True: 00504 for hexf in resources.get_file_paths(FileType.HEX): 00505 if hexf.find(t_self.target.OVERRIDE_BOOTLOADER_FILENAME) != -1: 00506 t_self.notify.debug("Bootloader file found %s." 00507 % t_self.target.OVERRIDE_BOOTLOADER_FILENAME) 00508 blf = hexf 00509 break 00510 elif hexf.find(softdevice_and_offset_entry['boot']) != -1: 00511 t_self.notify.debug("Bootloader file found %s." 00512 % softdevice_and_offset_entry['boot']) 00513 blf = hexf 00514 break 00515 00516 # Merge user code with softdevice 00517 from intelhex import IntelHex 00518 binh = IntelHex() 00519 _, ext = os.path.splitext(binf) 00520 if ext == ".hex": 00521 binh.loadhex(binf) 00522 elif ext == ".bin": 00523 binh.loadbin(binf, softdevice_and_offset_entry['offset']) 00524 00525 if t_self.target.MERGE_SOFT_DEVICE is True: 00526 t_self.notify.debug("Merge SoftDevice file %s" 00527 % softdevice_and_offset_entry['name']) 00528 sdh = IntelHex(sdf) 00529 sdh.start_addr = None 00530 binh.merge(sdh) 00531 00532 if t_self.target.MERGE_BOOTLOADER is True and blf is not None: 00533 t_self.notify.debug("Merge BootLoader file %s" % blf) 00534 blh = IntelHex(blf) 00535 blh.start_addr = None 00536 binh.merge(blh) 00537 00538 with open(binf.replace(".bin", ".hex"), "w") as fileout: 00539 binh.write_hex_file(fileout, write_start_addr=False) 00540 00541 class NCS36510TargetCode: 00542 @staticmethod 00543 def ncs36510_addfib(t_self, resources, elf, binf): 00544 from tools.targets.NCS import add_fib_at_start 00545 print("binf ", binf) 00546 add_fib_at_start(binf[:-4]) 00547 00548 class RTL8195ACode : 00549 """RTL8195A Hooks""" 00550 @staticmethod 00551 def binary_hook(t_self, resources, elf, binf): 00552 from tools.targets.REALTEK_RTL8195AM import rtl8195a_elf2bin 00553 rtl8195a_elf2bin(t_self, elf, binf) 00554 ################################################################################ 00555 00556 # Instantiate all public targets 00557 def update_target_data(): 00558 TARGETS[:] = [Target.get_target(tgt) for tgt, obj 00559 in Target.get_json_target_data().items() 00560 if obj.get("public", True)] 00561 # Map each target name to its unique instance 00562 TARGET_MAP.clear() 00563 TARGET_MAP.update(dict([(tgt.name, tgt) for tgt in TARGETS])) 00564 TARGET_NAMES[:] = TARGET_MAP.keys() 00565 00566 TARGETS = [] 00567 TARGET_MAP = dict() 00568 TARGET_NAMES = [] 00569 00570 update_target_data() 00571 00572 # Some targets with different name have the same exporters 00573 EXPORT_MAP = {} 00574 00575 # Detection APIs 00576 def get_target_detect_codes (): 00577 """ Returns dictionary mapping detect_code -> platform_name 00578 """ 00579 result = {} 00580 for tgt in TARGETS: 00581 for detect_code in tgt.detect_code: 00582 result[detect_code] = tgt.name 00583 return result 00584 00585 def set_targets_json_location (location=None): 00586 """Sets the location of the JSON file that contains the targets""" 00587 # First instruct Target about the new location 00588 Target.set_targets_json_location(location) 00589 # Then re-initialize TARGETS, TARGET_MAP and TARGET_NAMES. The 00590 # re-initialization does not create new variables, it keeps the old ones 00591 # instead. This ensures compatibility with code that does 00592 # "from tools.targets import TARGET_NAMES" 00593 update_target_data() 00594
Generated on Tue Aug 9 2022 00:37:00 by
1.7.2