├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── nrfjprog ├── __init__.py ├── __main__.py ├── model │ ├── __init__.py │ ├── device.py │ ├── perform_command.py │ ├── perform_command_daplink.py │ ├── perform_command_jlink.py │ └── perform_command_openocd.py └── nrfjprog_version.py ├── nrfjprog_cli.py ├── requirements.txt ├── setup.py └── tests ├── resources ├── ble_app_hrs_s130_with_dfu_pca10028.hex └── ble_app_hrs_s132_with_dfu_pca10040.hex └── unit_tests.py /.gitignore: -------------------------------------------------------------------------------- 1 | # For tests so we don't add the .exe under test to repo. 2 | dist/ 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | env/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *,cover 49 | .hypothesis/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | 58 | # Sphinx documentation 59 | docs/_build/ 60 | 61 | # PyBuilder 62 | target/ 63 | 64 | #Ipython Notebook 65 | .ipynb_checkpoints 66 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | # - "3.4" 5 | - "3.5" 6 | - "3.5-dev" # 3.5 development branch 7 | # - "nightly" # currently points to 3.6-dev 8 | # command to install dependencies 9 | install: "pip install -r requirements.txt" 10 | # command to run tests 11 | script: python -m nrfjprog -h 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Nordic Semiconductor 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Nordic Semiconductor ASA nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/NordicSemiconductor/nrfjprog.svg?branch=master)](https://travis-ci.org/NordicSemiconductor/nRF5-universal-prog) 2 | [![PyPI](https://img.shields.io/pypi/l/Django.svg)](https://opensource.org/licenses/BSD-3-Clause) 3 | 4 | # nRF5-universal-prog 5 | 6 | Note: nRF5-universal-prog is not supported by Nordic Semiconductor. It is uploaded to GitHub to serve as a guide to using pynrfjprog/pyOCD/openOCD with nRF5 devices. However it works, and may be useful. For an officially supported, production quality, command line tool, see: https://infocenter.nordicsemi.com/index.jsp. 7 | 8 | nRF5-universal-prog is a tool to program and debug Nordic Semiconductor's nRF5 series devices. Using a tool such as PyInstaller or py2exe this module can be converted to a stand-alone (cross-platform) executable. The user has the option to run nRF5-universal-prog without worrying about a python environment (as an exe), to run nRF5-universal-prog from python, or to use this module in their custom scripts as a higher-level alternative/supplement to pynrfjprog/pyOCD. 9 | 10 | nRF5-universal-prog will by default assume that the PC is connected to a JLink debugger and use pynrfjprog accordingly. Add the --daplink argument to any command to tell nRF5-universal-prog that the PC is connected to a CMSIS-DAP/DAP-Link debugger and to use pyOCD instead of pynrfjprog. 11 | 12 | Note, nRF5-universal-prog is slightly faster when running as a built executable instead of as a Python script. 13 | 14 | # Running the .exe 15 | 1. In Releases, download the correct compressed folder for your operating system and extract it. 16 | 2. Either add the path containing 'nRF5-universal-prog.exe' to your environment variables or navigate to it's directory. 17 | 3. $ nRF5-universal-prog -h (nRF5-universal-prog.exe -h if path not added to your environment variables). 18 | 4. $ nRF5-universal-prog program -h 19 | 5. $ nRF5-universal-prog program -f PATH_TO_APP.hex -e -v -r 20 | 21 | # Running in Python 22 | 1. Clone or download this repository and navigate to the repo's root directory ~/nRF5-universal-prog/. 23 | 2. $ python setup.py install or $ sudo pip install -r requirements.txt 24 | * If installing via setup.py, the nRF5-universal-prog package will be installed in your system's Python environment. 25 | 3. $ python nrfjprog_cli.py --help 26 | 4. $ python nrfjprog_cli.py program --help 27 | 5. $ python nrfjprog_cli.py program --file PATH_TO_APP.hex --eraseall --verify --systemreset (--daplink) 28 | 29 | # Structure 30 | ```python 31 | nRF5-universal-prog\ 32 | # LICENSE, README.md, setup.py and requirements.txt (used to install this module). 33 | nrfjprog_cli.py # Located outside the nRF5-universal-prog package so PyInstaller can build into an .exe properly. nRF5-universal-prog can be run in python with this file as well. 34 | nrfjprog\ 35 | __init__.py # Package marker to make nrfjprog a module. 36 | __main__.py # This is where the command line interface is implemented. It parses arguments using argparse and fowards them to perform_command.py. 37 | nrfjprog_version.py # A global variable containing the current version number of nRF5-universal-prog. 38 | model\ 39 | __init__.py # Package marker to make model a module. 40 | perform_command.py # Determines if a CMSIS-DAP/DAP-Link or JLink debugger is connected to the PC and fowards the command accordingly. 41 | perform_command_daplink.py # This is where the functionality of each command is implemented. Relies on the pyOCD module. 42 | perform_command_jlink.py # This is where the functionality of each command is implemented. Relies on the pynrfjprog module. 43 | device.py # Implements a class to represent the specs of a specific device (i.e. NRF52_FP1). 44 | tests\ 45 | unit_tests.py # All of the unit tests for nRF5-universal-prog.exe. Requires that dist/OS/ to be present on system which contains the built .exe for the system's OS. 46 | ``` 47 | 48 | # Architecture 49 | ```python 50 | """ 51 | Detailed below is how our software is stacked. Each layer depends on the layer below. 52 | """ 53 | nRF5-universal-prog.exe # Command line tool providing high level programming functionality for nRF5x devices. 54 | 55 | pynrfjprog # Imports the nrfjprog DLL into Python and wraps it to be used in applications like this one or directly in scripts. 56 | nrfjprogdll # A DLL that does some error checking and calls SEGGER's JLink API. Wraps JLink API specifically for nRF5x devices. 57 | JLinkARMDLL # A DLL provided by SEGGER that works with SEGGER debuggers. Performs all low level operations with target device. 58 | 59 | or, if using a CMSIS-DAP/DAP-Link debugger, then only: 60 | pyOCD # https://github.com/mbedmicro/pyOCD. 61 | ``` 62 | 63 | # Bundling as an executable 64 | ```python 65 | """ 66 | PyInstaller bundles a Python application and all its dependencies into a single package and is tested against Windows, Mac OS X, and Linux. http://pythonhosted.org/PyInstaller/. 67 | We will use PyInstaller to create an executable to distribute to the end user from our nRF5-universal-prog Python application. It will be multi platform. 68 | Currently we bundle into a single package but we can also bundle into a single executable (one file) using PyInstaller. 69 | """ 70 | ``` 71 | 1. $ sudo pip install pyinstaller 72 | 2. Navigate to root directory of this repo and run $ pyinstaller nrfjprog_cli.py --clean --name nRF5-universal-prog 73 | 3. Move the DLL's required by the [official nrfjprog.exe](https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.tools/dita/tools/nrf5x_command_line_tools/nrf5x_nrfjprogexe.html) into ~/dist/nRF5-universal-prog/ 74 | 4. Navigate to ~/dist/nRF5-universal-prog and run $ nRF5-universal-prog.exe --help 75 | 5. Add ~/dist/nRF5-universal-prog to your path and call $ nRF5-universal-prog -h from any directory. 76 | 77 | # Coding Standard 78 | * https://google.github.io/styleguide/pyguide.html 79 | * http://www.clifford.at/style.html 80 | * http://semver.org/ 81 | 82 | # Future 83 | We want nRF5-universal-prog to be flexible and open. We want it to be an option for our users all the way from development and testing to production programming. 84 | -------------------------------------------------------------------------------- /nrfjprog/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016, Nordic Semiconductor 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # 7 | # * Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | # 10 | # * Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # 14 | # * Neither the name of Nordic Semiconductor ASA nor the names of its 15 | # contributors may be used to endorse or promote products derived from 16 | # this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | """Package marker file.""" 30 | -------------------------------------------------------------------------------- /nrfjprog/__main__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016, Nordic Semiconductor 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # 7 | # * Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | # 10 | # * Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # 14 | # * Neither the name of Nordic Semiconductor ASA nor the names of its 15 | # contributors may be used to endorse or promote products derived from 16 | # this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | """ 30 | This module sets up and runs the command-line-interface for nrfjprog. 31 | 32 | It receives the command and options from the user and passes it to model/perform_command.py. 33 | """ 34 | 35 | import argparse 36 | 37 | 38 | class Nrfjprog(object): 39 | """Class that handles the command-line interface.""" 40 | 41 | nrfjprog_description = "nrfjprog is a command line tool used for programming nRF5x devices. It is implemented in Python and utilizes pynrfjprog, a Python wrapper for the nrfjprog DLL. Both nrfjprog and pynrfjprog are open source and can be found on Nordic's GitHub. To report an issue, request a feature, or contribute please see: https://github.com/NordicSemiconductor/nrfjprog." 42 | nrfjprog_epilog = "One positional command is required, followed by it's specific arguments (if any). To see arguments for a specific command: python nrfjprog COMMAND -h (i.e. python nrfjprog erase -h)." 43 | 44 | help_messages = { 45 | 'erase': "Erases the device's FLASH.", 46 | 'halt': "Halts the device's CPU.", 47 | 'ids': 'Displays the serial numbers of all debuggers connected to the PC.', 48 | 'memrd': "Reads the device's memory.", 49 | 'memwr': "Writes one word in the device's memory.", 50 | 'pinresetenable': "Enable the pin reset (GPIO 21) on nRF52 devices. Invalid command on nRF51 devices.", 51 | 'program': 'Programs the device.', 52 | 'rbp': 'Enables the readback protection mechanism.', 53 | 'readregs': 'Reads the CPU registers.', 54 | 'readtofile': "Reads and stores the device's memory.", 55 | 'recover': 'Erases all user FLASH and RAM and disables any readback protection mechanisms that are enabled.', 56 | 'reset': 'Resets the device.', 57 | 'run': "Runs the device's CPU.", 58 | 'verify': "Verifies that the device's memory contains the correct data.", 59 | 'version': 'Display the nrfjprog and JLinkARM DLL versions.'} 60 | 61 | def __init__(self): 62 | """Initializes the command-line interface.""" 63 | self.parser = argparse.ArgumentParser( 64 | description=self.nrfjprog_description, 65 | epilog=self.nrfjprog_epilog) 66 | self.subparsers = self.parser.add_subparsers(dest='command') 67 | self.args = None 68 | 69 | self._add_commands() 70 | 71 | # TODO fix doc and param names for callback as string. 72 | def add_common_properties_to_command(self, parser, connects=True): 73 | """ 74 | Adds the common arguments each command shares. All commands except the 'ids' and 'version' command share these arguments. 75 | 76 | @param ArgumentParser parser: The top-level positional command to add the shared arguments to. 77 | @param boolean connects: If this command connects to the emulator (debugger) and should have the option to set the clock speed/serial number. 78 | """ 79 | self._add_daplink_argument(parser) 80 | self._add_jlink_arm_dll_path_argument(parser) 81 | self._add_openocd_argument(parser) 82 | self._add_quiet_argument(parser) 83 | 84 | if connects: 85 | self._add_clockspeed_argument(parser) 86 | self._add_deviceversion_argument(parser) 87 | self._add_snr_argument(parser) 88 | 89 | def run(self): 90 | """Parse user input and execute the requested functionality.""" 91 | self.args = self.parser.parse_args() 92 | 93 | if self.args.daplink: 94 | from .model.perform_command_daplink import DapLink 95 | perform_command = DapLink() 96 | elif self.args.openocd: 97 | from .model.perform_command_openocd import OpenOCD 98 | perform_command = OpenOCD() 99 | else: 100 | from .model.perform_command_jlink import JLink 101 | perform_command = JLink() 102 | 103 | getattr( 104 | perform_command, 105 | 'log')( 106 | self.args, 107 | self.help_messages[ 108 | self.args.command]) 109 | getattr(perform_command, self.args.command)(self.args) 110 | 111 | def _add_commands(self): 112 | """ 113 | Split up the functionality of nrfjprog into multiple sub-commands. 114 | 115 | :param Object subparsers: https://docs.python.org/3/library/argparse.html#sub-commands. 116 | """ 117 | self._add_erase_command() 118 | self._add_halt_command() 119 | self._add_ids_command() 120 | self._add_memrd_command() 121 | self._add_memwr_command() 122 | self._add_pinresetenable_command() 123 | self._add_program_command() 124 | self._add_readback_command() 125 | self._add_readregs_command() 126 | self._add_readtofile_command() 127 | self._add_recover_command() 128 | self._add_reset_command() 129 | self._add_run_command() 130 | self._add_verify_command() 131 | self._add_version_command() 132 | 133 | # The top-level positional commands of our command-line interface. 134 | 135 | def _add_erase_command(self): 136 | erase_parser = self.subparsers.add_parser( 137 | 'erase', help=self.help_messages['erase']) 138 | self.add_common_properties_to_command(erase_parser) 139 | 140 | self._add_erase_group(erase_parser) 141 | 142 | def _add_halt_command(self): 143 | halt_parser = self.subparsers.add_parser( 144 | 'halt', help=self.help_messages['halt']) 145 | self.add_common_properties_to_command(halt_parser) 146 | 147 | def _add_ids_command(self): 148 | ids_parser = self.subparsers.add_parser( 149 | 'ids', help=self.help_messages['ids']) 150 | self.add_common_properties_to_command(ids_parser, connects=False) 151 | 152 | def _add_memrd_command(self): 153 | memrd_parser = self.subparsers.add_parser( 154 | 'memrd', help=self.help_messages['memrd']) 155 | self.add_common_properties_to_command(memrd_parser) 156 | 157 | self._add_addr_argument(memrd_parser) 158 | self._add_length_argument(memrd_parser) 159 | 160 | def _add_memwr_command(self): 161 | memwr_parser = self.subparsers.add_parser( 162 | 'memwr', help=self.help_messages['memwr']) 163 | self.add_common_properties_to_command(memwr_parser) 164 | 165 | self._add_addr_argument(memwr_parser) 166 | self._add_val_argument(memwr_parser) 167 | 168 | def _add_pinresetenable_command(self): 169 | pinresetenable_parser = self.subparsers.add_parser( 170 | 'pinresetenable', help=self.help_messages['pinresetenable']) 171 | self.add_common_properties_to_command(pinresetenable_parser) 172 | 173 | def _add_program_command(self): 174 | program_parser = self.subparsers.add_parser( 175 | 'program', help=self.help_messages['program']) 176 | self.add_common_properties_to_command(program_parser) 177 | 178 | self._add_file_argument(program_parser) 179 | self._add_erase_before_flash_group(program_parser) 180 | self._add_verify_argument(program_parser) 181 | self._add_reset_group(program_parser) 182 | 183 | def _add_readback_command(self): 184 | readback_parser = self.subparsers.add_parser( 185 | 'rbp', help=self.help_messages['rbp']) 186 | self.add_common_properties_to_command(readback_parser) 187 | 188 | self._add_rbplevel_argument(readback_parser) 189 | 190 | def _add_readregs_command(self): 191 | readregs_parser = self.subparsers.add_parser( 192 | 'readregs', help=self.help_messages['readregs']) 193 | self.add_common_properties_to_command(readregs_parser) 194 | 195 | def _add_readtofile_command(self): 196 | readtofile_parser = self.subparsers.add_parser( 197 | 'readtofile', help=self.help_messages['readtofile']) 198 | self.add_common_properties_to_command(readtofile_parser) 199 | 200 | self._add_file_argument(readtofile_parser) 201 | self._add_readcode_argument(readtofile_parser) 202 | self._add_readram_argument(readtofile_parser) 203 | self._add_readuicr_argument(readtofile_parser) 204 | 205 | def _add_recover_command(self): 206 | recover_parser = self.subparsers.add_parser( 207 | 'recover', help=self.help_messages['recover']) 208 | self.add_common_properties_to_command(recover_parser) 209 | 210 | self._add_family_argument(recover_parser) 211 | 212 | def _add_reset_command(self): 213 | reset_parser = self.subparsers.add_parser( 214 | 'reset', help=self.help_messages['reset']) 215 | self.add_common_properties_to_command(reset_parser) 216 | 217 | self._add_reset_group(reset_parser) 218 | 219 | def _add_run_command(self): 220 | run_parser = self.subparsers.add_parser( 221 | 'run', help=self.help_messages['run']) 222 | self.add_common_properties_to_command(run_parser) 223 | 224 | self._add_pc_argument(run_parser) 225 | self._add_sp_argument(run_parser) 226 | 227 | def _add_verify_command(self): 228 | verify_parser = self.subparsers.add_parser( 229 | 'verify', help=self.help_messages['verify']) 230 | self.add_common_properties_to_command(verify_parser) 231 | 232 | self._add_file_argument(verify_parser) 233 | 234 | def _add_version_command(self): 235 | version_parser = self.subparsers.add_parser( 236 | 'version', help=self.help_messages['version']) 237 | self.add_common_properties_to_command(version_parser, connects=False) 238 | 239 | # Mutually exclusive groups. argparse will make sure only one of the 240 | # arguments in a mutually exclusive group was present on the command-line. 241 | 242 | def _add_erase_group(self, parser): 243 | erase_group = parser.add_mutually_exclusive_group() 244 | self._add_eraseall_argument(erase_group) 245 | self._add_erasepage_argument(erase_group) 246 | self._add_eraseuicr_argument(erase_group) 247 | 248 | def _add_erase_before_flash_group(self, parser): 249 | erase_before_flash_group = parser.add_mutually_exclusive_group() 250 | self._add_eraseall_argument(erase_before_flash_group) 251 | self._add_sectors_erase_argument(erase_before_flash_group) 252 | self._add_sectorsuicr_erase_argument(erase_before_flash_group) 253 | 254 | def _add_reset_group(self, parser): 255 | reset_group = parser.add_mutually_exclusive_group() 256 | self._add_debugreset_argument(reset_group) 257 | self._add_pinreset_argument(reset_group) 258 | self._add_sysreset_argument(reset_group) 259 | 260 | # The add_argument helper functions. They define how a single command-line 261 | # argument should be parsed. These are all options. 262 | 263 | def _add_addr_argument(self, parser): 264 | parser.add_argument( 265 | '-a', 266 | '--addr', 267 | type=self.auto_int, 268 | help='The address in memory to be read/written.', 269 | required=True) 270 | 271 | def _add_clockspeed_argument(self, parser): 272 | parser.add_argument( 273 | '-c', 274 | '--clockspeed', 275 | type=int, 276 | metavar='CLOCKSPEEDKHZ', 277 | help='Sets the debugger SWD clock speed in kHz for the operation.') 278 | 279 | def _add_daplink_argument(self, parser): 280 | parser.add_argument( 281 | '--daplink', 282 | action='store_true', 283 | help='PC is connected to a CMSIS-DAP/DAP-Link debugger.') 284 | 285 | def _add_debugreset_argument(self, parser): 286 | parser.add_argument( 287 | '-d', 288 | '--debugreset', 289 | action='store_true', 290 | help='Executes a debug reset.') 291 | 292 | NRF5_DEVICE_VERSIONS = [ 293 | 'NRF52_FP1', 294 | 'NRF52_FP1_ENGB', 295 | 'NRF52_FP1_ENGA', 296 | 'NRF51_XLR3LC', 297 | 'NRF51_XLR3P', 298 | 'NRF51_L3', 299 | 'NRF51_XLR3', 300 | 'NRF51_XLR2', 301 | 'NRF51_XLR1'] 302 | 303 | def _add_deviceversion_argument(self, parser): 304 | parser.add_argument( 305 | '--deviceversion', 306 | type=str, 307 | help='The version of the target device.', 308 | required=False, 309 | choices=self.NRF5_DEVICE_VERSIONS) 310 | 311 | def _add_eraseall_argument(self, parser): 312 | parser.add_argument( 313 | '-e', 314 | '--eraseall', 315 | action='store_true', 316 | help='Erase all user FLASH including UICR.') 317 | 318 | def _add_erasepage_argument(self, parser): 319 | parser.add_argument( 320 | '--erasepage', 321 | type=self.auto_int, 322 | metavar='PAGESTARTADDR', 323 | help='Erase the page starting at the address PAGESTARTADDR.') 324 | 325 | def _add_eraseuicr_argument(self, parser): 326 | parser.add_argument( 327 | '--eraseuicr', 328 | action='store_true', 329 | help='Erase the UICR page in FLASH.') 330 | 331 | def _add_family_argument(self, parser): 332 | parser.add_argument( 333 | '--family', 334 | type=str, 335 | help='The family of the target device.', 336 | required=True, 337 | choices=[ 338 | 'NRF51', 339 | 'NRF52']) 340 | 341 | def _add_file_argument(self, parser): 342 | parser.add_argument( 343 | '-f', 344 | '--file', 345 | help='The hex file to be used in this operation.', 346 | required=True) 347 | 348 | def _add_jlink_arm_dll_path_argument(self, parser): 349 | parser.add_argument( 350 | '--jlink_arm_dll_path', 351 | help='''Absolute path to JLink_ARM.dll to use instead of looking in the default SEGGER installation directory.''') 352 | 353 | def _add_length_argument(self, parser): 354 | parser.add_argument( 355 | '-l', 356 | '--length', 357 | type=self.auto_int, 358 | help='The number of bytes to be read. 4 (one word) by default.', 359 | default=4) 360 | 361 | def _add_openocd_argument(self, parser): 362 | parser.add_argument( 363 | '--openocd', 364 | action='store_true', 365 | help='PC should use openOCD as debugger host.') 366 | 367 | def _add_pc_argument(self, parser): 368 | parser.add_argument( 369 | '--pc', 370 | type=self.auto_int, 371 | metavar='PC_ADDR', 372 | help='Initial program counter to start the CPU running from.') 373 | 374 | def _add_pinreset_argument(self, parser): 375 | parser.add_argument( 376 | '-p', 377 | '--pinreset', 378 | action='store_true', 379 | help='Executes a pin reset.') 380 | 381 | def _add_quiet_argument(self, parser): 382 | parser.add_argument( 383 | '-q', 384 | '--quiet', 385 | action='store_true', 386 | help='Nothing will be printed to terminal during the operation.') 387 | 388 | def _add_rbplevel_argument(self, parser): 389 | parser.add_argument( 390 | '--rbplevel', 391 | help='Specify the read back protection level (NRF51 only).', 392 | choices=[ 393 | 'CR0', 394 | 'ALL']) 395 | 396 | def _add_readcode_argument(self, parser): 397 | parser.add_argument( 398 | '--readcode', 399 | action='store_true', 400 | help='If this argument is specified read code FLASH and store in FILE.') 401 | 402 | def _add_readram_argument(self, parser): 403 | parser.add_argument( 404 | '--readram', 405 | action='store_true', 406 | help='If this argument is specified read RAM and store in FILE.') 407 | 408 | def _add_readuicr_argument(self, parser): 409 | parser.add_argument( 410 | '--readuicr', 411 | action='store_true', 412 | help='If this argument is specified read UICR FLASH and store in FILE.') 413 | 414 | def _add_sectors_erase_argument(self, parser): 415 | parser.add_argument( 416 | '-se', 417 | '--sectorserase', 418 | action='store_true', 419 | help='Erase all sectors that FILE contains data in before programming.') 420 | 421 | def _add_sectorsuicr_erase_argument(self, parser): 422 | parser.add_argument( 423 | '-u', 424 | '--sectorsanduicrerase', 425 | action='store_true', 426 | help='Erase all sectors that FILE contains data in and the UICR (unconditionally) before programming.') 427 | 428 | def _add_snr_argument(self, parser): 429 | parser.add_argument( 430 | '-s', 431 | '--snr', 432 | type=int, 433 | help='Selects the debugger with the given serial number among all those connected to the PC for the operation.') 434 | 435 | def _add_sp_argument(self, parser): 436 | parser.add_argument( 437 | '--sp', 438 | type=self.auto_int, 439 | metavar='SP_ADDR', 440 | help='Initial stack pointer.') 441 | 442 | def _add_sysreset_argument(self, parser): 443 | parser.add_argument( 444 | '-r', 445 | '--systemreset', 446 | action='store_true', 447 | help='Executes a system reset.') 448 | 449 | def _add_val_argument(self, parser): 450 | parser.add_argument( 451 | '--val', 452 | type=self.auto_int, 453 | help='The 32 bit word to be written to memory.', 454 | required=True) 455 | 456 | def _add_verify_argument(self, parser): 457 | parser.add_argument( 458 | '-v', 459 | '--verify', 460 | action='store_true', 461 | help='Read back memory and verify that it matches FILE.') 462 | 463 | # Helpers. 464 | 465 | @staticmethod 466 | def auto_int(number): 467 | """Needed in order to accommodate base 16 (hex) and base 10 (decimal) parameters we can enable auto base detection.""" 468 | return int(number, 0) 469 | 470 | 471 | def main(): 472 | """ 473 | Set up a command line interface using the argparse module. 474 | 475 | Above we will define what arguments our program requires and argparse will figure out how to parse those from sys.argv. 476 | For info on argparse see: https://docs.python.org/3/library/argparse.html. 477 | """ 478 | cli = Nrfjprog() 479 | cli.run() 480 | 481 | 482 | if __name__ == '__main__': 483 | main() 484 | -------------------------------------------------------------------------------- /nrfjprog/model/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016, Nordic Semiconductor 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # 7 | # * Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | # 10 | # * Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # 14 | # * Neither the name of Nordic Semiconductor ASA nor the names of its 15 | # contributors may be used to endorse or promote products derived from 16 | # this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | """Package marker file.""" 30 | -------------------------------------------------------------------------------- /nrfjprog/model/device.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016, Nordic Semiconductor 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # 7 | # * Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | # 10 | # * Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # 14 | # * Neither the name of Nordic Semiconductor ASA nor the names of its 15 | # contributors may be used to endorse or promote products derived from 16 | # this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | """ 30 | Device specific info. 31 | 32 | """ 33 | 34 | FLASH_SIZE = {'NRF52_FP1': 0x80000, 35 | 'NRF52_FP1_ENGB': 0x80000, 36 | 'NRF52_FP1_ENGA': 0x80000, 37 | 'NRF51_XLR3LC': 0x40000, 38 | 'NRF51_XLR3P': 0x40000, 39 | 'NRF51_L3': 0x20000, 40 | 'NRF51_XLR3': 0x40000, 41 | 'NRF51_XLR2': 0x40000, 42 | 'NRF51_XLR1': 0x40000} 43 | 44 | RAM_SIZE = {'NRF52_FP1': 0x10000, 45 | 'NRF52_FP1_ENGB': 0x8000, 46 | 'NRF52_FP1_ENGA': 0x4000, 47 | 'NRF51_XLR3LC': 0x4000, 48 | 'NRF51_XLR3P': 0x8000, 49 | 'NRF51_L3': 0x4000, 50 | 'NRF51_XLR3': 0x4000, 51 | 'NRF51_XLR2': 0x4000, 52 | 'NRF51_XLR1': 0x4000} 53 | 54 | PAGE_SIZE = {'NRF52_FP1': 0x1000, 55 | 'NRF52_FP1_ENGB': 0x1000, 56 | 'NRF52_FP1_ENGA': 0x1000, 57 | 'NRF51_XLR3LC': 0x400, 58 | 'NRF51_XLR3P': 0x400, 59 | 'NRF51_L3': 0x400, 60 | 'NRF51_XLR3': 0x400, 61 | 'NRF51_XLR2': 0x400, 62 | 'NRF51_XLR1': 0x400} 63 | 64 | 65 | class NRF5xDevice(object): 66 | """ 67 | Class representing an nRF5x device. 68 | 69 | """ 70 | 71 | flash_start = 0x0 72 | ram_start = 0x20000000 73 | ficr_start = 0x10000000 74 | uicr_start = 0x10001000 75 | 76 | def __init__(self, device_version): 77 | """ 78 | Initialize the device specific specs. 79 | 80 | """ 81 | self.flash_size = FLASH_SIZE[device_version] 82 | self.ram_size = RAM_SIZE[device_version] 83 | self.page_size = PAGE_SIZE[device_version] 84 | 85 | self.flash_end = self.flash_start + self.flash_size 86 | self.ram_end = self.ram_start + self.ram_size 87 | self.ficr_end = self.ficr_start + self.page_size 88 | self.uicr_end = self.uicr_start + self.page_size 89 | 90 | self.number_of_flash_pages_in_code = self.flash_size / self.page_size 91 | -------------------------------------------------------------------------------- /nrfjprog/model/perform_command.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016, Nordic Semiconductor 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # 7 | # * Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | # 10 | # * Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # 14 | # * Neither the name of Nordic Semiconductor ASA nor the names of its 15 | # contributors may be used to endorse or promote products derived from 16 | # this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | """ 30 | This module receives user input from __main__.py and performs the operation via JLink (pynrfjprog), DAP-Link/CMSIS-DAP (pyOCD) or openOCD. 31 | 32 | """ 33 | 34 | 35 | class PerformCommand(object): 36 | """Base class.""" 37 | 38 | def byte_lists_equal(self, data, read_data): 39 | """Return True if two lists of bytes are identical""" 40 | for i in xrange(len(data)): 41 | if data[i] != read_data[i]: 42 | return False 43 | return True 44 | 45 | def is_flash_addr(self, addr, device): 46 | """Return True if addr is in a FLASH region, False if not.""" 47 | return addr in range( 48 | device.flash_start, 49 | device.flash_end) or addr in range( 50 | device.uicr_start, 51 | device.uicr_end) 52 | 53 | def log(self, args, msg): 54 | """Print log to stdout unless user has specified --quiet.""" 55 | if args.quiet: 56 | pass 57 | else: 58 | print(msg) 59 | 60 | def output_data(self, addr, byte_array, file=None): 61 | """Read data from memory and output it to the console or file with the following format: ADDRESS: WORD\n""" 62 | index = 0 63 | 64 | while index < len(byte_array): 65 | string = "{}: {}".format(hex(addr), byte_array[index: index + 4]) 66 | if file: 67 | file.write(string + '\n') 68 | else: 69 | print(string) 70 | addr = addr + 4 71 | index = index + 4 72 | -------------------------------------------------------------------------------- /nrfjprog/model/perform_command_daplink.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016, Nordic Semiconductor 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # 7 | # * Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | # 10 | # * Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # 14 | # * Neither the name of Nordic Semiconductor ASA nor the names of its 15 | # contributors may be used to endorse or promote products derived from 16 | # this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | import enum 30 | from intelhex import IntelHex 31 | import os 32 | from pyOCD.board import MbedBoard 33 | from pyOCD.coresight import cortex_m 34 | 35 | from nrfjprog import nrfjprog_version 36 | from nrfjprog.model import device 37 | from nrfjprog.model.perform_command import PerformCommand 38 | 39 | 40 | @enum.unique 41 | class Memory_Access_Mode(enum.IntEnum): 42 | READ_ENABLE = 0 43 | WRITE_ENABLE = 1 44 | ERASE_ENABLE = 2 45 | 46 | 47 | class DapLink(PerformCommand): 48 | """ 49 | 50 | """ 51 | 52 | def erase(self, args): 53 | board = self._setup() 54 | board.flash.init() 55 | 56 | NVMC_ERASEUICR_ADDR = 0x4001E514 57 | 58 | if args.erasepage: 59 | board.flash.erasePage(args.erasepage) 60 | elif args.eraseuicr: 61 | self._erase_uicr(board.target) 62 | else: 63 | board.flash.eraseAll() 64 | 65 | def halt(self, args): 66 | board = self._setup() 67 | board.target.halt() 68 | 69 | def ids(self, args): 70 | MbedBoard.listConnectedBoards() 71 | 72 | def memrd(self, args): 73 | board = self._setup() 74 | data = board.target.readBlockMemoryUnaligned8(args.addr, args.length) 75 | self.output_data(args.addr, data) 76 | 77 | def memwr(self, args): 78 | board = self._setup() 79 | 80 | # TODO: This should not be hard-coded. 81 | nRF5_device = device.NRF5xDevice('NRF52_FP1') 82 | 83 | if self.is_flash_addr(args.addr, nRF5_device): 84 | self._config_NVMC(board.target, Memory_Access_Mode.WRITE_ENABLE) 85 | board.target.write32(args.addr, args.val) 86 | self._config_NVMC(board.target, Memory_Access_Mode.READ_ENABLE) 87 | else: 88 | board.target.write32(args.addr, args.val) 89 | 90 | def pinresetenable(self, args): 91 | board = self._setup() 92 | assert(board.getTargetType() is 'nrf52') 93 | 94 | self._config_NVMC(board.target, Memory_Access_Mode.WRITE_ENABLE) 95 | 96 | uicr_pselreset0_addr = 0x10001200 97 | uicr_pselreset1_addr = 0x10001204 98 | # Writes the CONNECT and PIN bit fields (reset is connected and GPIO 99 | # pin 21 is selected as the reset pin). 100 | uicr_pselreset_21_connect = 0x15 101 | 102 | board.target.write32(uicr_pselreset0_addr, uicr_pselreset_21_connect) 103 | board.target.write32(uicr_pselreset1_addr, uicr_pselreset_21_connect) 104 | 105 | self._config_NVMC(board.target, Memory_Access_Mode.READ_ENABLE) 106 | 107 | board.target.reset() 108 | 109 | def program(self, args): 110 | board = self._setup() 111 | board.flash.init() 112 | 113 | tmp_bin_file = 'tmp.bin' 114 | 115 | if args.sectorsanduicrerase: 116 | # TODO: May not be needed if pyOCD does this. Double check before 117 | # removing. 118 | self._erase_uicr(board.target) 119 | 120 | hex_file = IntelHex(args.file) 121 | hex_file.tobinfile(tmp_bin_file) 122 | board.flash.flashBinary( 123 | tmp_bin_file, 124 | chip_erase=args.eraseall, 125 | fast_verify=args.verify) 126 | 127 | if args.debugreset or args.pinreset or args.systemreset: 128 | board.target.reset() 129 | 130 | os.remove(tmp_bin_file) 131 | 132 | def rbp(self, args): 133 | print('Not implemented in nrfjprog when using pyOCD.') 134 | 135 | def readregs(self, args): 136 | board = self._setup() 137 | 138 | for reg in cortex_m.CORE_REGISTER: 139 | if cortex_m.CORE_REGISTER[reg] in range(0, 16): 140 | print(board.target.readCoreRegister(reg)) 141 | 142 | def readtofile(self, args): 143 | board = self._setup() 144 | # TODO: This should not be hard-coded. 145 | nRF5_device = device.NRF5xDevice('NRF52_FP1') 146 | 147 | try: 148 | with open(args.file, 'w') as file: 149 | if args.readcode or not (args.readuicr or args.readram): 150 | file.write('----------Code FLASH----------\n\n') 151 | self.output_data( 152 | nRF5_device.flash_start, 153 | board.target.readBlockMemoryAligned32( 154 | nRF5_device.flash_start, 155 | nRF5_device.flash_size), 156 | file) 157 | file.write('\n\n') 158 | if args.readuicr: 159 | file.write('----------UICR----------\n\n') 160 | self.output_data( 161 | nRF5_device.uicr_start, 162 | board.target.readBlockMemoryAligned32( 163 | nRF5_device.uicr_start, 164 | nRF5_device.page_size), 165 | file) 166 | file.write('\n\n') 167 | if args.readram: 168 | file.write('----------RAM----------\n\n') 169 | self.output_data( 170 | nRF5_device.ram_start, 171 | board.target.readBlockMemoryAligned32( 172 | nRF5_device.ram_start, 173 | nRF5_device.ram_size), 174 | file) 175 | except IOError as error: 176 | pass # TODO: do something... 177 | 178 | def recover(self, args): 179 | print('WARNING: This will not actually unlock the chip right now, just does a full erase.') 180 | board = self._setup() 181 | 182 | board.flash.init() 183 | board.flash.eraseAll() 184 | 185 | def reset(self, args): 186 | board = self._setup() 187 | board.target.reset() 188 | 189 | def run(self, args): 190 | board = self._setup() 191 | board.target.resume() 192 | 193 | def verify(self, args): 194 | board = self._setup() 195 | 196 | hex_file = IntelHex(args.file) 197 | for segment in hex_file.segments(): 198 | start_addr, end_addr = segment 199 | size = end_addr - start_addr 200 | 201 | data = hex_file.tobinarray(start=start_addr, size=size) 202 | read_data = board.target.readBlockMemoryUnaligned8( 203 | start_addr, size) 204 | 205 | assert (self.byte_lists_equal(data, read_data) 206 | ), 'Verify failed. Data readback from memory does not match data written.' 207 | 208 | def version(self, args): 209 | print('nRFjprog version: {}'.format(nrfjprog_version.NRFJPROG_VERSION)) 210 | 211 | # Helpers. 212 | 213 | def _config_NVMC(self, target, access_mode): 214 | """ 215 | Configure the NVMC to read, write, or erase FLASH. 216 | 217 | """ 218 | NVMC_CONFIG_ADDR = 0x4001E504 219 | target.write32(NVMC_CONFIG_ADDR, access_mode) 220 | 221 | def _erase_uicr(self, target): 222 | self._config_NVMC(target, Memory_Access_Mode.ERASE_ENABLE) 223 | target.write32(NVMC_ERASEUICR_ADDR, 1) 224 | self._config_NVMC(target, Memory_Access_Mode.READ_ENABLE) 225 | 226 | def _setup(self): 227 | board = MbedBoard.chooseBoard() 228 | return board 229 | -------------------------------------------------------------------------------- /nrfjprog/model/perform_command_jlink.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016, Nordic Semiconductor 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # 7 | # * Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | # 10 | # * Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # 14 | # * Neither the name of Nordic Semiconductor ASA nor the names of its 15 | # contributors may be used to endorse or promote products derived from 16 | # this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | from pynrfjprog import API 30 | 31 | from nrfjprog import nrfjprog_version 32 | from nrfjprog.model import device 33 | from nrfjprog.model.perform_command import PerformCommand 34 | 35 | 36 | class SetupCommand(object): 37 | """Class that handles the pynrfjprog api instance, some shared arguments, and logging.""" 38 | 39 | DEFAULT_JLINK_SPEED_KHZ = 5000 40 | 41 | def __init__(self, args, do_not_initialize_api=False): 42 | """ 43 | Initialize the class's properties, sets up the connection to our target device, and configures some logging options. 44 | 45 | :param Object args: Arguments the command was called with. 46 | :param bool do_not_initialize_api: If api should be initialized (the connection to the target device should be set up - a command may not need to connect to the target device). 47 | """ 48 | self.args = args 49 | self.api = None 50 | self.device = None 51 | self.device_version = None 52 | 53 | if not do_not_initialize_api: 54 | if self._setup('NRF52'): 55 | pass 56 | elif self._setup('NRF51'): 57 | pass 58 | else: 59 | assert(False), 'Unknown device family.' 60 | 61 | def cleanup(self): 62 | """ 63 | Disconnect from the emulator (debugger) and close the pynrfjprog api instance. 64 | 65 | """ 66 | self.api.disconnect_from_emu() 67 | self.api.close() 68 | self.api = None 69 | self.device = None 70 | self.device_version = None 71 | 72 | def connect_to_emu(self, api): 73 | """ 74 | This method should only be called when this class is created with the do_not_initialize_api flag (i.e. called by recover()). 75 | 76 | :param API api: An instance of api that has been initialized by the caller. 77 | """ 78 | assert ( 79 | self.api is None), "The class's api property has already been initialized." 80 | self.api = api 81 | self._connect_to_emu() 82 | 83 | def _connect_to_emu(self): 84 | """ 85 | Connect to the emulator (debugger) with the specific serial number and/or clock speed if either was specified in the command-line arguments. 86 | 87 | """ 88 | if self.args.snr and self.args.clockspeed: 89 | self.api.connect_to_emu_with_snr( 90 | self.args.snr, self.args.clockspeed) 91 | elif self.args.snr: 92 | self.api.connect_to_emu_with_snr( 93 | self.args.snr, self.DEFAULT_JLINK_SPEED_KHZ) 94 | elif self.args.clockspeed: 95 | self.api.connect_to_emu_without_snr(self.args.clockspeed) 96 | else: 97 | self.api.connect_to_emu_without_snr(self.DEFAULT_JLINK_SPEED_KHZ) 98 | 99 | def _setup(self, device_family_guess): 100 | """ 101 | Connect to target device and check if device_family_guess is correct. If correct, initialize api and device_version and return True. Else, cleanup and return False. 102 | 103 | :param String device_family_guess: The device family type to try. 104 | :return Boolean: If device_family_guess was correct and we initialized everything successfully. 105 | """ 106 | if self.args.jlink_arm_dll_path: 107 | self.api = API.API(device_family_guess, 108 | jlink_arm_dll_path=self.args.jlink_arm_dll_path) 109 | else: 110 | self.api = API.API(device_family_guess) 111 | self.api.open() 112 | self._connect_to_emu() 113 | 114 | if not self.args.deviceversion: 115 | try: 116 | self.device_version = self.api.read_device_version() 117 | except API.APIError as error: 118 | if error.err_code == API.NrfjprogdllErr.WRONG_FAMILY_FOR_DEVICE: 119 | self.cleanup() 120 | return False 121 | else: 122 | assert(False), 'Error!' 123 | else: 124 | self.device_version = self.args.deviceversion 125 | 126 | self.device = device.NRF5xDevice(self.device_version) 127 | return True 128 | 129 | 130 | class JLink(PerformCommand): 131 | """ 132 | 133 | """ 134 | 135 | def erase(self, args): 136 | nrf = SetupCommand(args) 137 | 138 | if args.erasepage: 139 | nrf.api.erase_page(args.erasepage) 140 | elif args.eraseuicr: 141 | nrf.api.erase_uicr() 142 | else: 143 | nrf.api.erase_all() 144 | 145 | nrf.cleanup() 146 | 147 | def halt(self, args): 148 | nrf = SetupCommand(args) 149 | 150 | nrf.api.halt() 151 | 152 | nrf.cleanup() 153 | 154 | def ids(self, args): 155 | nrf = SetupCommand(args, do_not_initialize_api=True) 156 | 157 | # Device family type arbitrary since we are not connecting to a device. 158 | # Use NRF52 by default. 159 | api = API.API('NRF52') 160 | api.open() 161 | 162 | ids = api.enum_emu_snr() 163 | if ids: 164 | print(sorted(ids)) 165 | 166 | api.close() 167 | 168 | def memrd(self, args): 169 | nrf = SetupCommand(args) 170 | 171 | data = nrf.api.read(args.addr, args.length) 172 | self.output_data(args.addr, data) 173 | 174 | nrf.cleanup() 175 | 176 | def memwr(self, args): 177 | nrf = SetupCommand(args) 178 | 179 | nrf.api.write_u32( 180 | args.addr, 181 | args.val, 182 | self.is_flash_addr( 183 | args.addr, 184 | nrf.device)) 185 | 186 | nrf.cleanup() 187 | 188 | def pinresetenable(self, args): 189 | nrf = SetupCommand(args) 190 | 191 | assert( 192 | nrf.device_version[ 193 | :5] != 'NRF51'), "Enabling pin reset is not a valid command for nRF51 devices." 194 | 195 | uicr_pselreset0_addr = 0x10001200 196 | uicr_pselreset1_addr = 0x10001204 197 | # Writes the CONNECT and PIN bit fields (reset is connected and GPIO 198 | # pin 21 is selected as the reset pin). 199 | uicr_pselreset_21_connect = 0x15 200 | 201 | nrf.api.write_u32( 202 | uicr_pselreset0_addr, 203 | uicr_pselreset_21_connect, 204 | True) 205 | nrf.api.write_u32( 206 | uicr_pselreset1_addr, 207 | uicr_pselreset_21_connect, 208 | True) 209 | nrf.api.sys_reset() 210 | 211 | nrf.cleanup() 212 | 213 | def program(self, args): 214 | from intelhex import IntelHex 215 | nrf = SetupCommand(args) 216 | 217 | if args.eraseall: 218 | nrf.api.erase_all() 219 | if args.sectorsanduicrerase: 220 | nrf.api.erase_uicr() 221 | 222 | hex_file = IntelHex(args.file) 223 | for segment in hex_file.segments(): 224 | start_addr, end_addr = segment 225 | size = end_addr - start_addr 226 | 227 | if args.sectorserase or args.sectorsanduicrerase: 228 | start_page = int(start_addr / nrf.device.page_size) 229 | end_page = int(end_addr / nrf.device.page_size) 230 | for page in range(start_page, end_page + 1): 231 | nrf.api.erase_page(page * nrf.device.page_size) 232 | 233 | data = hex_file.tobinarray(start=start_addr, size=(size)) 234 | nrf.api.write(start_addr, data.tolist(), True) 235 | 236 | if args.verify: 237 | read_data = nrf.api.read(start_addr, len(data)) 238 | assert ( 239 | self.byte_lists_equal( 240 | data, read_data)), 'Verify failed. Data readback from memory does not match data written.' 241 | 242 | self._reset(nrf, args) 243 | 244 | nrf.cleanup() 245 | 246 | def rbp(self, args): 247 | nrf = SetupCommand(args) 248 | 249 | if args.rbplevel == 'CR0': 250 | nrf.api.readback_protect(API.ReadbackProtection.REGION_0) 251 | else: 252 | nrf.api.readback_protect(API.ReadbackProtection.ALL) 253 | 254 | nrf.cleanup() 255 | 256 | def readregs(self, args): 257 | nrf = SetupCommand(args) 258 | 259 | for reg in API.CpuRegister: 260 | print( 261 | '{}: {}'.format( 262 | reg.name, hex( 263 | nrf.api.read_cpu_register(reg)))) 264 | 265 | nrf.cleanup() 266 | 267 | def readtofile(self, args): 268 | nrf = SetupCommand(args) 269 | 270 | try: 271 | with open(args.file, 'w') as file: 272 | if args.readcode or not (args.readuicr or args.readram): 273 | file.write('----------Code FLASH----------\n\n') 274 | self.output_data( 275 | nrf.device.flash_start, 276 | nrf.api.read( 277 | nrf.device.flash_start, 278 | nrf.device.flash_size), 279 | file) 280 | file.write('\n\n') 281 | if args.readuicr: 282 | file.write('----------UICR----------\n\n') 283 | self.output_data( 284 | nrf.device.uicr_start, 285 | nrf.api.read( 286 | nrf.device.uicr_start, 287 | nrf.device.page_size), 288 | file) 289 | file.write('\n\n') 290 | if args.readram: 291 | file.write('----------RAM----------\n\n') 292 | self.output_data( 293 | nrf.device.ram_start, nrf.api.read( 294 | nrf.device.ram_start, nrf.device.ram_size), file) 295 | except IOError as error: 296 | print("{}.".format(error)) 297 | 298 | nrf.cleanup() 299 | 300 | def recover(self, args): 301 | nrf = SetupCommand(args, do_not_initialize_api=True) 302 | 303 | api = API.API(args.family) 304 | api.open() 305 | 306 | nrf.connect_to_emu(api) 307 | nrf.api.recover() 308 | 309 | nrf.cleanup() 310 | 311 | def reset(self, args): 312 | nrf = SetupCommand(args) 313 | 314 | self._reset(nrf, args, default_sys_reset=True) 315 | 316 | nrf.cleanup() 317 | 318 | def run(self, args): 319 | nrf = SetupCommand(args) 320 | 321 | if args.pc is not None and args.sp is not None: 322 | nrf.api.run(args.pc, args.sp) 323 | elif args.pc is not None or args.sp is not None: 324 | assert(False), 'Both the PC and the SP must be specified.' 325 | else: 326 | nrf.api.go() 327 | 328 | nrf.cleanup() 329 | 330 | def verify(self, args): 331 | from intelhex import IntelHex 332 | nrf = SetupCommand(args) 333 | 334 | hex_file = IntelHex(args.file) 335 | for segment in hex_file.segments(): 336 | start_addr, end_addr = segment 337 | size = end_addr - start_addr 338 | 339 | data = hex_file.tobinarray(start=start_addr, size=size) 340 | read_data = nrf.api.read(start_addr, size) 341 | 342 | assert (self.byte_lists_equal(data, read_data) 343 | ), 'Verify failed. Data readback from memory does not match data written.' 344 | 345 | nrf.cleanup() 346 | 347 | def version(self, args): 348 | nrf = SetupCommand(args, do_not_initialize_api=True) 349 | 350 | api = API.API('NRF52') 351 | api.open() 352 | 353 | jlink_arm_dll_version = api.dll_version() 354 | print('JLink version: {}'.format(jlink_arm_dll_version)) 355 | print('nRFjprog version: {}'.format(nrfjprog_version.NRFJPROG_VERSION)) 356 | 357 | api.close() 358 | 359 | # Helper functions. 360 | 361 | def _reset(self, nrf, args, default_sys_reset=False): 362 | """ 363 | Reset and run the device. 364 | 365 | """ 366 | if args.debugreset: 367 | nrf.api.debug_reset() 368 | elif args.pinreset: 369 | nrf.api.pin_reset() 370 | elif args.systemreset or default_sys_reset: 371 | nrf.api.sys_reset() 372 | else: 373 | return 374 | 375 | nrf.api.go() 376 | -------------------------------------------------------------------------------- /nrfjprog/model/perform_command_openocd.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016, Nordic Semiconductor 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # 7 | # * Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | # 10 | # * Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # 14 | # * Neither the name of Nordic Semiconductor ASA nor the names of its 15 | # contributors may be used to endorse or promote products derived from 16 | # this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | import subprocess 30 | 31 | from nrfjprog import nrfjprog_version 32 | from nrfjprog.model import device 33 | from nrfjprog.model.perform_command import PerformCommand 34 | 35 | 36 | class OpenOCD(PerformCommand): 37 | """ 38 | Note: Missing some functions and program not working all the time. 39 | 40 | Probably will need --family arugment to determine which target/nrf5x.cfg script to use - until this is shared in openOCD. 41 | """ 42 | 43 | def _create_shell_command(self, command): 44 | return [ 45 | 'sudo', 46 | 'openocd', 47 | '-f', 48 | 'interface/cmsis-dap.cfg', 49 | '-f', 50 | 'target/nrf52.cfg', 51 | '-c', 52 | 'init', 53 | '-c', 54 | command, 55 | '-c', 56 | 'exit'] 57 | 58 | def erase(self, args): 59 | command = 'nrf52 mass_erase' 60 | shell_command = self._create_shell_command(command) 61 | subprocess.check_call( 62 | shell_command, 63 | stdin=None, 64 | stdout=None, 65 | stderr=None, 66 | shell=False) 67 | 68 | def halt(self, args): 69 | command = 'halt' 70 | shell_command = self._create_shell_command(command) 71 | subprocess.check_call( 72 | shell_command, 73 | stdin=None, 74 | stdout=None, 75 | stderr=None, 76 | shell=False) 77 | 78 | def ids(self, args): 79 | command = 'targets' 80 | shell_command = self._create_shell_command(command) 81 | subprocess.check_call( 82 | shell_command, 83 | stdin=None, 84 | stdout=None, 85 | stderr=None, 86 | shell=False) 87 | 88 | def memrd(self, args): 89 | command = 'mdw ' + str(args.addr) + ' ' + str(args.length) 90 | shell_command = self._create_shell_command(command) 91 | subprocess.check_call( 92 | shell_command, 93 | stdin=None, 94 | stdout=None, 95 | stderr=None, 96 | shell=False) 97 | 98 | def memwr(self, args): 99 | """ 100 | Not working. 101 | 102 | """ 103 | command = 'mww ' + str(args.addr) + ' ' + str(args.val) + ' ' + str(1) 104 | shell_command = self._create_shell_command(command) 105 | subprocess.check_call( 106 | shell_command, 107 | stdin=None, 108 | stdout=None, 109 | stderr=None, 110 | shell=False) 111 | 112 | def program(self, args): 113 | command = 'program ' + args.file + ' verify reset' 114 | shell_command = self._create_shell_command(command) 115 | subprocess.check_call( 116 | shell_command, 117 | stdin=None, 118 | stdout=None, 119 | stderr=None, 120 | shell=False) 121 | 122 | def readregs(self, args): 123 | command = 'reg' 124 | shell_command = self._create_shell_command(command) 125 | subprocess.check_call( 126 | shell_command, 127 | stdin=None, 128 | stdout=None, 129 | stderr=None, 130 | shell=False) 131 | 132 | def reset(self, args): 133 | command = 'reset' 134 | shell_command = self._create_shell_command(command) 135 | subprocess.check_call( 136 | shell_command, 137 | stdin=None, 138 | stdout=None, 139 | stderr=None, 140 | shell=False) 141 | 142 | def run(self, args): 143 | command = 'resume ' + str(args.pc) 144 | shell_command = self._create_shell_command(command) 145 | subprocess.check_call( 146 | shell_command, 147 | stdin=None, 148 | stdout=None, 149 | stderr=None, 150 | shell=False) 151 | 152 | def version(self, args): 153 | print('nRFjprog version: {}'.format(nrfjprog_version.NRFJPROG_VERSION)) 154 | command = 'version' 155 | shell_command = self._create_shell_command(command) 156 | subprocess.check_call( 157 | shell_command, 158 | stdin=None, 159 | stdout=None, 160 | stderr=None, 161 | shell=False) 162 | -------------------------------------------------------------------------------- /nrfjprog/nrfjprog_version.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016, Nordic Semiconductor 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # 7 | # * Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | # 10 | # * Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # 14 | # * Neither the name of Nordic Semiconductor ASA nor the names of its 15 | # contributors may be used to endorse or promote products derived from 16 | # this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | """ 30 | Version definition for nrfjprog. 31 | 32 | """ 33 | 34 | NRFJPROG_VERSION = "0.3.0" 35 | -------------------------------------------------------------------------------- /nrfjprog_cli.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016, Nordic Semiconductor 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # 7 | # * Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | # 10 | # * Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # 14 | # * Neither the name of Nordic Semiconductor ASA nor the names of its 15 | # contributors may be used to endorse or promote products derived from 16 | # this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | """ 30 | This module is located outside the nrfjprog package and allows PyInstaller to properly build nrfjprog into an .exe by running $ pyinstaller nrfjprog_cli.py --clean --name nrfjprog 31 | 32 | Users can also call the nrfjprog command-line-tool from Python by running $ python nrfjprog_cli.py --help 33 | """ 34 | 35 | from nrfjprog.__main__ import main 36 | 37 | 38 | if __name__ == '__main__': 39 | main() 40 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | argparse 2 | intelhex 3 | pynrfjprog 4 | pyocd 5 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016, Nordic Semiconductor 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # 7 | # * Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | # 10 | # * Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # 14 | # * Neither the name of Nordic Semiconductor ASA nor the names of its 15 | # contributors may be used to endorse or promote products derived from 16 | # this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | """ 30 | Setup script for nrfjprog. 31 | 32 | USAGE: 33 | python setup.py install or python setup.py bdist_egg (to create a Python egg) 34 | """ 35 | 36 | #import fnmatch 37 | import os 38 | from setuptools import setup, find_packages 39 | #import subprocess 40 | import sys 41 | 42 | from nrfjprog import nrfjprog_version 43 | 44 | 45 | def read(fname): 46 | return open(os.path.join(os.path.dirname(__file__), fname)).read() 47 | 48 | 49 | def read_requirements(fname): 50 | return open(os.path.join(os.path.dirname(__file__), fname)).readlines() 51 | 52 | 53 | setup( 54 | name='nRF5-universal-prog', 55 | version=nrfjprog_version.NRFJPROG_VERSION, 56 | description='The nRF5-universal-prog command line tool implemented in Python.', 57 | long_description=read('README.md'), 58 | url='https://github.com/NordicPlayground/nRF5-universal-prog', 59 | author='Nordic Semiconductor ASA', 60 | license='BSD', 61 | classifiers=[ 62 | 'Development Status :: 3 - Alpha', 63 | 'Intended Audience :: Developers', 64 | 'Operating System :: MacOS', 65 | 'Operating System :: Microsoft :: Windows', 66 | 'Operating System :: POSIX :: Linux', 67 | 'Topic :: Software Development :: Build Tools', 68 | 'Topic :: Software Development :: Debuggers', 69 | 'Topic :: Software Development :: Embedded Systems', 70 | 'License :: OSI Approved :: BSD License', 71 | 'Programming Language :: Python :: 2.7', 72 | 'Programming Language :: Python :: 3.4', 73 | 'Programming Language :: Python :: 3.5' 74 | ], 75 | keywords='nRF5 nRF51 nRF52 nrfjprog pynrfjprog pyOCD Nordic Semiconductor SEGGER JLink', 76 | install_requires=read_requirements('requirements.txt'), 77 | packages=find_packages(exclude=["tests.*", "tests"]), 78 | include_package_data=False 79 | ) 80 | 81 | """if __name__ == '__main__': 82 | print('#### Auto formatting all Python code in nRFTools according to PEP 8...') 83 | matches = [] 84 | for root, dirnames, filenames in os.walk( 85 | os.path.dirname(os.path.realpath(__file__))): 86 | for filename in fnmatch.filter(filenames, '*.py'): 87 | matches.append(os.path.join(root, filename)) 88 | for match in matches: 89 | subprocess.check_call( 90 | ["autopep8", "--in-place", "--aggressive", "--aggressive", match])""" 91 | -------------------------------------------------------------------------------- /tests/unit_tests.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016, Nordic Semiconductor 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # 7 | # * Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | # 10 | # * Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # 14 | # * Neither the name of Nordic Semiconductor ASA nor the names of its 15 | # contributors may be used to endorse or promote products derived from 16 | # this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | """ 30 | Test nrfjprog.exe that was built from our repo. 31 | 32 | Must add tests/dist/SYSTEM_OS on system running this script where it contains the built .exe and all it's dependencies. 33 | """ 34 | 35 | import subprocess 36 | import sys 37 | import unittest 38 | 39 | from pynrfjprog import API 40 | 41 | 42 | if sys.platform.lower().startswith('win'): 43 | PATH_TO_EXE = "dist\\windows_64\\nrfjprog.exe" 44 | elif sys.platform.lower().startswith('linux'): 45 | PATH_TO_EXE = "dist/linux_64/nrfjprog" 46 | elif sys.platform.lower().startswith('dar'): 47 | PATH_TO_EXE = "dist/mac_osx_64/nrfjprog" 48 | 49 | 50 | def run_exe(cmd): 51 | """ 52 | Run nrfjprog with the given commands. 53 | 54 | :param List cmd: Commands to run nrfjprog with. 55 | """ 56 | command = [] 57 | command.append(PATH_TO_EXE) 58 | command.extend(cmd) 59 | return subprocess.call( 60 | command, 61 | stdout=subprocess.PIPE, 62 | stderr=subprocess.PIPE) 63 | 64 | 65 | def setup_api(): 66 | """ 67 | Initialize api and connect to the target device. 68 | 69 | :return Object api: Instance of API that is initialized and connected to the target device, ready to be used. 70 | """ 71 | api = API.API('NRF52') # TODO: Should not be hard coded. 72 | api.open() 73 | api.connect_to_emu_without_snr() # TODO: Should have the option for snr. 74 | return api 75 | 76 | 77 | def cleanup_api(api): 78 | api.disconnect_from_emu() 79 | api.close() 80 | api = None 81 | 82 | 83 | class TestBaseClass(unittest.TestCase): 84 | """ 85 | Base class that does the common setup/tear down to parent all specific test cases. 86 | 87 | """ 88 | 89 | @classmethod 90 | def setUpClass(cls): 91 | cls.api = setup_api() 92 | 93 | @classmethod 94 | def tearDownClass(cls): 95 | cleanup_api(cls.api) 96 | 97 | def setUp(self): 98 | self.api.recover() 99 | 100 | def tearDown(self): 101 | pass 102 | 103 | 104 | class TestHelpScreens(TestBaseClass): 105 | """ 106 | Tests to verify nrfjprog runs. 107 | 108 | """ 109 | 110 | def test_help(self): 111 | self.assertTrue(run_exe(["-h"]) == 0) 112 | 113 | 114 | class TestEraseCommand(TestBaseClass): 115 | """ 116 | Tests to verify the erase command and it's arguments. 117 | 118 | """ 119 | 120 | def test_erase_help(self): 121 | self.assertTrue(run_exe(["erase", "-h"]) == 0) 122 | 123 | # Not a good unit test, just demonstrating using pynrfjprog. 124 | def test_erase(self): 125 | self.api.write_u32(0x0, 0x0, True) 126 | run_exe(["erase"]) 127 | self.assertTrue(self.api.read_u32(0x0) == 0xFFFFFFFF) 128 | 129 | 130 | class TestProgramCommand(TestBaseClass): 131 | """ 132 | Tests to verify the program command and it's arguments. 133 | 134 | """ 135 | 136 | def test_program_help(self): 137 | self.assertTrue(run_exe(["program", "-h"]) == 0) 138 | 139 | # Not a good unit test, just demonstrating resources\. 140 | def test_program(self): 141 | self.assertTrue(run_exe( 142 | ["program", "-f", "resources\\ble_app_hrs_s132_with_dfu_pca10040.hex"]) == 0) 143 | self.assertTrue(run_exe( 144 | ["verify", "-f", "resources\\ble_app_hrs_s132_with_dfu_pca10040.hex"]) == 0) 145 | 146 | 147 | if __name__ == '__main__': 148 | """ 149 | Run the tests with specified options. 150 | 151 | """ 152 | unittest.main( 153 | verbosity=2) # TODO: Run tests in a way where specific Test Cases can be run or the entire suite. 154 | --------------------------------------------------------------------------------