1#!/usr/bin/env python 2 3# Copyright JS Foundation and other contributors, http://js.foundation 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16""" 17Generate pins.cpp for a specified target, using target definitions from the 18mbed OS source tree. 19 20It's expecting to be run from the targets/mbedos5 directory. 21""" 22 23from __future__ import print_function 24 25import argparse 26import ast 27 28import sys 29import os 30 31from pycparserext.ext_c_parser import GnuCParser 32from pycparser import parse_file, c_ast 33 34# import mbed tools 35sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'mbed-os')) 36from tools.targets import Target 37 38LICENSE = '''/* Copyright JS Foundation and other contributors, http://js.foundation 39 * 40 * Licensed under the Apache License, Version 2.0 (the \"License\"); 41 * you may not use this file except in compliance with the License. 42 * You may obtain a copy of the License at 43 * 44 * http://www.apache.org/licenses/LICENSE-2.0 45 * 46 * Unless required by applicable law or agreed to in writing, software 47 * distributed under the License is distributed on an \"AS IS\" BASIS 48 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 49 * See the License for the specific language governing permissions and 50 * limitations under the License. 51 * 52 * This file is generated by generate_pins.py. Please do not modify. 53 */ 54 ''' 55 56 57def find_file(root_dir, directories, name): 58 """ 59 Find the first instance of file with name 'name' in the directory tree 60 starting with 'root_dir'. 61 62 Filter out directories that are not in directories, or do not start with 63 TARGET_. 64 65 Since this looks in (essentially )the same directories as the compiler would 66 when compiling mbed OS, we should only find one PinNames.h. 67 """ 68 69 for root, dirs, files in os.walk(root_dir, topdown=True): 70 # modify dirs in place 71 dirs[:] = [directory for directory in dirs if directory in directories or not directory.startswith('TARGET_')] 72 73 if name in files: 74 return os.path.join(root, name) 75 76 77def enumerate_includes(root_dir, directories): 78 """ 79 Walk through the directory tree, starting at root_dir, and enumerate all 80 valid include directories. 81 """ 82 for root, dirs, _ in os.walk(root_dir, topdown=True): 83 # modify dirs in place 84 dirs[:] = [dir_label for dir_label in dirs 85 if dir_label in directories 86 or (not dir_label.startswith('TARGET_') 87 and not dir_label.startswith('TOOLCHAIN_'))] 88 yield root 89 90 91class TypeDeclVisitor(c_ast.NodeVisitor): 92 """ 93 A TypeDecl visitor class that walks the ast and calls a visitor function for every node found. 94 """ 95 def __init__(self, filter_names=None): 96 self.names = filter_names or [] 97 98 def visit(self, node): 99 value = None 100 101 if node.__class__.__name__ == "TypeDecl": 102 value = self.visit_typedecl(node) 103 104 if value is None: 105 for _, child_node in node.children(): 106 value = value or self.visit(child_node) 107 108 return value 109 110 def visit_typedecl(self, node): 111 """ 112 Visit a node. 113 """ 114 if node.declname in self.names: 115 return [pin.name for pin in node.type.values.enumerators] 116 117 118def enumerate_pins(c_source_file, include_dirs, definitions): 119 """ 120 Enumerate pins specified in PinNames.h, by looking for a PinName enum 121 typedef somewhere in the file. 122 """ 123 definitions += ['__attribute(x)__=', '__extension__(x)=', 'register=', '__IO=', 'uint32_t=unsigned int'] 124 125 gcc_args = ['-E', '-fmerge-all-constants'] 126 gcc_args += ['-I' + directory for directory in include_dirs] 127 128 gcc_args += ['-D' + definition for definition in definitions] 129 parsed_ast = parse_file(c_source_file, 130 use_cpp=True, 131 cpp_path='arm-none-eabi-gcc', 132 cpp_args=gcc_args, 133 parser=GnuCParser()) 134 135 # now, walk the AST 136 visitor = TypeDeclVisitor(['PinName']) 137 return visitor.visit(parsed_ast) 138 139 140def write_pins_to_file(pins, pins_file, out_cpp_file): 141 """ 142 Write the generated pins for a specified mbed board into the output C++ file. 143 """ 144 145 include = '\n#include "../{}"'.format(pins_file) 146 147 count = ''' 148unsigned int jsmbed_js_magic_string_count = {}; 149 '''.format(len(pins)) 150 151 lengths = ',\n '.join(str(len(pin)) for pin in pins) 152 lenghts_source = ''' 153unsigned int jsmbed_js_magic_string_lengths[] = { 154 %s 155}; 156 ''' % lengths 157 158 magic_values = ',\n '.join(pins) 159 magic_source = ''' 160unsigned int jsmbed_js_magic_string_values[] = { 161 %s 162}; 163 ''' % magic_values 164 165 magic_strings = ',\n '.join('"' + pin + '"' for pin in pins) 166 magic_string_source = ''' 167const char * jsmbed_js_magic_strings[] = { 168 %s 169}; 170 ''' % magic_strings 171 172 out_cpp_file.write(LICENSE + include + count + lenghts_source + magic_source + magic_string_source) 173 174 175def main(): 176 """ 177 Perform the main function of this program 178 """ 179 if not os.path.exists('./mbed-os'): 180 print("Fatal: mbed-os directory does not exist.") 181 print("Try running 'make getlibs'") 182 sys.exit(1) 183 184 description = """ 185 Generate pins.cpp for a specified mbed board, using target definitions from the 186 mbed OS source tree. 187 """ 188 189 parser = argparse.ArgumentParser(description=description) 190 191 parser.add_argument('board', help='mbed board name') 192 parser.add_argument('-c', 193 help='Output C++ file (default: %(default)s)', 194 default='source/pins.cpp', 195 type=argparse.FileType('w')) 196 197 args = parser.parse_args() 198 board_name = args.board.upper() 199 200 target = Target.get_target(board_name) 201 202 directory_labels = ['TARGET_' + label for label in target.labels] + target.macros 203 204 targets_dir = os.path.join('.', 'mbed-os', 'targets') 205 206 pins_file = find_file(targets_dir, directory_labels, 'PinNames.h') 207 208 includes = enumerate_includes(targets_dir, directory_labels) 209 defines = list(directory_labels) 210 211 # enumerate pins from PinNames.h 212 pins = enumerate_pins(pins_file, ['./tools'] + list(includes), defines) 213 214 # first sort alphabetically, then by length. 215 pins = sorted(pins, key=lambda x: (len(x), x.lower())) 216 217 write_pins_to_file(pins, pins_file, args.c) 218 219 220if __name__ == "__main__": 221 main() 222