• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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