Anders Blomdell / mbed-sdk-tools
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers __init__.py Source File

__init__.py

00001 from __future__ import print_function, absolute_import
00002 from builtins import str
00003 
00004 import os
00005 from os.path import normpath, exists, dirname
00006 import ntpath
00007 import copy
00008 from collections import namedtuple
00009 import shutil
00010 from subprocess import Popen, PIPE
00011 import re
00012 
00013 from tools.resources import FileType
00014 from tools.targets import TARGET_MAP
00015 from tools.export.exporters import Exporter
00016 from tools.export.cmsis import DeviceCMSIS
00017 
00018 
00019 class DeviceUvision (DeviceCMSIS):
00020     """Uvision Device class, inherits CMSIS Device class
00021 
00022     Encapsulates information necessary for uvision project targets"""
00023     def __init__(self, target):
00024         DeviceCMSIS.__init__(self, target)
00025         dev_format = "$$Device:{0}${1}"
00026         self.svd  = ''
00027         if self.debug_svd:
00028             self.svd  = dev_format.format(self.dname, self.debug_svd)
00029         self.reg_file  = dev_format.format(self.dname, self.compile_header)
00030         self.debug_interface  = self.uv_debug ()
00031         self.flash_dll  = self.generate_flash_dll ()
00032 
00033     def uv_debug (self):
00034         """Return a namedtuple of information about uvision debug settings"""
00035         UVDebug = namedtuple('UVDebug', ['bin_loc', 'core_flag', 'key'])
00036 
00037         # CortexMXn => pCMX
00038         cpu = self.core.replace("Cortex-", "C")
00039         cpu = cpu.replace("+", "")
00040         cpu = cpu.replace("F", "")
00041         cpu = cpu.replace("-NS", "")
00042         cpu_flag = "p"+cpu
00043 
00044         # Locations found in Keil_v5/TOOLS.INI
00045         debuggers = {
00046             "st-link": ('STLink\\ST-LINKIII-KEIL_SWO.dll',
00047                         'ST-LINKIII-KEIL_SWO'),
00048             "j-link": ('Segger\\JL2CM3.dll', 'JL2CM3'),
00049             "cmsis-dap": ('BIN\\CMSIS_AGDI.dll', 'CMSIS_AGDI'),
00050             "nulink": ('NULink\\Nu_Link.dll', 'Nu_Link')
00051         }
00052         res = debuggers[self.debug.lower()]
00053         binary = res[0]
00054         key = res[1]
00055 
00056         return UVDebug(binary, cpu_flag, key)
00057 
00058     def generate_flash_dll (self):
00059         '''Flash DLL string from uvision
00060         S = SW/JTAG Clock ID
00061         C = CPU index in JTAG chain
00062         P = Access Port
00063         For the Options for Target -> Debug -> settings -> "Flash" dialog:
00064         FD = RAM Start for Flash Functions
00065         FC = RAM Size for Flash Functions
00066         FN = Number of Flash types
00067         FF = Flash File Name (without an extension)
00068         FS = Start Address of the Flash Device
00069         FL = Size of the Flash Device
00070         FP = Full path to the Device algorithm (RTE)
00071 
00072         Necessary to flash some targets.
00073         '''
00074         fl_count = 0
00075 
00076         def get_mem_no_x(mem_str):
00077             mem_reg = "\dx(\w+)"
00078             m = re.search(mem_reg, mem_str)
00079             return m.group(1) if m else None
00080 
00081         RAMS = [
00082             (get_mem_no_x(info["start"]), get_mem_no_x(info["size"]))
00083             for mem, info in self.target_info["memory"].items() if "RAM" in mem
00084         ]
00085         format_str = (
00086             "UL2CM3(-S0 -C0 -P0 -FD{ramstart}"
00087             " -FC{ramsize} -FN{num_algos} {extra_flags})"
00088         )
00089         ramstart = ''
00090         # Default according to Keil developer
00091         ramsize = '1000'
00092         if len(RAMS) >= 1:
00093             ramstart = RAMS[0][0]
00094         extra_flags = []
00095         for name, info in self.target_info["algorithm"].items():
00096             if not name or not info:
00097                 continue
00098             if int(info["default"]) == 0:
00099                 continue
00100             name_reg = "\w*/([\w_]+)\.flm"
00101             m = re.search(name_reg, name.lower())
00102             fl_name = m.group(1) if m else None
00103             name_flag = "-FF" + str(fl_count) + fl_name
00104 
00105             start = get_mem_no_x(info["start"])
00106             size = get_mem_no_x(info["size"])
00107             rom_start_flag = "-FS" + str(fl_count) + str(start)
00108             rom_size_flag = "-FL" + str(fl_count) + str(size)
00109 
00110             if info["ramstart"] is not None and info["ramsize"] is not None:
00111                 ramstart = get_mem_no_x(info["ramstart"])
00112                 ramsize = get_mem_no_x(info["ramsize"])
00113 
00114             path_flag = "-FP{}($$Device:{}${})".format(
00115                 str(fl_count), self.dname, name
00116             )
00117 
00118             extra_flags.extend([
00119                 name_flag, rom_start_flag, rom_size_flag, path_flag
00120             ])
00121             fl_count += 1
00122 
00123         extra = " ".join(extra_flags)
00124         return format_str.format(ramstart=ramstart,
00125                                  ramsize=ramsize,
00126                                  extra_flags=extra, num_algos=fl_count)
00127 
00128 
00129 class Uvision (Exporter):
00130     """Keil Uvision class
00131 
00132     This class encapsulates information to be contained in a Uvision
00133     project file (.uvprojx).
00134     The needed information can be viewed in uvision.tmpl
00135     """
00136 
00137     POST_BINARY_WHITELIST = set([
00138         "MCU_NRF51Code.binary_hook",
00139         "TEENSY3_1Code.binary_hook",
00140         "LPCTargetCode.lpc_patch",
00141         "LPC4088Code.binary_hook",
00142         "MTSCode.combine_bins_mts_dot",
00143         "MTSCode.combine_bins_mts_dragonfly",
00144         "NCS36510TargetCode.ncs36510_addfib"
00145     ])
00146 
00147     # File associations within .uvprojx file
00148     file_types = {'.cpp': 8, '.c': 1, '.s': 2,
00149                   '.obj': 3, '.o': 3, '.lib': 4,
00150                   '.ar': 4, '.h': 5, '.hpp': 5, '.sct': 4}
00151 
00152     def uv_files (self, files):
00153         """An generator containing Uvision specific information about project files
00154         Positional Arguments:
00155         files - the location of source files
00156 
00157         .uvprojx XML for project file:
00158         <File>
00159             <FileType>{{file.type}}</FileType>
00160             <FileName>{{file.name}}</FileName>
00161             <FilePath>{{file.loc}}</FilePath>
00162         </File>
00163         """
00164         for loc in files:
00165             # Encapsulates the information necessary for template entry above
00166             UVFile = namedtuple('UVFile', ['type', 'loc', 'name'])
00167             _, ext = os.path.splitext(loc)
00168             if ext.lower() in self.file_types :
00169                 type = self.file_types [ext.lower()]
00170                 name = ntpath.basename(normpath(loc))
00171                 yield UVFile(type, loc, name)
00172 
00173     def format_flags (self):
00174         """Format toolchain flags for Uvision"""
00175         flags = copy.deepcopy(self.flags)
00176         asm_flag_string = (
00177             '--cpreproc --cpreproc_opts=-D__ASSERT_MSG,' +
00178             ",".join("-D{}".format(s) for s in
00179                      self.toolchain.get_symbols(for_asm=True)))
00180         flags['asm_flags'] = asm_flag_string
00181 
00182         config_header = self.config_header_ref
00183         config_option = self.toolchain.get_config_option(config_header.name)
00184         c_flags = set(
00185             flags['c_flags'] + flags['cxx_flags'] + flags['common_flags']
00186         )
00187         in_template = set(
00188             ["--no_vla", "--cpp", "--c99", "-MMD"] + config_option
00189         )
00190 
00191         def valid_flag(x):
00192             return (
00193                 x not in in_template and
00194                 not x.startswith("-O") and
00195                 not x.startswith("-std") and
00196                 not x.startswith("-D")
00197             )
00198 
00199         def is_define(s):
00200             return s.startswith("-D") and "(" not in s
00201 
00202         flags['c_flags'] = " ".join(
00203             f.replace('"', '\\"') for f in c_flags if valid_flag(f)
00204         )
00205         flags['c_flags'] += " "
00206         flags['c_flags'] += " ".join(config_option)
00207         flags['c_defines'] = " ".join(f[2:].replace('"', '\\"')
00208                                       for f in c_flags if is_define(f))
00209         flags['ld_flags'] = " ".join(set(flags['ld_flags']))
00210         return flags
00211 
00212     def format_src (self, srcs):
00213         """Make sources into the named tuple for use in the template"""
00214         grouped = self.group_project_files(srcs)
00215         for group, files in grouped.items():
00216             grouped[group] = sorted(list(self.uv_files (files)),
00217                                     key=lambda tuple: tuple[2].lower())
00218         return grouped
00219 
00220     @staticmethod
00221     def format_fpu (core):
00222         """Generate a core's FPU string"""
00223         if core.endswith("FD"):
00224             return "FPU3(DFPU)"
00225         elif core.endswith("F"):
00226             return "FPU2"
00227         else:
00228             return ""
00229 
00230     def generate (self):
00231         """Generate the .uvproj file"""
00232         srcs = (
00233             self.resources.headers + self.resources.s_sources +
00234             self.resources.c_sources + self.resources.cpp_sources +
00235             self.resources.objects + self.libraries
00236         )
00237         ctx = {
00238             'name': self.project_name,
00239             # project_files => dict of generators - file group to generator of
00240             # UVFile tuples defined above
00241             'project_files': sorted(list(self.format_src (srcs).items()),
00242                                     key=lambda tuple: tuple[0].lower()),
00243             'include_paths': ';'.join(self.filter_dot(d) for d in
00244                                       self.resources.inc_dirs).encode('utf-8'),
00245             'device': DeviceUvision(self.target),
00246         }
00247         sct_name, sct_path = self.resources.get_file_refs(
00248             FileType.LD_SCRIPT)[0]
00249         ctx['linker_script'] = self.toolchain.correct_scatter_shebang(
00250             sct_path, dirname(sct_name))
00251         if ctx['linker_script'] != sct_path:
00252             self.generated_files.append(ctx['linker_script'])
00253         ctx['cputype'] = ctx['device'].core.rstrip("FD").replace("-NS", "")
00254         if ctx['device'].core.endswith("FD"):
00255             ctx['fpu_setting'] = 3
00256         elif ctx['device'].core.endswith("F"):
00257             ctx['fpu_setting'] = 2
00258         else:
00259             ctx['fpu_setting'] = 1
00260         ctx['fputype'] = self.format_fpu (ctx['device'].core)
00261         ctx['armc6'] = int(self.TOOLCHAIN is 'ARMC6')
00262         ctx['toolchain_name'] = self.TOOLCHAIN_NAME
00263         ctx.update(self.format_flags ())
00264         self.gen_file(
00265             'uvision/uvision.tmpl', ctx, self.project_name + ".uvprojx"
00266         )
00267         self.gen_file(
00268             'uvision/uvision_debug.tmpl', ctx, self.project_name + ".uvoptx"
00269         )
00270 
00271     @staticmethod
00272     def clean(project_name):
00273         os.remove(project_name + ".uvprojx")
00274         os.remove(project_name + ".uvoptx")
00275         # legacy .build directory cleaned if exists
00276         if exists('.build'):
00277             shutil.rmtree('.build')
00278         if exists('BUILD'):
00279             shutil.rmtree('BUILD')
00280 
00281     @staticmethod
00282     def build (project_name, log_name='build_log.txt', cleanup=True):
00283         """ Build Uvision project """
00284         # > UV4 -r -j0 -o [log_name] [project_name].uvprojx
00285         proj_file = project_name + ".uvprojx"
00286         cmd = ['UV4', '-r', '-j0', '-o', log_name, proj_file]
00287 
00288         # Build the project
00289         p = Popen(cmd, stdout=PIPE, stderr=PIPE)
00290         out, err = p.communicate()
00291         ret_code = p.returncode
00292 
00293         # Print the log file to stdout
00294         with open(log_name, 'r') as f:
00295             print(f.read())
00296 
00297         # Cleanup the exported and built files
00298         if cleanup:
00299             os.remove(log_name)
00300             Uvision.clean(project_name)
00301 
00302         # Returns 0 upon success, 1 upon a warning, and neither upon an error
00303         if ret_code != 0 and ret_code != 1:
00304             # Seems like something went wrong.
00305             return -1
00306         else:
00307             return 0
00308 
00309 
00310 class UvisionArmc5(Uvision ):
00311     NAME = 'uvision5-armc5'
00312     TOOLCHAIN = 'ARM'
00313     TOOLCHAIN_NAME = ''
00314 
00315     @classmethod
00316     def is_target_supported(cls, target_name):
00317         target = TARGET_MAP[target_name]
00318         if not (set(target.supported_toolchains).intersection(
00319                 set(["ARM", "uARM"]))):
00320             return False
00321         if not DeviceCMSIS.check_supported(target_name):
00322             return False
00323         if "Cortex-A" in target.core:
00324             return False
00325         if not hasattr(target, "post_binary_hook"):
00326             return True
00327         if target.post_binary_hook['function'] in cls.POST_BINARY_WHITELIST:
00328             return True
00329         else:
00330             return False
00331 
00332 
00333 class UvisionArmc6(Uvision ):
00334     NAME = 'uvision5-armc6'
00335     TOOLCHAIN = 'ARMC6'
00336     TOOLCHAIN_NAME = '6070000::V6.7::.\ARMCLANG'
00337 
00338     @classmethod
00339     def is_target_supported(cls, target_name):
00340         target = TARGET_MAP[target_name]
00341         if not (set(target.supported_toolchains).intersection(
00342                 set(["ARMC6"]))):
00343             return False
00344         if not DeviceCMSIS.check_supported(target_name):
00345             return False
00346         if "Cortex-A" in target.core:
00347             return False
00348         if not hasattr(target, "post_binary_hook"):
00349             return True
00350         if target.post_binary_hook['function'] in cls.POST_BINARY_WHITELIST:
00351             return True
00352         else:
00353             return False