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