pynrfjprog erase_all stops erasing flash memory

I have written my own python script that will be used to program devices with some additional data placed in UICR registers like public MAC address, passkey and serial number.

I'm using pynrfjprog low level api and everything seems to working fine to the point when dfu bootloader and soft device merged hex file is flashed.

At first try script works as expected but at every subsequent flashing of the same device api.erase_all() seems to not erase the memory anymore. There is no error from the method call, self implemented verification of the flash memory contents and UICR stored data fails. I also checked device contents just after calling erase all and part is not empty.

I have to erase memory using ses or nrf connect programmer app to make script and api.erase_all() working again, until flashing bootloader merged hex file which blocks this function somehow.

I'm using JLink base programmer, also tested this behavior on U-Blox Nina evaluation board which has JLink included on board.

import argparse
import os
import re
import struct

from math import ceil

try:
    from .. import LowLevel, Hex
except Exception:
    from pynrfjprog import LowLevel, Hex

# UICR register addresses
UICR_MAC_ADDR_U32_1 =    0x10001080  # CUSTOMER[0]
UICR_MAC_ADDR_U32_2 =    0x10001084  # CUSTOMER[1]
UICR_PASSKEY_U32_1 =     0x10001088  # CUSTOMER[2]
UICR_PASSKEY_U32_2 =     0x1000108C  # CUSTOMER[3]
UICR_SERIAL_U32_1 =      0x10001090  # CUSTOMER[4]
UICR_SERIAL_U32_2 =      0x10001094  # CUSTOMER[5]
UICR_SERIAL_U32_3 =      0x10001098  # CUSTOMER[6]

def str_to_u32_list(str):
    u32_list = []

    size = 4 * ceil(len(str) / 4)

    ba = bytearray(b'\xFF' * size)

    for i, b in enumerate(str.encode()):
        ba[i] = b

    for i in range(0, len(ba), 4):
        u32_list.append(int.from_bytes(ba[i:i + 4], 'little', signed=False))

    return u32_list

# Parse command-line arguments
parser = argparse.ArgumentParser()

parser.add_argument("-f", "--hex_file", required=True, help="Path to firmware hex file")
parser.add_argument("-k", "--passkey", required=False, help="Static pairing passkey")
parser.add_argument("-s", "--serial_number", required=False, help="Device serial number")
parser.add_argument("-m", "--mac_addr", required=False, help="MAC address to be programmed")
parser.add_argument("-j", "--jlink_snr", required=False, help="Serial number of the Jlink to be used")
parser.add_argument("-v", "--verify", action="store_true", required=False, help="Verify flash memory contents")

args = parser.parse_args()

try:
    api = LowLevel.API(LowLevel.DeviceFamily.NRF52)

    api.open()

    if args.jlink_snr is not None:
        api.connect_to_emu_with_snr(args.jlink_snr)
    else:
        api.connect_to_emu_without_snr()

    # Get the MAC address
    mac_addr_u32_l = 0
    mac_addr_u32_h = 0
    if args.mac_addr is None:
        # Get the MAC address stored in UICR
        print('Reading MAC address from UICR')

        mac_addr_u32_l = api.read_u32(UICR_MAC_ADDR_U32_1)
        mac_addr_u32_h = api.read_u32(UICR_MAC_ADDR_U32_2)
    elif re.match("[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", args.mac_addr.lower()):
        mac_addr_str = args.mac_addr.translate(str.maketrans('', '', ':.- '))
        mac_addr = int(mac_addr_str, 16)

        mac_addr_u32_h = (mac_addr >> 32) & 0xFFFFFFFF
        mac_addr_u32_l = mac_addr & 0xFFFFFFFF

    if mac_addr_u32_l != 0 and mac_addr_u32_h != 0:
        mac_addr = (struct.unpack("4B", struct.pack(">L", mac_addr_u32_h)) + 
                    struct.unpack("4B", struct.pack(">L", mac_addr_u32_l)))
        mac_addr = mac_addr[2:]

        if all((byte == 0xFF or byte == 0x00) for byte in mac_addr):
            mac_addr = []

    # Convert passkey and serial number ASCII string to u32 int value lists
    passkey_u32_list = []
    serial_u32_list = []

    if args.passkey is not None:
        if len(args.passkey) != 6:
            raise ValueError('Passkey must be 6-digit long')

        passkey_u32_list = str_to_u32_list(args.passkey)

    if args.serial_number is not None:
        if len(args.serial_number) > 12:
            raise ValueError('Serial number maximum size is 12 chars')

        serial_u32_list = str_to_u32_list(args.serial_number)
    if not os.path.isfile(args.hex_file):
        print('%s is not a file' % args.hex_file)
    else:
        print('Erasing all flash memory')

        api.erase_all()

        # Parse and program hex into device's memory
        program = Hex.Hex(args.hex_file)

        print('Writing %s to device' % args.hex_file)
        for segment in program:
            api.write(segment.address, segment.data, True)

        if args.verify:
            print('Veryfing')
            for segment in program:
                if not segment.data == api.read(segment.address, len(segment.data)):
                    print('Segment data mismatch at address: %s, length: %s' % (hex(segment.address), hex(len(segment.data))))

        # Set MAC address
        if mac_addr:
            print('Setting MAC address: %02X:%02X:%02X:%02X:%02X:%02X' % mac_addr)
            api.write_u32(UICR_MAC_ADDR_U32_1, mac_addr_u32_l, True)
            api.write_u32(UICR_MAC_ADDR_U32_2, mac_addr_u32_h, True)
        else:
            print('Static random MAC address will be used')

        # Set passkey and serial number in UICR if requested
        if passkey_u32_list:
            print('Setting passkey %s' % args.passkey)
            api.write_u32(UICR_PASSKEY_U32_1, passkey_u32_list[0], True)
            api.write_u32(UICR_PASSKEY_U32_2, passkey_u32_list[1], True)
            if args.verify:
                if not passkey_u32_list[0] == api.read_u32(UICR_PASSKEY_U32_1) and \
                   not passkey_u32_list[1] == api.read_u32(UICR_PASSKEY_U32_2):
                    print('Passkey UICR data mismatch')
        else:
            print('No passkey will be set')

        if serial_u32_list:
            print('Setting serial number %s' % args.serial_number)
            api.write_u32(UICR_SERIAL_U32_1, serial_u32_list[0], True)
            api.write_u32(UICR_SERIAL_U32_2, serial_u32_list[1], True)
            api.write_u32(UICR_SERIAL_U32_3, serial_u32_list[2], True)
        else:
            print('No serial number will be set')

        print('Device reset')
        api.sys_reset()
        api.go()

        print('Done')

        api.close()
except LowLevel.APIError:
    api.close()
    raise
except ValueError as err:
    api.close()
    raise

Parents Reply Children
Related