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