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