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