Source code for atprogram.atprogram

"""Python bindings for `make` and `atprogram`.
"""

from os import path, getenv, getcwd
from subprocess import run, PIPE, STDOUT

import re


[docs]def atprogram(project_path=None, device_name="ATSAML11E16A", verbose=0, clean=False, build=True, erase=True, program=True, verify=False, tool="EDBG", interface="SWD", atmel_studio_path=path.join( getenv("programfiles(x86)"), "Atmel", "Studio", "7.0"), make_path=None, atprogram_path=None, configuration="Debug", device_sn=None, jobs=getenv("NUMBER_OF_PROCESSORS"), make_command=None, atprogram_command=None, return_output=False, dry_run=False): r"""This function accesses the ``atprogram.exe`` program from Atmel Studio's installation directory, to automatically compile and flash code on Atmel boards. This function can compile projects and write them to a device. It determines what to do based on the arguments it gets. Specify at least one of either `project_path`, `make_command` or `atprogram_command`. .. warning:: \ **WARNING**: This function works only on projects that have been **built** at least *once*. The function makes use of the `makefiles` that are only generated by Atmel Studio. .. note:: \ **NOTE**: Verifying that the code has been flashed correctly to the board is known to return ``23``. This also happens when using ``atprogram.exe`` from the command line. This has been experienced with the Atmel board model `ATSAML11E16A`. Parameters ---------- project_path : str Location where the project resides. If it ends in ``.elf`` the elf file will be used. If it ends in ``Makefile`` the Makefile will be used. Otherwise it should be a path to a folder which holds the `Debug` folder. (the default is ``None``, which will return an error) device_name : str, optional Device name. E.g. `"atxmega128a1"` or `"at32uc3a0256"`. (the default is ``"ATSAML11E16A"``, which is a variant of the Atmel SAM L11 board) verbose : int, optional Specify the verbosity of the `atprogram` function: - 0: Silent - 1: Info - 2: Debug - 3: List (the default is `0`, which only prints compiler error and/or warnings) clean : bool, optional Run ``make clean`` in the provided `project_path`, if `True` (the default is `False`) build : bool, optional Run ``make all`` in the provided `project_path`, if `True` (the default is `True`) erase : bool, optional Run ``atprogram chiperase`` if `True` (the default is `True`) program : bool, optional Run ``atprogram program`` if `True` (the default is `True`) verify : bool, optional Run ``atprogram verify`` if `True` (the default is `False`) tool : str, optional Tool's name. Available options are: ``avrdragon``, ``avrispmk2``, ``avrone``, ``jtagice3``, ``jtagicemkii``, ``qt600``, ``stk500``, ``stk600``, ``samice``, ``edbg``, ``medbg``, ``nedbg``, ``atmelice``, ``pickit4``, ``powerdebugger``, ``megadfu`` or ``flip``. (the default is `"EDBG"`, being the Atmel® Embedded Debugger, which is what the Atmel board of model `ATSAML11E16A` uses) interface : str, optional Physical interface. Available options are: ``aWire``, ``debugWIRE``, ``HVPP``, ``HVSP``, ``ISP``, ``JTAG``, ``PDI``, ``UPDI``, ``TPI`` or ``SWD``. (the default is `"SWD"`, which is what the Atmel board of model `ATSAML11E16A` uses) atmel_studio_path : str, optional Specify the location where Atmel Studio is installed. Note that the path usually ends in a folder name corresponding to the version name e.g. 7.0 (the default is `"C:\\Program Files (x86)\\Atmel\\Studio\\7.0"`), which is the default installation location of Atmel Studio 7.0) make_path : str, optional Specify the path to `make.exe`, which should be the location of a GNU make installation for Windows. (the default is `"C:\\Program Files (x86)\\Atmel\\Studio\\7.0\\shellutils\\make.exe"`, as Atmel Studio already provides a GNU make installation for Windows) atprogram_path : str, optional Specify the location where `atprogram.exe` is installed. (the default is `"C:\\Program Files (x86)\\Atmel\\Studio\\7.0\\atbackend\\ atprogram.exe"`, as it is included with the installation of Atmel Studio) configuration : str, optional Specify which configuration of the Atmel Studio solution project should be used for compilation, flashing, etc. Most common options are: `Release`, `Debug`. (the default is `"Debug"`) device_sn : str, optional The programmer/debugger's serial number. Must be specified when more than one debugger is connected. (the default is `None`, as it is common to have a single programmer/debugger connected) jobs : int, optional How many jobs `make` should use (the default is ``getenv("NUMBER_OF_PROCESSORS")``, which takes your number of processor cores available) make_command : str, optional Other options to specify to `make`, similar to the command line ``[options] [target] ...`` (the default is `None`, which means no extra commands/options) atprogram_command : str, optional Other command(s) to pass to `atprogram`, similar to the command line arguments. This will be passed as: ``-d {device_name} -i {interface} [options] <command> [arguments] [<command> [arguments] ...]``. (the default is `None`, which means no extra commands/options) return_output : bool, optional If `True` the return value will be the output, else it will be the return code (the default is `False`, which means the return value is the return code of ``atprogram.exe`` or ``make``) dry_run : bool, optional Whether to run the commands using the `subprocess` module or just print the commands. (the default is `False`, which means the commands are indeed run using the `subprocess` module) Raises ------ ValueError Need to specify at least one of `project_path`, `make_command` or `atprogram_command`. Returns ------- int or str Depending on the `return_output` value, objects of different type are returned by this function. If the `return_output` value is `False`, then it returns a non-zero return value indicates the subprocess call returned an error. If `return_output` is `True`, then it returns a string holding the text returned by ``atprogram.exe`` or ``make``. """ elf_mode = False makefile_mode = False makefile_path = None if project_path is not None: elf_mode = path.splitext(project_path)[1] is ".elf" makefile_path, makefile = path.split(project_path) makefile_mode = makefile is "Makefile" if not makefile_mode: makefile_path = path.join(project_path, configuration) elif make_command is None and atprogram_command is None: raise ValueError( "Need to specify at least one of project_path, make_command or " + "atprogram_command.") else: # This is make_command mode or atprogram_command mode clean = build = erase = program = verify = False returncode = 0 output = SavePrint(return_output) stdout = PIPE if verbose >= 0 else None stderr = STDOUT if verbose >= 1 else None if not elf_mode and (clean or build or make_command): make_path = make_path or path.join( atmel_studio_path, "shellutils", "make.exe") def make_caller(command): args = ([make_path] + command.split()) kwargs = dict(cwd=makefile_path, stdout=stdout, stderr=stderr) if dry_run: output.print("".join(kwargs.get("cwd", getcwd()) ) + "> " + " ".join(args)) return 0 res = run(args, **kwargs) if verbose: output.print(res.stdout.decode()) return res.returncode if make_command and not returncode: returncode = make_caller(make_command) if clean and not returncode: returncode = make_caller("clean") if build and not returncode: returncode = make_caller(f"all --jobs {jobs} --output-sync") if (not makefile_mode and (erase or program or verify or atprogram_command) and not returncode): atprogram_path = atprogram_path or path.join( atmel_studio_path, "atbackend", "atprogram.exe") def atprogram_caller(command): args = ([atprogram_path] + (verbose-1) * ["-v"] + command.split()) kwargs = dict(stdout=stdout, stderr=stderr) if dry_run: output.print(getcwd() + "> " + " ".join(args)) return 0 res = run(args, **kwargs) if verbose: output.print(res.stdout.decode()) return res.returncode elf_file_path = None if not (program or verify) else project_path if \ elf_mode else path.join(project_path, configuration, path.basename(project_path)) + ".elf" atprogram_command = \ f"-t {tool} -i {interface} -d {device_name} " + \ (device_sn is not None) * f" -s {device_sn} " + \ (atprogram_command or erase * " chiperase " + program * f" program -f {elf_file_path} " + verify * f" verify -f {elf_file_path} " + (verbose >= 3) * " info") returncode = atprogram_caller(atprogram_command) if returncode: output.print(returncode) atprogram_caller("exitcode") if return_output: return output.output + f"\n{returncode}" else: return returncode
[docs]class SavePrint(object): """SavePrint A helper class to store standard output or print if `return_output` is false. Parameters ---------- return_output : bool If `True`, the output is returned but not printed. If `False`, the output is printed. Attributes ---------- output : str Holds the text that was taken from standard output. """ def __init__(self, return_output): self.return_output = return_output self.output = ""
[docs] def print(self, s): """print Parameters ---------- s : str A string to be added to the output. """ if self.return_output: self.output += s else: print(s)
[docs]def get_device_info( device_name="ATSAML11E16A", verbose=0, tool="EDBG", interface="SWD", atmel_studio_path=path.join( getenv("programfiles(x86)"), "Atmel", "Studio", "7.0"), atprogram_path=None, device_sn=None): r"""Polls the connected board for information, such as: target voltage, general device information (name, JTAG ID, CPU architecture,board series, DAL (debug access levels fuses status) and memory information regarding the address space and fuses). Parameters ---------- device_name : str, optional Device name. E.g. `"atxmega128a1"` or `"at32uc3a0256"`. (the default is ``"ATSAML11E16A"``, which is a variant of the Atmel SAM L11 board) verbose : int, optional Print most of `atprogram` output, if value is `1`, and print debugging information as well, if value is `2` (the default is `0`, which is to print only compiler warnings and errors) tool : str, optional Tool's name. Available options are: ``avrdragon``, ``avrispmk2``, ``avrone``, ``jtagice3``, ``jtagicemkii``, ``qt600``, ``stk500``, ``stk600``, ``samice``, ``edbg``, ``medbg``, ``nedbg``, ``atmelice``, ``pickit4``, ``powerdebugger``, ``megadfu`` or ``flip``. (the default is `"EDBG"`, being the Atmel® Embedded Debugger, which is what the Atmel board of model `ATSAML11E16A` uses) interface : str, optional Physical interface. Available options are: ``aWire``, ``debugWIRE``, ``HVPP``, ``HVSP``, ``ISP``, ``JTAG``, ``PDI``, ``UPDI``, ``TPI`` or ``SWD``. (the default is `"SWD"`, which is what the Atmel board of model `ATSAML11E16A` uses) atmel_studio_path : str, optional Specify the location where Atmel Studio is installed. Note that the path usually ends in a folder name corresponding to the version name e.g. 7.0 (the default is `"C:\\Program Files (x86)\\Atmel\\ Studio\\7.0"`), which is the default installation location of Atmel Studio 7.0) atprogram_path : str, optional Specify the location where `atprogram.exe` is installed. (the default is `"C:\\Program Files (x86)\\Atmel\\Studio\\7.0\\atbackend\\ atprogram.exe"`, as it is included with the installation of Atmel Studio) device_sn : str, optional The programmer/debugger's serial number. Must be specified when more than one debugger is connected. (the default is `None`, as it is common to have a single programmer/debugger connected) Returns ------- str The device information. """ atprogram_info = atprogram( atprogram_command="info", return_output=True, verbose=1, device_name=device_name, tool=tool, interface=interface, atmel_studio_path=atmel_studio_path, atprogram_path=atprogram_path, device_sn=device_sn, dry_run=False) device_info = { "Target voltage": float(re.findall( r"\nTarget voltage:\s(\d+(\.\d+)?)\sV", atprogram_info)[0][0]), "Device information": { "Name": re.findall(r"\nName:\s+(\S+)\s+", atprogram_info)[0], "JtagId": re.findall(r"\nJtagId:\s+(\S+)\s+", atprogram_info)[0], "CPU arch.": re.findall(r"\nCPU arch.:\s+(\S+)\s+", atprogram_info)[0], "Series": re.findall(r"\nSeries:\s+(\S+)\s+", atprogram_info)[0], "DAL": int(re.findall(r"\nDAL:\s+(\d+)\s+", atprogram_info)[0])}, "Memory Information": { "base": { address_space: [int(start_address, 16), int(size, 16)] for address_space, start_address, size in re.findall( r"\n (\w+)\s+(0[xX][0-9a-fA-F]+)\s+(0[xX][0-9a-fA-F]+)\s+\n", atprogram_info)}, "fuses": { fuse: int(value, 16) for fuse, value in re.findall( r"\n (\w+)\s+(0[xX][0-9a-fA-F]+)\s+\n", atprogram_info)} } } if verbose >= 2: print(device_info) return(device_info)
size_regexp = re.compile( r"\.elf\"\n\s*text\t\s*data\t\s*bss\t\s*dec\t\s*hex\t\s*filename\r\n\s*" + r"(\d+)\t\s*(\d+)\t\s*(\d+)\t\s*(\d+)\t\s*([0-9a-fA-F]+)\t\s*(\S+.elf)")
[docs]def get_project_size( project_path, device_name="ATSAML11E16A", verbose=0, tool="EDBG", interface="SWD", atmel_studio_path=path.join( getenv("programfiles(x86)"), "Atmel", "Studio", "7.0"), make_path=None, configuration="Debug", device_sn=None, jobs=getenv("NUMBER_OF_PROCESSORS")): """Returns a dictionary object with the information about the sizes of the program segments/sections (text, data, BSS) that usually make up a computer program, and that are in the ``.elf`` file compiled from the Atmel Studio project at `project_path`. The dictionary object returned specifically contains the `text`, `data` and `bss` sizes of the respective program segments, but also includes `dec` and `hex` values which are the decimal and, respectively, the hexadecimal value of the sum of `text`, `data` and `bss`, making up to the whole program size to be flashed to the Atmel board. The returned dictionary object also contains the `filename` of the ``.elf`` file. .. note:: \ All of the information returned by this function is obtained after a new compilation, of the Atmel project located at ``project_path``, using the :func:`atprogram` function. Parameters ---------- project_path : str Location where the project resides. If it ends in ``.elf`` the elf file will be used. If it ends in ``Makefile`` the Makefile will be used. Otherwise it should be a path to a folder which holds the `Debug` folder. (the default is ``None``, which will return an error) device_name : str, optional Device name. E.g. `"atxmega128a1"` or `"at32uc3a0256"`. (the default is ``"ATSAML11E16A"``, which is a variant of the Atmel SAM L11 board) verbose : int, optional Print most of `atprogram` output, if value is `1`, and print debugging information as well, if value is `2` (the default is `0`, which is to print only compiler warnings and errors) tool : str, optional Tool's name. Available options are: ``avrdragon``, ``avrispmk2``, ``avrone``, ``jtagice3``, ``jtagicemkii``, ``qt600``, ``stk500``, ``stk600``, ``samice``, ``edbg``, ``medbg``, ``nedbg``, ``atmelice``, ``pickit4``, ``powerdebugger``, ``megadfu`` or ``flip``. (the default is `"EDBG"`, being the Atmel® Embedded Debugger, which is what the Atmel board of model `ATSAML11E16A` uses) interface : str, optional Physical interface. Available options are: ``aWire``, ``debugWIRE``, ``HVPP``, ``HVSP``, ``ISP``, ``JTAG``, ``PDI``, ``UPDI``, ``TPI`` or ``SWD``. (the default is `"SWD"`, which is what the Atmel board of model `ATSAML11E16A` uses) atmel_studio_path : str, optional Specify the location where Atmel Studio is installed. Note that the path usually ends in a folder name corresponding to the version name e.g. 7.0 (the default is `"C:\\Program Files (x86)\\Atmel\\ Studio\\7.0"`), which is the default installation location of Atmel Studio 7.0) make_path : str, optional Specify the path to `make.exe`, which should be the location of a GNU make installation for Windows. (the default is `"C:\\Program Files (x86)\\Atmel\\Studio\\7.0\\shellutils\\ make.exe"`, as Atmel Studio already provides a GNU make installation for Windows) configuration : str, optional Specify which configuration of the Atmel Studio solution project should be used for compilation, flashing, etc. Most common options are: `Release`, `Debug`. (the default is `"Debug"`) device_sn : str, optional The programmer/debugger's serial number. Must be specified when more than one debugger is connected. (the default is `None`, as it is common to have a single programmer/debugger connected) jobs : int, optional How many jobs `make` should use (the default is ``getenv("NUMBER_OF_PROCESSORS")``, which takes your number of processor cores available) Returns ------- dict A dictionary of sizes of the sections, the total size and the file name. The keys of the dictionary are: ``"text"``, ``"data"`` ``"dss"``, ``"hex"`` and ``"filename"``. """ (text, data, bss, dec, hex, filename) = size_regexp.findall(atprogram( project_path, clean=True, build=True, erase=False, program=False, verify=False, return_output=True, verbose=1, dry_run=False))[0] result = {"text": int(text), "data": int(data), "bss": int( bss), "dec": int(dec), "hex": int(hex, 16), "filename": filename} if verbose >= 2: print(result) return result