nkjnm
Dependencies: MAX44000 nexpaq_mdk
Fork of LED_Demo by
mbd_os/tools/targets.py@1:55a6170b404f, 2016-09-17 (annotated)
- Committer:
- nexpaq
- Date:
- Sat Sep 17 16:32:05 2016 +0000
- Revision:
- 1:55a6170b404f
checking in for sharing
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
nexpaq | 1:55a6170b404f | 1 | """ |
nexpaq | 1:55a6170b404f | 2 | mbed SDK |
nexpaq | 1:55a6170b404f | 3 | Copyright (c) 2011-2016 ARM Limited |
nexpaq | 1:55a6170b404f | 4 | |
nexpaq | 1:55a6170b404f | 5 | Licensed under the Apache License, Version 2.0 (the "License"); |
nexpaq | 1:55a6170b404f | 6 | you may not use this file except in compliance with the License. |
nexpaq | 1:55a6170b404f | 7 | You may obtain a copy of the License at |
nexpaq | 1:55a6170b404f | 8 | |
nexpaq | 1:55a6170b404f | 9 | http://www.apache.org/licenses/LICENSE-2.0 |
nexpaq | 1:55a6170b404f | 10 | |
nexpaq | 1:55a6170b404f | 11 | Unless required by applicable law or agreed to in writing, software |
nexpaq | 1:55a6170b404f | 12 | distributed under the License is distributed on an "AS IS" BASIS, |
nexpaq | 1:55a6170b404f | 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
nexpaq | 1:55a6170b404f | 14 | See the License for the specific language governing permissions and |
nexpaq | 1:55a6170b404f | 15 | limitations under the License. |
nexpaq | 1:55a6170b404f | 16 | """ |
nexpaq | 1:55a6170b404f | 17 | |
nexpaq | 1:55a6170b404f | 18 | import os |
nexpaq | 1:55a6170b404f | 19 | import binascii |
nexpaq | 1:55a6170b404f | 20 | import struct |
nexpaq | 1:55a6170b404f | 21 | import shutil |
nexpaq | 1:55a6170b404f | 22 | import inspect |
nexpaq | 1:55a6170b404f | 23 | import sys |
nexpaq | 1:55a6170b404f | 24 | from tools.patch import patch |
nexpaq | 1:55a6170b404f | 25 | from tools.paths import TOOLS_BOOTLOADERS |
nexpaq | 1:55a6170b404f | 26 | from tools.utils import json_file_to_dict |
nexpaq | 1:55a6170b404f | 27 | |
nexpaq | 1:55a6170b404f | 28 | CORE_LABELS = { |
nexpaq | 1:55a6170b404f | 29 | "ARM7TDMI-S": ["ARM7", "LIKE_CORTEX_ARM7"], |
nexpaq | 1:55a6170b404f | 30 | "Cortex-M0" : ["M0", "CORTEX_M", "LIKE_CORTEX_M0"], |
nexpaq | 1:55a6170b404f | 31 | "Cortex-M0+": ["M0P", "CORTEX_M", "LIKE_CORTEX_M0"], |
nexpaq | 1:55a6170b404f | 32 | "Cortex-M1" : ["M1", "CORTEX_M", "LIKE_CORTEX_M1"], |
nexpaq | 1:55a6170b404f | 33 | "Cortex-M3" : ["M3", "CORTEX_M", "LIKE_CORTEX_M3"], |
nexpaq | 1:55a6170b404f | 34 | "Cortex-M4" : ["M4", "CORTEX_M", "RTOS_M4_M7", "LIKE_CORTEX_M4"], |
nexpaq | 1:55a6170b404f | 35 | "Cortex-M4F" : ["M4", "CORTEX_M", "RTOS_M4_M7", "LIKE_CORTEX_M4"], |
nexpaq | 1:55a6170b404f | 36 | "Cortex-M7" : ["M7", "CORTEX_M", "RTOS_M4_M7", "LIKE_CORTEX_M7"], |
nexpaq | 1:55a6170b404f | 37 | "Cortex-M7F" : ["M7", "CORTEX_M", "RTOS_M4_M7", "LIKE_CORTEX_M7"], |
nexpaq | 1:55a6170b404f | 38 | "Cortex-M7FD" : ["M7", "CORTEX_M", "RTOS_M4_M7", "LIKE_CORTEX_M7"], |
nexpaq | 1:55a6170b404f | 39 | "Cortex-A9" : ["A9", "CORTEX_A", "LIKE_CORTEX_A9"] |
nexpaq | 1:55a6170b404f | 40 | } |
nexpaq | 1:55a6170b404f | 41 | |
nexpaq | 1:55a6170b404f | 42 | ################################################################################ |
nexpaq | 1:55a6170b404f | 43 | # Generic Target class that reads and interprets the data in targets.json |
nexpaq | 1:55a6170b404f | 44 | |
nexpaq | 1:55a6170b404f | 45 | class HookError(Exception): |
nexpaq | 1:55a6170b404f | 46 | """ A simple class that represents all the exceptions associated with |
nexpaq | 1:55a6170b404f | 47 | hooking |
nexpaq | 1:55a6170b404f | 48 | """ |
nexpaq | 1:55a6170b404f | 49 | pass |
nexpaq | 1:55a6170b404f | 50 | |
nexpaq | 1:55a6170b404f | 51 | CACHES = {} |
nexpaq | 1:55a6170b404f | 52 | def cached(func): |
nexpaq | 1:55a6170b404f | 53 | """A simple decorator used for automatically caching data returned by a |
nexpaq | 1:55a6170b404f | 54 | function |
nexpaq | 1:55a6170b404f | 55 | """ |
nexpaq | 1:55a6170b404f | 56 | def wrapper(*args, **kwargs): |
nexpaq | 1:55a6170b404f | 57 | """The wrapped function itself""" |
nexpaq | 1:55a6170b404f | 58 | if not CACHES.has_key((func.__name__, args)): |
nexpaq | 1:55a6170b404f | 59 | CACHES[(func.__name__, args)] = func(*args, **kwargs) |
nexpaq | 1:55a6170b404f | 60 | return CACHES[(func.__name__, args)] |
nexpaq | 1:55a6170b404f | 61 | return wrapper |
nexpaq | 1:55a6170b404f | 62 | |
nexpaq | 1:55a6170b404f | 63 | class Target(object): |
nexpaq | 1:55a6170b404f | 64 | """An object to represent a Target (MCU/Board)""" |
nexpaq | 1:55a6170b404f | 65 | # Cumulative attributes can have values appended to them, so they |
nexpaq | 1:55a6170b404f | 66 | # need to be computed differently than regular attributes |
nexpaq | 1:55a6170b404f | 67 | cumulative_attributes = ['extra_labels', 'macros', 'device_has', 'features'] |
nexpaq | 1:55a6170b404f | 68 | |
nexpaq | 1:55a6170b404f | 69 | # List of targets that were added dynamically using "add_py_targets" (see |
nexpaq | 1:55a6170b404f | 70 | # below) |
nexpaq | 1:55a6170b404f | 71 | __py_targets = set() |
nexpaq | 1:55a6170b404f | 72 | |
nexpaq | 1:55a6170b404f | 73 | # Default location of the 'targets.json' file |
nexpaq | 1:55a6170b404f | 74 | __targets_json_location_default = os.path.join( |
nexpaq | 1:55a6170b404f | 75 | os.path.dirname(os.path.abspath(__file__)), '..', 'hal', 'targets.json') |
nexpaq | 1:55a6170b404f | 76 | |
nexpaq | 1:55a6170b404f | 77 | # Current/new location of the 'targets.json' file |
nexpaq | 1:55a6170b404f | 78 | __targets_json_location = None |
nexpaq | 1:55a6170b404f | 79 | |
nexpaq | 1:55a6170b404f | 80 | @staticmethod |
nexpaq | 1:55a6170b404f | 81 | @cached |
nexpaq | 1:55a6170b404f | 82 | def get_json_target_data(): |
nexpaq | 1:55a6170b404f | 83 | """Load the description of JSON target data""" |
nexpaq | 1:55a6170b404f | 84 | return json_file_to_dict(Target.__targets_json_location or |
nexpaq | 1:55a6170b404f | 85 | Target.__targets_json_location_default) |
nexpaq | 1:55a6170b404f | 86 | |
nexpaq | 1:55a6170b404f | 87 | @staticmethod |
nexpaq | 1:55a6170b404f | 88 | def set_targets_json_location(location=None): |
nexpaq | 1:55a6170b404f | 89 | """Set the location of the targets.json file""" |
nexpaq | 1:55a6170b404f | 90 | Target.__targets_json_location = (location or |
nexpaq | 1:55a6170b404f | 91 | Target.__targets_json_location_default) |
nexpaq | 1:55a6170b404f | 92 | # Invalidate caches, since the location of the JSON file changed |
nexpaq | 1:55a6170b404f | 93 | CACHES.clear() |
nexpaq | 1:55a6170b404f | 94 | |
nexpaq | 1:55a6170b404f | 95 | @staticmethod |
nexpaq | 1:55a6170b404f | 96 | @cached |
nexpaq | 1:55a6170b404f | 97 | def get_module_data(): |
nexpaq | 1:55a6170b404f | 98 | """Get the members of this module using Python's "inspect" module""" |
nexpaq | 1:55a6170b404f | 99 | return dict([(m[0], m[1]) for m in |
nexpaq | 1:55a6170b404f | 100 | inspect.getmembers(sys.modules[__name__])]) |
nexpaq | 1:55a6170b404f | 101 | |
nexpaq | 1:55a6170b404f | 102 | def __get_resolution_order(self, target_name, order, level=0): |
nexpaq | 1:55a6170b404f | 103 | """ Return the order in which target descriptions are searched for |
nexpaq | 1:55a6170b404f | 104 | attributes. This mimics the Python 2.2 method resolution order, which |
nexpaq | 1:55a6170b404f | 105 | is what the old targets.py module used. For more details, check |
nexpaq | 1:55a6170b404f | 106 | http://makina-corpus.com/blog/metier/2014/python-tutorial-understanding-python-mro-class-search-path |
nexpaq | 1:55a6170b404f | 107 | The resolution order contains (name, level) tuples, where "name" is the |
nexpaq | 1:55a6170b404f | 108 | name of the class and "level" is the level in the inheritance hierarchy |
nexpaq | 1:55a6170b404f | 109 | (the target itself is at level 0, its first parent at level 1, its |
nexpaq | 1:55a6170b404f | 110 | parent's parent at level 2 and so on) |
nexpaq | 1:55a6170b404f | 111 | """ |
nexpaq | 1:55a6170b404f | 112 | # the resolution order can't contain duplicate target names |
nexpaq | 1:55a6170b404f | 113 | if target_name not in [l[0] for l in order]: |
nexpaq | 1:55a6170b404f | 114 | order.append((target_name, level)) |
nexpaq | 1:55a6170b404f | 115 | parents = self.get_json_target_data()[target_name].get("inherits", []) |
nexpaq | 1:55a6170b404f | 116 | for par in parents: |
nexpaq | 1:55a6170b404f | 117 | order = self.__get_resolution_order(par, order, level + 1) |
nexpaq | 1:55a6170b404f | 118 | return order |
nexpaq | 1:55a6170b404f | 119 | |
nexpaq | 1:55a6170b404f | 120 | @staticmethod |
nexpaq | 1:55a6170b404f | 121 | def __add_paths_to_progen(data): |
nexpaq | 1:55a6170b404f | 122 | """Modify the exporter specification ("progen") by changing all |
nexpaq | 1:55a6170b404f | 123 | "template" keys to full paths |
nexpaq | 1:55a6170b404f | 124 | """ |
nexpaq | 1:55a6170b404f | 125 | out = {} |
nexpaq | 1:55a6170b404f | 126 | for key, val in data.items(): |
nexpaq | 1:55a6170b404f | 127 | if isinstance(val, dict): |
nexpaq | 1:55a6170b404f | 128 | out[key] = Target.__add_paths_to_progen(val) |
nexpaq | 1:55a6170b404f | 129 | elif key == "template": |
nexpaq | 1:55a6170b404f | 130 | out[key] = [os.path.join(os.path.dirname(__file__), 'export', v) |
nexpaq | 1:55a6170b404f | 131 | for v in val] |
nexpaq | 1:55a6170b404f | 132 | else: |
nexpaq | 1:55a6170b404f | 133 | out[key] = val |
nexpaq | 1:55a6170b404f | 134 | return out |
nexpaq | 1:55a6170b404f | 135 | |
nexpaq | 1:55a6170b404f | 136 | def __getattr_cumulative(self, attrname): |
nexpaq | 1:55a6170b404f | 137 | """Look for the attribute in the class and its parents, as defined by |
nexpaq | 1:55a6170b404f | 138 | the resolution order |
nexpaq | 1:55a6170b404f | 139 | """ |
nexpaq | 1:55a6170b404f | 140 | tdata = self.get_json_target_data() |
nexpaq | 1:55a6170b404f | 141 | # For a cumulative attribute, figure out when it was defined the |
nexpaq | 1:55a6170b404f | 142 | # last time (in attribute resolution order) then follow the "_add" |
nexpaq | 1:55a6170b404f | 143 | # and "_remove" data fields |
nexpaq | 1:55a6170b404f | 144 | for idx, target in enumerate(self.resolution_order): |
nexpaq | 1:55a6170b404f | 145 | # the attribute was defined at this level in the resolution |
nexpaq | 1:55a6170b404f | 146 | # order |
nexpaq | 1:55a6170b404f | 147 | if attrname in tdata[target[0]]: |
nexpaq | 1:55a6170b404f | 148 | def_idx = idx |
nexpaq | 1:55a6170b404f | 149 | break |
nexpaq | 1:55a6170b404f | 150 | else: |
nexpaq | 1:55a6170b404f | 151 | raise AttributeError("Attribute '%s' not found in target '%s'" |
nexpaq | 1:55a6170b404f | 152 | % (attrname, self.name)) |
nexpaq | 1:55a6170b404f | 153 | # Get the starting value of the attribute |
nexpaq | 1:55a6170b404f | 154 | starting_value = (tdata[self.resolution_order[def_idx][0]][attrname] |
nexpaq | 1:55a6170b404f | 155 | or [])[:] |
nexpaq | 1:55a6170b404f | 156 | # Traverse the resolution list in high inheritance to low |
nexpaq | 1:55a6170b404f | 157 | # inheritance level, left to right order to figure out all the |
nexpaq | 1:55a6170b404f | 158 | # other classes that change the definition by adding or removing |
nexpaq | 1:55a6170b404f | 159 | # elements |
nexpaq | 1:55a6170b404f | 160 | for idx in xrange(self.resolution_order[def_idx][1] - 1, -1, -1): |
nexpaq | 1:55a6170b404f | 161 | same_level_targets = [tar[0] for tar in self.resolution_order |
nexpaq | 1:55a6170b404f | 162 | if tar[1] == idx] |
nexpaq | 1:55a6170b404f | 163 | for tar in same_level_targets: |
nexpaq | 1:55a6170b404f | 164 | data = tdata[tar] |
nexpaq | 1:55a6170b404f | 165 | # Do we have anything to add ? |
nexpaq | 1:55a6170b404f | 166 | if data.has_key(attrname + "_add"): |
nexpaq | 1:55a6170b404f | 167 | starting_value.extend(data[attrname + "_add"]) |
nexpaq | 1:55a6170b404f | 168 | # Do we have anything to remove ? |
nexpaq | 1:55a6170b404f | 169 | if data.has_key(attrname + "_remove"): |
nexpaq | 1:55a6170b404f | 170 | # Macros can be defined either without a value (MACRO) |
nexpaq | 1:55a6170b404f | 171 | # or with a value (MACRO=10). When removing, we specify |
nexpaq | 1:55a6170b404f | 172 | # only the name of the macro, without the value. So we |
nexpaq | 1:55a6170b404f | 173 | # need to create a mapping between the macro name and |
nexpaq | 1:55a6170b404f | 174 | # its value. This will work for extra_labels and other |
nexpaq | 1:55a6170b404f | 175 | # type of arrays as well, since they fall into the |
nexpaq | 1:55a6170b404f | 176 | # "macros without a value" category (simple definitions |
nexpaq | 1:55a6170b404f | 177 | # without a value). |
nexpaq | 1:55a6170b404f | 178 | name_def_map = {} |
nexpaq | 1:55a6170b404f | 179 | for crtv in starting_value: |
nexpaq | 1:55a6170b404f | 180 | if crtv.find('=') != -1: |
nexpaq | 1:55a6170b404f | 181 | temp = crtv.split('=') |
nexpaq | 1:55a6170b404f | 182 | if len(temp) != 2: |
nexpaq | 1:55a6170b404f | 183 | raise ValueError( |
nexpaq | 1:55a6170b404f | 184 | "Invalid macro definition '%s'" % crtv) |
nexpaq | 1:55a6170b404f | 185 | name_def_map[temp[0]] = crtv |
nexpaq | 1:55a6170b404f | 186 | else: |
nexpaq | 1:55a6170b404f | 187 | name_def_map[crtv] = crtv |
nexpaq | 1:55a6170b404f | 188 | for element in data[attrname + "_remove"]: |
nexpaq | 1:55a6170b404f | 189 | if element not in name_def_map: |
nexpaq | 1:55a6170b404f | 190 | raise ValueError( |
nexpaq | 1:55a6170b404f | 191 | ("Unable to remove '%s' in '%s.%s' since " |
nexpaq | 1:55a6170b404f | 192 | % (element, self.name, attrname)) + |
nexpaq | 1:55a6170b404f | 193 | "it doesn't exist") |
nexpaq | 1:55a6170b404f | 194 | starting_value.remove(name_def_map[element]) |
nexpaq | 1:55a6170b404f | 195 | return starting_value |
nexpaq | 1:55a6170b404f | 196 | |
nexpaq | 1:55a6170b404f | 197 | def __getattr_helper(self, attrname): |
nexpaq | 1:55a6170b404f | 198 | """Compute the value of a given target attribute""" |
nexpaq | 1:55a6170b404f | 199 | if attrname in self.cumulative_attributes: |
nexpaq | 1:55a6170b404f | 200 | return self.__getattr_cumulative(attrname) |
nexpaq | 1:55a6170b404f | 201 | else: |
nexpaq | 1:55a6170b404f | 202 | tdata = self.get_json_target_data() |
nexpaq | 1:55a6170b404f | 203 | starting_value = None |
nexpaq | 1:55a6170b404f | 204 | for target in self.resolution_order: |
nexpaq | 1:55a6170b404f | 205 | data = tdata[target[0]] |
nexpaq | 1:55a6170b404f | 206 | if data.has_key(attrname): |
nexpaq | 1:55a6170b404f | 207 | starting_value = data[attrname] |
nexpaq | 1:55a6170b404f | 208 | break |
nexpaq | 1:55a6170b404f | 209 | else: # Attribute not found |
nexpaq | 1:55a6170b404f | 210 | raise AttributeError( |
nexpaq | 1:55a6170b404f | 211 | "Attribute '%s' not found in target '%s'" |
nexpaq | 1:55a6170b404f | 212 | % (attrname, self.name)) |
nexpaq | 1:55a6170b404f | 213 | # 'progen' needs the full path to the template (the path in JSON is |
nexpaq | 1:55a6170b404f | 214 | # relative to tools/export) |
nexpaq | 1:55a6170b404f | 215 | if attrname == "progen": |
nexpaq | 1:55a6170b404f | 216 | return self.__add_paths_to_progen(starting_value) |
nexpaq | 1:55a6170b404f | 217 | else: |
nexpaq | 1:55a6170b404f | 218 | return starting_value |
nexpaq | 1:55a6170b404f | 219 | |
nexpaq | 1:55a6170b404f | 220 | def __getattr__(self, attrname): |
nexpaq | 1:55a6170b404f | 221 | """ Return the value of an attribute. This function only computes the |
nexpaq | 1:55a6170b404f | 222 | attribute's value once, then adds it to the instance attributes (in |
nexpaq | 1:55a6170b404f | 223 | __dict__), so the next time it is returned directly |
nexpaq | 1:55a6170b404f | 224 | """ |
nexpaq | 1:55a6170b404f | 225 | result = self.__getattr_helper(attrname) |
nexpaq | 1:55a6170b404f | 226 | self.__dict__[attrname] = result |
nexpaq | 1:55a6170b404f | 227 | return result |
nexpaq | 1:55a6170b404f | 228 | |
nexpaq | 1:55a6170b404f | 229 | @staticmethod |
nexpaq | 1:55a6170b404f | 230 | def add_py_targets(new_targets): |
nexpaq | 1:55a6170b404f | 231 | """Add one or more new target(s) represented as a Python dictionary |
nexpaq | 1:55a6170b404f | 232 | in 'new_targets'. It is an error to add a target with a name that |
nexpaq | 1:55a6170b404f | 233 | already exists. |
nexpaq | 1:55a6170b404f | 234 | """ |
nexpaq | 1:55a6170b404f | 235 | crt_data = Target.get_json_target_data() |
nexpaq | 1:55a6170b404f | 236 | for target_key, target_value in new_targets.items(): |
nexpaq | 1:55a6170b404f | 237 | if crt_data.has_key(target_key): |
nexpaq | 1:55a6170b404f | 238 | raise Exception( |
nexpaq | 1:55a6170b404f | 239 | "Attempt to add target '%s' that already exists" |
nexpaq | 1:55a6170b404f | 240 | % target_key) |
nexpaq | 1:55a6170b404f | 241 | # Add target data to the internal target dictionary |
nexpaq | 1:55a6170b404f | 242 | crt_data[target_key] = target_value |
nexpaq | 1:55a6170b404f | 243 | # Create the new target and add it to the relevant data structures |
nexpaq | 1:55a6170b404f | 244 | new_target = Target(target_key) |
nexpaq | 1:55a6170b404f | 245 | TARGETS.append(new_target) |
nexpaq | 1:55a6170b404f | 246 | TARGET_MAP[target_key] = new_target |
nexpaq | 1:55a6170b404f | 247 | TARGET_NAMES.append(target_key) |
nexpaq | 1:55a6170b404f | 248 | |
nexpaq | 1:55a6170b404f | 249 | @staticmethod |
nexpaq | 1:55a6170b404f | 250 | @cached |
nexpaq | 1:55a6170b404f | 251 | def get_target(target_name): |
nexpaq | 1:55a6170b404f | 252 | """ Return the target instance starting from the target name """ |
nexpaq | 1:55a6170b404f | 253 | return Target(target_name) |
nexpaq | 1:55a6170b404f | 254 | |
nexpaq | 1:55a6170b404f | 255 | def __init__(self, target_name): |
nexpaq | 1:55a6170b404f | 256 | self.name = target_name |
nexpaq | 1:55a6170b404f | 257 | |
nexpaq | 1:55a6170b404f | 258 | # Compute resolution order once (it will be used later in __getattr__) |
nexpaq | 1:55a6170b404f | 259 | self.resolution_order = self.__get_resolution_order(self.name, []) |
nexpaq | 1:55a6170b404f | 260 | # Create also a list with only the names of the targets in the |
nexpaq | 1:55a6170b404f | 261 | # resolution order |
nexpaq | 1:55a6170b404f | 262 | self.resolution_order_names = [target[0] for target |
nexpaq | 1:55a6170b404f | 263 | in self.resolution_order] |
nexpaq | 1:55a6170b404f | 264 | |
nexpaq | 1:55a6170b404f | 265 | @property |
nexpaq | 1:55a6170b404f | 266 | def program_cycle_s(self): |
nexpaq | 1:55a6170b404f | 267 | """Special override for program_cycle_s as it's default value depends |
nexpaq | 1:55a6170b404f | 268 | upon is_disk_virtual |
nexpaq | 1:55a6170b404f | 269 | """ |
nexpaq | 1:55a6170b404f | 270 | try: |
nexpaq | 1:55a6170b404f | 271 | return self.__getattr__("program_cycle_s") |
nexpaq | 1:55a6170b404f | 272 | except AttributeError: |
nexpaq | 1:55a6170b404f | 273 | return 4 if self.is_disk_virtual else 1.5 |
nexpaq | 1:55a6170b404f | 274 | |
nexpaq | 1:55a6170b404f | 275 | def get_labels(self): |
nexpaq | 1:55a6170b404f | 276 | """Get all possible labels for this target""" |
nexpaq | 1:55a6170b404f | 277 | labels = [self.name] + CORE_LABELS[self.core] + self.extra_labels |
nexpaq | 1:55a6170b404f | 278 | # Automatically define UVISOR_UNSUPPORTED if the target doesn't |
nexpaq | 1:55a6170b404f | 279 | # specifically define UVISOR_SUPPORTED |
nexpaq | 1:55a6170b404f | 280 | if "UVISOR_SUPPORTED" not in labels: |
nexpaq | 1:55a6170b404f | 281 | labels.append("UVISOR_UNSUPPORTED") |
nexpaq | 1:55a6170b404f | 282 | return labels |
nexpaq | 1:55a6170b404f | 283 | |
nexpaq | 1:55a6170b404f | 284 | def init_hooks(self, hook, toolchain_name): |
nexpaq | 1:55a6170b404f | 285 | """Initialize the post-build hooks for a toolchain. For now, this |
nexpaq | 1:55a6170b404f | 286 | function only allows "post binary" hooks (hooks that are executed |
nexpaq | 1:55a6170b404f | 287 | after the binary image is extracted from the executable file) |
nexpaq | 1:55a6170b404f | 288 | """ |
nexpaq | 1:55a6170b404f | 289 | |
nexpaq | 1:55a6170b404f | 290 | # If there's no hook, simply return |
nexpaq | 1:55a6170b404f | 291 | try: |
nexpaq | 1:55a6170b404f | 292 | hook_data = self.post_binary_hook |
nexpaq | 1:55a6170b404f | 293 | except AttributeError: |
nexpaq | 1:55a6170b404f | 294 | return |
nexpaq | 1:55a6170b404f | 295 | # A hook was found. The hook's name is in the format |
nexpaq | 1:55a6170b404f | 296 | # "classname.functionname" |
nexpaq | 1:55a6170b404f | 297 | temp = hook_data["function"].split(".") |
nexpaq | 1:55a6170b404f | 298 | if len(temp) != 2: |
nexpaq | 1:55a6170b404f | 299 | raise HookError( |
nexpaq | 1:55a6170b404f | 300 | ("Invalid format for hook '%s' in target '%s'" |
nexpaq | 1:55a6170b404f | 301 | % (hook_data["function"], self.name)) + |
nexpaq | 1:55a6170b404f | 302 | " (must be 'class_name.function_name')") |
nexpaq | 1:55a6170b404f | 303 | class_name, function_name = temp[0], temp[1] |
nexpaq | 1:55a6170b404f | 304 | # "class_name" must refer to a class in this file, so check if the |
nexpaq | 1:55a6170b404f | 305 | # class exists |
nexpaq | 1:55a6170b404f | 306 | mdata = self.get_module_data() |
nexpaq | 1:55a6170b404f | 307 | if not mdata.has_key(class_name) or \ |
nexpaq | 1:55a6170b404f | 308 | not inspect.isclass(mdata[class_name]): |
nexpaq | 1:55a6170b404f | 309 | raise HookError( |
nexpaq | 1:55a6170b404f | 310 | ("Class '%s' required by '%s' in target '%s'" |
nexpaq | 1:55a6170b404f | 311 | % (class_name, hook_data["function"], self.name)) + |
nexpaq | 1:55a6170b404f | 312 | " not found in targets.py") |
nexpaq | 1:55a6170b404f | 313 | # "function_name" must refer to a static function inside class |
nexpaq | 1:55a6170b404f | 314 | # "class_name" |
nexpaq | 1:55a6170b404f | 315 | cls = mdata[class_name] |
nexpaq | 1:55a6170b404f | 316 | if (not hasattr(cls, function_name)) or \ |
nexpaq | 1:55a6170b404f | 317 | (not inspect.isfunction(getattr(cls, function_name))): |
nexpaq | 1:55a6170b404f | 318 | raise HookError( |
nexpaq | 1:55a6170b404f | 319 | ("Static function '%s' " % function_name) + |
nexpaq | 1:55a6170b404f | 320 | ("required by '%s' " % hook_data["function"]) + |
nexpaq | 1:55a6170b404f | 321 | ("in target '%s' " % self.name) + |
nexpaq | 1:55a6170b404f | 322 | ("not found in class '%s'" % class_name)) |
nexpaq | 1:55a6170b404f | 323 | # Check if the hook specification also has target restrictions |
nexpaq | 1:55a6170b404f | 324 | toolchain_restrictions = hook_data.get("toolchains", []) |
nexpaq | 1:55a6170b404f | 325 | if toolchain_restrictions and \ |
nexpaq | 1:55a6170b404f | 326 | (toolchain_name not in toolchain_restrictions): |
nexpaq | 1:55a6170b404f | 327 | return |
nexpaq | 1:55a6170b404f | 328 | # Finally, hook the requested function |
nexpaq | 1:55a6170b404f | 329 | hook.hook_add_binary("post", getattr(cls, function_name)) |
nexpaq | 1:55a6170b404f | 330 | |
nexpaq | 1:55a6170b404f | 331 | ################################################################################ |
nexpaq | 1:55a6170b404f | 332 | # Target specific code goes in this section |
nexpaq | 1:55a6170b404f | 333 | # This code can be invoked from the target description using the |
nexpaq | 1:55a6170b404f | 334 | # "post_binary_hook" key |
nexpaq | 1:55a6170b404f | 335 | |
nexpaq | 1:55a6170b404f | 336 | class LPCTargetCode(object): |
nexpaq | 1:55a6170b404f | 337 | """General LPC Target patching code""" |
nexpaq | 1:55a6170b404f | 338 | @staticmethod |
nexpaq | 1:55a6170b404f | 339 | def lpc_patch(t_self, resources, elf, binf): |
nexpaq | 1:55a6170b404f | 340 | """Patch an elf file""" |
nexpaq | 1:55a6170b404f | 341 | t_self.debug("LPC Patch: %s" % os.path.split(binf)[1]) |
nexpaq | 1:55a6170b404f | 342 | patch(binf) |
nexpaq | 1:55a6170b404f | 343 | |
nexpaq | 1:55a6170b404f | 344 | class LPC4088Code(object): |
nexpaq | 1:55a6170b404f | 345 | """Code specific to the LPC4088""" |
nexpaq | 1:55a6170b404f | 346 | @staticmethod |
nexpaq | 1:55a6170b404f | 347 | def binary_hook(t_self, resources, elf, binf): |
nexpaq | 1:55a6170b404f | 348 | """Hook to be run after an elf file is built""" |
nexpaq | 1:55a6170b404f | 349 | if not os.path.isdir(binf): |
nexpaq | 1:55a6170b404f | 350 | # Regular binary file, nothing to do |
nexpaq | 1:55a6170b404f | 351 | LPCTargetCode.lpc_patch(t_self, resources, elf, binf) |
nexpaq | 1:55a6170b404f | 352 | return |
nexpaq | 1:55a6170b404f | 353 | outbin = open(binf + ".temp", "wb") |
nexpaq | 1:55a6170b404f | 354 | partf = open(os.path.join(binf, "ER_IROM1"), "rb") |
nexpaq | 1:55a6170b404f | 355 | # Pad the fist part (internal flash) with 0xFF to 512k |
nexpaq | 1:55a6170b404f | 356 | data = partf.read() |
nexpaq | 1:55a6170b404f | 357 | outbin.write(data) |
nexpaq | 1:55a6170b404f | 358 | outbin.write('\xFF' * (512*1024 - len(data))) |
nexpaq | 1:55a6170b404f | 359 | partf.close() |
nexpaq | 1:55a6170b404f | 360 | # Read and append the second part (external flash) in chunks of fixed |
nexpaq | 1:55a6170b404f | 361 | # size |
nexpaq | 1:55a6170b404f | 362 | chunksize = 128 * 1024 |
nexpaq | 1:55a6170b404f | 363 | partf = open(os.path.join(binf, "ER_IROM2"), "rb") |
nexpaq | 1:55a6170b404f | 364 | while True: |
nexpaq | 1:55a6170b404f | 365 | data = partf.read(chunksize) |
nexpaq | 1:55a6170b404f | 366 | outbin.write(data) |
nexpaq | 1:55a6170b404f | 367 | if len(data) < chunksize: |
nexpaq | 1:55a6170b404f | 368 | break |
nexpaq | 1:55a6170b404f | 369 | partf.close() |
nexpaq | 1:55a6170b404f | 370 | outbin.close() |
nexpaq | 1:55a6170b404f | 371 | # Remove the directory with the binary parts and rename the temporary |
nexpaq | 1:55a6170b404f | 372 | # file to 'binf' |
nexpaq | 1:55a6170b404f | 373 | shutil.rmtree(binf, True) |
nexpaq | 1:55a6170b404f | 374 | os.rename(binf + '.temp', binf) |
nexpaq | 1:55a6170b404f | 375 | t_self.debug("Generated custom binary file (internal flash + SPIFI)") |
nexpaq | 1:55a6170b404f | 376 | LPCTargetCode.lpc_patch(t_self, resources, elf, binf) |
nexpaq | 1:55a6170b404f | 377 | |
nexpaq | 1:55a6170b404f | 378 | class TEENSY3_1Code(object): |
nexpaq | 1:55a6170b404f | 379 | """Hooks for the TEENSY3.1""" |
nexpaq | 1:55a6170b404f | 380 | @staticmethod |
nexpaq | 1:55a6170b404f | 381 | def binary_hook(t_self, resources, elf, binf): |
nexpaq | 1:55a6170b404f | 382 | """Hook that is run after elf is generated""" |
nexpaq | 1:55a6170b404f | 383 | from intelhex import IntelHex |
nexpaq | 1:55a6170b404f | 384 | binh = IntelHex() |
nexpaq | 1:55a6170b404f | 385 | binh.loadbin(binf, offset=0) |
nexpaq | 1:55a6170b404f | 386 | |
nexpaq | 1:55a6170b404f | 387 | with open(binf.replace(".bin", ".hex"), "w") as file_desc: |
nexpaq | 1:55a6170b404f | 388 | binh.tofile(file_desc, format='hex') |
nexpaq | 1:55a6170b404f | 389 | |
nexpaq | 1:55a6170b404f | 390 | class MTSCode(object): |
nexpaq | 1:55a6170b404f | 391 | """Generic MTS code""" |
nexpaq | 1:55a6170b404f | 392 | @staticmethod |
nexpaq | 1:55a6170b404f | 393 | def _combine_bins_helper(target_name, binf): |
nexpaq | 1:55a6170b404f | 394 | """combine bins with the bootloader for a particular target""" |
nexpaq | 1:55a6170b404f | 395 | loader = os.path.join(TOOLS_BOOTLOADERS, target_name, "bootloader.bin") |
nexpaq | 1:55a6170b404f | 396 | target = binf + ".tmp" |
nexpaq | 1:55a6170b404f | 397 | if not os.path.exists(loader): |
nexpaq | 1:55a6170b404f | 398 | print "Can't find bootloader binary: " + loader |
nexpaq | 1:55a6170b404f | 399 | return |
nexpaq | 1:55a6170b404f | 400 | outbin = open(target, 'w+b') |
nexpaq | 1:55a6170b404f | 401 | part = open(loader, 'rb') |
nexpaq | 1:55a6170b404f | 402 | data = part.read() |
nexpaq | 1:55a6170b404f | 403 | outbin.write(data) |
nexpaq | 1:55a6170b404f | 404 | outbin.write('\xFF' * (64*1024 - len(data))) |
nexpaq | 1:55a6170b404f | 405 | part.close() |
nexpaq | 1:55a6170b404f | 406 | part = open(binf, 'rb') |
nexpaq | 1:55a6170b404f | 407 | data = part.read() |
nexpaq | 1:55a6170b404f | 408 | outbin.write(data) |
nexpaq | 1:55a6170b404f | 409 | part.close() |
nexpaq | 1:55a6170b404f | 410 | outbin.seek(0, 0) |
nexpaq | 1:55a6170b404f | 411 | data = outbin.read() |
nexpaq | 1:55a6170b404f | 412 | outbin.seek(0, 1) |
nexpaq | 1:55a6170b404f | 413 | crc = struct.pack('<I', binascii.crc32(data) & 0xFFFFFFFF) |
nexpaq | 1:55a6170b404f | 414 | outbin.write(crc) |
nexpaq | 1:55a6170b404f | 415 | outbin.close() |
nexpaq | 1:55a6170b404f | 416 | os.remove(binf) |
nexpaq | 1:55a6170b404f | 417 | os.rename(target, binf) |
nexpaq | 1:55a6170b404f | 418 | |
nexpaq | 1:55a6170b404f | 419 | @staticmethod |
nexpaq | 1:55a6170b404f | 420 | def combine_bins_mts_dot(t_self, resources, elf, binf): |
nexpaq | 1:55a6170b404f | 421 | """A hook for the MTS MDOT""" |
nexpaq | 1:55a6170b404f | 422 | MTSCode._combine_bins_helper("MTS_MDOT_F411RE", binf) |
nexpaq | 1:55a6170b404f | 423 | |
nexpaq | 1:55a6170b404f | 424 | @staticmethod |
nexpaq | 1:55a6170b404f | 425 | def combine_bins_mts_dragonfly(t_self, resources, elf, binf): |
nexpaq | 1:55a6170b404f | 426 | """A hoof for the MTS Dragonfly""" |
nexpaq | 1:55a6170b404f | 427 | MTSCode._combine_bins_helper("MTS_DRAGONFLY_F411RE", binf) |
nexpaq | 1:55a6170b404f | 428 | |
nexpaq | 1:55a6170b404f | 429 | class MCU_NRF51Code(object): |
nexpaq | 1:55a6170b404f | 430 | """NRF51 Hooks""" |
nexpaq | 1:55a6170b404f | 431 | @staticmethod |
nexpaq | 1:55a6170b404f | 432 | def binary_hook(t_self, resources, _, binf): |
nexpaq | 1:55a6170b404f | 433 | """Hook that merges the soft device with the bin file""" |
nexpaq | 1:55a6170b404f | 434 | # Scan to find the actual paths of soft device |
nexpaq | 1:55a6170b404f | 435 | sdf = None |
nexpaq | 1:55a6170b404f | 436 | for softdevice_and_offset_entry\ |
nexpaq | 1:55a6170b404f | 437 | in t_self.target.EXPECTED_SOFTDEVICES_WITH_OFFSETS: |
nexpaq | 1:55a6170b404f | 438 | for hexf in resources.hex_files: |
nexpaq | 1:55a6170b404f | 439 | if hexf.find(softdevice_and_offset_entry['name']) != -1: |
nexpaq | 1:55a6170b404f | 440 | t_self.debug("SoftDevice file found %s." |
nexpaq | 1:55a6170b404f | 441 | % softdevice_and_offset_entry['name']) |
nexpaq | 1:55a6170b404f | 442 | sdf = hexf |
nexpaq | 1:55a6170b404f | 443 | |
nexpaq | 1:55a6170b404f | 444 | if sdf is not None: |
nexpaq | 1:55a6170b404f | 445 | break |
nexpaq | 1:55a6170b404f | 446 | if sdf is not None: |
nexpaq | 1:55a6170b404f | 447 | break |
nexpaq | 1:55a6170b404f | 448 | |
nexpaq | 1:55a6170b404f | 449 | if sdf is None: |
nexpaq | 1:55a6170b404f | 450 | t_self.debug("Hex file not found. Aborting.") |
nexpaq | 1:55a6170b404f | 451 | return |
nexpaq | 1:55a6170b404f | 452 | |
nexpaq | 1:55a6170b404f | 453 | # Look for bootloader file that matches this soft device or bootloader |
nexpaq | 1:55a6170b404f | 454 | # override image |
nexpaq | 1:55a6170b404f | 455 | blf = None |
nexpaq | 1:55a6170b404f | 456 | if t_self.target.MERGE_BOOTLOADER is True: |
nexpaq | 1:55a6170b404f | 457 | for hexf in resources.hex_files: |
nexpaq | 1:55a6170b404f | 458 | if hexf.find(t_self.target.OVERRIDE_BOOTLOADER_FILENAME) != -1: |
nexpaq | 1:55a6170b404f | 459 | t_self.debug("Bootloader file found %s." |
nexpaq | 1:55a6170b404f | 460 | % t_self.target.OVERRIDE_BOOTLOADER_FILENAME) |
nexpaq | 1:55a6170b404f | 461 | blf = hexf |
nexpaq | 1:55a6170b404f | 462 | break |
nexpaq | 1:55a6170b404f | 463 | elif hexf.find(softdevice_and_offset_entry['boot']) != -1: |
nexpaq | 1:55a6170b404f | 464 | t_self.debug("Bootloader file found %s." |
nexpaq | 1:55a6170b404f | 465 | % softdevice_and_offset_entry['boot']) |
nexpaq | 1:55a6170b404f | 466 | blf = hexf |
nexpaq | 1:55a6170b404f | 467 | break |
nexpaq | 1:55a6170b404f | 468 | |
nexpaq | 1:55a6170b404f | 469 | # Merge user code with softdevice |
nexpaq | 1:55a6170b404f | 470 | from intelhex import IntelHex |
nexpaq | 1:55a6170b404f | 471 | binh = IntelHex() |
nexpaq | 1:55a6170b404f | 472 | binh.loadbin(binf, offset=softdevice_and_offset_entry['offset']) |
nexpaq | 1:55a6170b404f | 473 | |
nexpaq | 1:55a6170b404f | 474 | if t_self.target.MERGE_SOFT_DEVICE is True: |
nexpaq | 1:55a6170b404f | 475 | t_self.debug("Merge SoftDevice file %s" |
nexpaq | 1:55a6170b404f | 476 | % softdevice_and_offset_entry['name']) |
nexpaq | 1:55a6170b404f | 477 | sdh = IntelHex(sdf) |
nexpaq | 1:55a6170b404f | 478 | binh.merge(sdh) |
nexpaq | 1:55a6170b404f | 479 | |
nexpaq | 1:55a6170b404f | 480 | if t_self.target.MERGE_BOOTLOADER is True and blf is not None: |
nexpaq | 1:55a6170b404f | 481 | t_self.debug("Merge BootLoader file %s" % blf) |
nexpaq | 1:55a6170b404f | 482 | blh = IntelHex(blf) |
nexpaq | 1:55a6170b404f | 483 | binh.merge(blh) |
nexpaq | 1:55a6170b404f | 484 | |
nexpaq | 1:55a6170b404f | 485 | with open(binf.replace(".bin", ".hex"), "w") as fileout: |
nexpaq | 1:55a6170b404f | 486 | binh.tofile(fileout, format='hex') |
nexpaq | 1:55a6170b404f | 487 | |
nexpaq | 1:55a6170b404f | 488 | class NCS36510TargetCode: |
nexpaq | 1:55a6170b404f | 489 | @staticmethod |
nexpaq | 1:55a6170b404f | 490 | def ncs36510_addfib(t_self, resources, elf, binf): |
nexpaq | 1:55a6170b404f | 491 | from tools.add_fib import add_fib_at_start |
nexpaq | 1:55a6170b404f | 492 | print("binf ", binf) |
nexpaq | 1:55a6170b404f | 493 | add_fib_at_start(binf[:-4]) |
nexpaq | 1:55a6170b404f | 494 | ################################################################################ |
nexpaq | 1:55a6170b404f | 495 | |
nexpaq | 1:55a6170b404f | 496 | # Instantiate all public targets |
nexpaq | 1:55a6170b404f | 497 | TARGETS = [Target.get_target(name) for name, value |
nexpaq | 1:55a6170b404f | 498 | in Target.get_json_target_data().items() |
nexpaq | 1:55a6170b404f | 499 | if value.get("public", True)] |
nexpaq | 1:55a6170b404f | 500 | |
nexpaq | 1:55a6170b404f | 501 | # Map each target name to its unique instance |
nexpaq | 1:55a6170b404f | 502 | TARGET_MAP = dict([(t.name, t) for t in TARGETS]) |
nexpaq | 1:55a6170b404f | 503 | |
nexpaq | 1:55a6170b404f | 504 | TARGET_NAMES = TARGET_MAP.keys() |
nexpaq | 1:55a6170b404f | 505 | |
nexpaq | 1:55a6170b404f | 506 | # Some targets with different name have the same exporters |
nexpaq | 1:55a6170b404f | 507 | EXPORT_MAP = {} |
nexpaq | 1:55a6170b404f | 508 | |
nexpaq | 1:55a6170b404f | 509 | # Detection APIs |
nexpaq | 1:55a6170b404f | 510 | def get_target_detect_codes(): |
nexpaq | 1:55a6170b404f | 511 | """ Returns dictionary mapping detect_code -> platform_name |
nexpaq | 1:55a6170b404f | 512 | """ |
nexpaq | 1:55a6170b404f | 513 | result = {} |
nexpaq | 1:55a6170b404f | 514 | for target in TARGETS: |
nexpaq | 1:55a6170b404f | 515 | for detect_code in target.detect_code: |
nexpaq | 1:55a6170b404f | 516 | result[detect_code] = target.name |
nexpaq | 1:55a6170b404f | 517 | return result |
nexpaq | 1:55a6170b404f | 518 | |
nexpaq | 1:55a6170b404f | 519 | def set_targets_json_location(location=None): |
nexpaq | 1:55a6170b404f | 520 | """Sets the location of the JSON file that contains the targets""" |
nexpaq | 1:55a6170b404f | 521 | # First instruct Target about the new location |
nexpaq | 1:55a6170b404f | 522 | Target.set_targets_json_location(location) |
nexpaq | 1:55a6170b404f | 523 | # Then re-initialize TARGETS, TARGET_MAP and TARGET_NAMES. The |
nexpaq | 1:55a6170b404f | 524 | # re-initialization does not create new variables, it keeps the old ones |
nexpaq | 1:55a6170b404f | 525 | # instead. This ensures compatibility with code that does |
nexpaq | 1:55a6170b404f | 526 | # "from tools.targets import TARGET_NAMES" |
nexpaq | 1:55a6170b404f | 527 | TARGETS[:] = [Target.get_target(target) for target, obj |
nexpaq | 1:55a6170b404f | 528 | in Target.get_json_target_data().items() |
nexpaq | 1:55a6170b404f | 529 | if obj.get("public", True)] |
nexpaq | 1:55a6170b404f | 530 | TARGET_MAP.clear() |
nexpaq | 1:55a6170b404f | 531 | TARGET_MAP.update(dict([(target.name, target) for target in TARGETS])) |
nexpaq | 1:55a6170b404f | 532 | TARGET_NAMES[:] = TARGET_MAP.keys() |