Clone of official tools
Diff: export/uvision/__init__.py
- Revision:
- 31:8ea194f6145b
- Child:
- 35:da9c89f8be7d
diff -r f12ce67666d0 -r 8ea194f6145b export/uvision/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/export/uvision/__init__.py Wed Jan 04 11:58:24 2017 -0600 @@ -0,0 +1,245 @@ +import os +from os.path import sep, normpath, join, exists +import ntpath +import copy +from collections import namedtuple +import shutil +from subprocess import Popen, PIPE +import re + +from tools.arm_pack_manager import Cache +from tools.targets import TARGET_MAP +from tools.export.exporters import Exporter +from tools.export.cmsis import DeviceCMSIS + +cache_d = False + + +class DeviceUvision(DeviceCMSIS): + """Uvision Device class, inherits CMSIS Device class + + Encapsulates information necessary for uvision project targets""" + def __init__(self, target): + DeviceCMSIS.__init__(self, target) + dev_format = "$$Device:{0}${1}" + self.svd = '' + if self.debug_svd: + self.svd = dev_format.format(self.dname, self.debug_svd) + self.reg_file = dev_format.format(self.dname, self.compile_header) + self.debug_interface = self.uv_debug() + self.flash_dll = self.generate_flash_dll() + + def uv_debug(self): + """Return a namedtuple of information about uvision debug settings""" + UVDebug = namedtuple('UVDebug',['bin_loc','core_flag', 'key']) + + # CortexMXn => pCMX + cpu = self.core.replace("Cortex-", "C") + cpu = cpu.replace("+", "") + cpu = cpu.replace("F", "") + cpu_flag = "p"+cpu + + # Locations found in Keil_v5/TOOLS.INI + debuggers = {"st-link": ('STLink\\ST-LINKIII-KEIL_SWO.dll', 'ST-LINKIII-KEIL_SWO'), + "j-link":('Segger\\JL2CM3.dll', 'JL2CM3'), + "cmsis-dap":('BIN\\CMSIS_AGDI.dll', 'CMSIS_AGDI'), + "nulink":('NULink\\Nu_Link.dll','Nu_Link')} + res = debuggers[self.debug.lower()] + binary = res[0] + key = res[1] + + return UVDebug(binary, cpu_flag, key) + + def generate_flash_dll(self): + '''Flash DLL string from uvision + S = SW/JTAG Clock ID + C = CPU index in JTAG chain + P = Access Port + For the Options for Target -> Debug tab -> settings -> "Flash" tab in the dialog: + FD = RAM Start for Flash Functions + FC = RAM Size for Flash Functions + FN = Number of Flash types + FF = Flash File Name (without an extension) + FS = Start Address of the Flash Device + FL = Size of the Flash Device + FP = Full path to the Device algorithm (RTE) + + Necessary to flash some targets. Info gathered from algorithms field of pdsc file. + ''' + fl_count = 0 + def get_mem_no_x(mem_str): + mem_reg = "\dx(\w+)" + m = re.search(mem_reg, mem_str) + return m.group(1) if m else None + + RAMS = [(get_mem_no_x(info["start"]), get_mem_no_x(info["size"])) + for mem, info in self.target_info["memory"].items() if "RAM" in mem] + format_str = "UL2CM3(-S0 -C0 -P0 -FD{ramstart}"+" -FC{ramsize} "+"-FN{num_algos} {extra_flags})" + ramstart = '' + #Default according to Keil developer + ramsize = '1000' + if len(RAMS)>=1: + ramstart = RAMS[0][0] + extra_flags = [] + for name, info in self.target_info["algorithm"].items(): + if not name or not info: + continue + if int(info["default"])==0: + continue + name_reg = "\w*/([\w_]+)\.flm" + m = re.search(name_reg, name.lower()) + fl_name = m.group(1) if m else None + name_flag = "-FF" + str(fl_count) + fl_name + + start, size = get_mem_no_x(info["start"]), get_mem_no_x(info["size"]) + rom_start_flag = "-FS"+str(fl_count)+str(start) + rom_size_flag = "-FL" + str(fl_count) + str(size) + + if info["ramstart"] is not None and info["ramsize"] is not None: + ramstart = get_mem_no_x(info["ramstart"]) + ramsize = get_mem_no_x(info["ramsize"]) + + path_flag = "-FP" + str(fl_count) + "($$Device:"+self.dname+"$"+name+")" + + extra_flags.extend([name_flag, rom_start_flag, rom_size_flag, path_flag]) + fl_count += 1 + + extra = " ".join(extra_flags) + return format_str.format(ramstart=ramstart, + ramsize=ramsize, + extra_flags=extra, num_algos=fl_count) + + +class Uvision(Exporter): + """Keil Uvision class + + This class encapsulates information to be contained in a Uvision + project file (.uvprojx). + The needed information can be viewed in uvision.tmpl + """ + NAME = 'uvision5' + TOOLCHAIN = 'ARM' + TARGETS = [] + for target, obj in TARGET_MAP.iteritems(): + if not ("ARM" in obj.supported_toolchains and hasattr(obj, "device_name")): + continue + if not DeviceCMSIS.check_supported(target): + continue + TARGETS.append(target) + #File associations within .uvprojx file + file_types = {'.cpp': 8, '.c': 1, '.s': 2, + '.obj': 3, '.o': 3, '.lib': 4, + '.ar': 4, '.h': 5, '.hpp': 5, '.sct': 4} + + def uv_files(self, files): + """An generator containing Uvision specific information about project files + Positional Arguments: + files - the location of source files + + .uvprojx XML for project file: + <File> + <FileType>{{file.type}}</FileType> + <FileName>{{file.name}}</FileName> + <FilePath>{{file.loc}}</FilePath> + </File> + """ + for loc in files: + #Encapsulates the information necessary for template entry above + UVFile = namedtuple('UVFile', ['type','loc','name']) + _, ext = os.path.splitext(loc) + if ext.lower() in self.file_types: + type = self.file_types[ext.lower()] + name = ntpath.basename(normpath(loc)) + yield UVFile(type, loc, name) + + def format_flags(self): + """Format toolchain flags for Uvision""" + flags = copy.deepcopy(self.flags) + asm_flag_string = '--cpreproc --cpreproc_opts=-D__ASSERT_MSG,' + \ + ",".join(flags['asm_flags']) + # asm flags only, common are not valid within uvision project, + # they are armcc specific + flags['asm_flags'] = asm_flag_string + # cxx flags included, as uvision have them all in one tab + flags['c_flags'] = list(set(['-D__ASSERT_MSG'] + + flags['common_flags'] + + flags['c_flags'] + + flags['cxx_flags'])) + # not compatible with c99 flag set in the template + try: flags['c_flags'].remove("--c99") + except ValueError: pass + # cpp is not required as it's implicit for cpp files + try: flags['c_flags'].remove("--cpp") + except ValueError: pass + # we want no-vla for only cxx, but it's also applied for C in IDE, + # thus we remove it + try: flags['c_flags'].remove("--no_vla") + except ValueError: pass + flags['c_flags'] =" ".join(flags['c_flags']) + return flags + + def format_src(self, srcs): + """Make sources into the named tuple for use in the template""" + grouped = self.group_project_files(srcs) + for group, files in grouped.items(): + grouped[group] = self.uv_files(files) + return grouped + + def generate(self): + """Generate the .uvproj file""" + cache = Cache(True, False) + if cache_d: + cache.cache_descriptors() + + srcs = self.resources.headers + self.resources.s_sources + \ + self.resources.c_sources + self.resources.cpp_sources + \ + self.resources.objects + self.resources.libraries + ctx = { + 'name': self.project_name, + # project_files => dict of generators - file group to generator of + # UVFile tuples defined above + 'project_files': self.format_src(srcs), + 'linker_script':self.resources.linker_script, + 'include_paths': '; '.join(self.resources.inc_dirs).encode('utf-8'), + 'device': DeviceUvision(self.target), + } + # Turn on FPU optimizations if the core has an FPU + ctx['fpu_setting'] = 1 if 'f' not in ctx['device'].core.lower() \ + or 'd' in ctx['device'].core.lower() else 2 + ctx.update(self.format_flags()) + self.gen_file('uvision/uvision.tmpl', ctx, self.project_name+".uvprojx") + self.gen_file('uvision/uvision_debug.tmpl', ctx, self.project_name + ".uvoptx") + + @staticmethod + def build(project_name, log_name='build_log.txt', cleanup=True): + """ Build Uvision project """ + # > UV4 -r -j0 -o [log_name] [project_name].uvprojx + proj_file = project_name + ".uvprojx" + cmd = ['UV4', '-r', '-j0', '-o', log_name, proj_file] + + # Build the project + p = Popen(cmd, stdout=PIPE, stderr=PIPE) + out, err = p.communicate() + ret_code = p.returncode + + # Print the log file to stdout + with open(log_name, 'r') as f: + print f.read() + + # Cleanup the exported and built files + if cleanup: + os.remove(log_name) + os.remove(project_name+".uvprojx") + os.remove(project_name+".uvoptx") + # legacy .build directory cleaned if exists + if exists('.build'): + shutil.rmtree('.build') + if exists('BUILD'): + shutil.rmtree('BUILD') + + # Returns 0 upon success, 1 upon a warning, and neither upon an error + if ret_code != 0 and ret_code != 1: + # Seems like something went wrong. + return -1 + else: + return 0