Add tests for nand commands to test various NAND flash operations such as erase, write and read.
Signed-off-by: Love Kumar <[email protected]> --- Changes in v3: - Use the current test/py APIs (ubman fixture and utils helpers) - Refactor nand_pre_commands() to return a dictionary - Accept multi-chip nand info output (e.g. "Device 0: 2x nand0") - Add Sphinx-rendered documentation for the test - Update copyright year Changes in v2: - Fix the deprecation warning issue for invalid escape sequence --- doc/develop/pytest/test_nand.rst | 8 ++ test/py/tests/test_nand.py | 237 +++++++++++++++++++++++++++++++ 2 files changed, 245 insertions(+) create mode 100644 doc/develop/pytest/test_nand.rst create mode 100644 test/py/tests/test_nand.py diff --git a/doc/develop/pytest/test_nand.rst b/doc/develop/pytest/test_nand.rst new file mode 100644 index 000000000000..7d16ae859e1e --- /dev/null +++ b/doc/develop/pytest/test_nand.rst @@ -0,0 +1,8 @@ +test_nand +========= + +.. automodule:: test_nand + :synopsis: + :member-order: bysource + :members: + :undoc-members: diff --git a/test/py/tests/test_nand.py b/test/py/tests/test_nand.py new file mode 100644 index 000000000000..d6086f0983ef --- /dev/null +++ b/test/py/tests/test_nand.py @@ -0,0 +1,237 @@ +# SPDX-License-Identifier: GPL-2.0 +# (C) Copyright 2023-2026, Advanced Micro Devices, Inc. + +""" +Note: This test relies on boardenv_* containing configuration values to define +the nand device total size and timeout available for testing. Without this, the +test will be automatically skipped. This test will be also skipped if the NAND +flash device is not detected. + +For example: + +.. code-block:: python + + # Setup env__nand_device_test to set the NAND flash total size and timeout. + env__nand_device_test = { + 'size': '8192 MB', + 'timeout': 100000, + } +""" + +import pytest +import random +import re +import utils + +def nand_pre_commands(ubman): + """Probe the NAND flash device and gather geometry from `nand info`. + + Args: + ubman: A U-Boot console connection. + + Returns: + A dictionary with the following keys: + page_size: NAND page size in bytes. + erase_size: NAND erase (sector) size in bytes. + sector_size: NAND erase (sector) size in KiB. + total_size: Usable NAND size in bytes (bad blocks subtracted). + timeout: Timeout in milliseconds for long-running operations. + """ + + f = ubman.config.env.get('env__nand_device_test', None) + if not f: + pytest.skip('No env file to read for NAND device test') + + total_size = f.get('size', None) + timeout = f.get('timeout') + + if not total_size: + pytest.skip('NAND device size not recognized') + + output = ubman.run_command('nand info') + if not re.search(r'Device 0:\s*\S*\s*nand0', output): + pytest.skip('No NAND device available') + + m = re.search('Page size (.+?) b', output) + if not m: + pytest.fail('NAND page size not recognized') + try: + page_size = int(m.group(1)) + except ValueError: + pytest.fail('NAND page size not recognized') + + m = re.search('sector size (.+?) KiB', output) + if not m: + pytest.fail('NAND erase size not recognized') + try: + erase_size = int(m.group(1)) + sector_size = erase_size + except ValueError: + pytest.fail('NAND erase size not recognized') + + erase_size *= 1024 + + output = ubman.run_command('nand bad') + if not 'bad blocks:' in output: + pytest.skip('No NAND device available') + + count = 0 + m = re.search(r'bad blocks:([\n\s\s\d\w]*)', output) + if m: + print(m.group(1)) + var = m.group(1).split() + count = len(var) + print('bad blocks count= ' + str(count)) + + m = re.search('(.+?) MB', total_size) + if not m: + pytest.fail('NAND size not recognized') + try: + total_size = int(m.group(1)) + total_size *= 1024 * 1024 + print('Total size is: ' + str(total_size) + ' B') + total_size -= count * sector_size * 1024 + print('New Total size is: ' + str(total_size) + ' B') + except ValueError: + pytest.fail('NAND size not recognized') + + return { + 'page_size': page_size, + 'erase_size': erase_size, + 'sector_size': sector_size, + 'total_size': total_size, + 'timeout': timeout, + } + [email protected]('cmd_nand') [email protected]('cmd_bdi') [email protected]('cmd_memory') +def test_nand_read_twice(ubman): + """This test reads the whole NAND flash twice, random_size till full flash + size, random till page size. + """ + + nand_params = nand_pre_commands(ubman) + page_size = nand_params['page_size'] + total_size = nand_params['total_size'] + expected_read = 'read: OK' + + for size in ( + random.randint(4, page_size), + random.randint(4, total_size), + total_size, + ): + addr = utils.find_ram_base(ubman) + + output = ubman.run_command( + 'nand read %x 0 %x' % (addr + total_size, size) + ) + assert expected_read in output + + output = ubman.run_command('crc32 %x %x' % (addr + total_size, size)) + m = re.search('==> (.+?)', output) + if not m: + pytest.fail('CRC32 failed') + expected_crc32 = m.group(1) + + output = ubman.run_command( + 'nand read %x 0 %x' % (addr + total_size + 10, size) + ) + assert expected_read in output + + output = ubman.run_command( + 'crc32 %x %x' % (addr + total_size + 10, size) + ) + assert expected_crc32 in output + [email protected]('cmd_nand') [email protected]('cmd_bdi') [email protected]('cmd_memory') +def test_nand_write_twice(ubman): + """This test does the random writes till page size, size and full size""" + + nand_params = nand_pre_commands(ubman) + page_size = nand_params['page_size'] + erase_size = nand_params['erase_size'] + total_size = nand_params['total_size'] + expected_write = 'written: OK' + expected_read = 'read: OK' + expected_erase = '100% complete.' + old_size = 0 + + for size in ( + random.randint(4, page_size), + random.randint(page_size, total_size), + total_size, + ): + offset = page_size + addr = utils.find_ram_base(ubman) + size = size - old_size + output = ubman.run_command('crc32 %x %x' % (addr + total_size, size)) + m = re.search('==> (.+?)', output) + if not m: + pytest.fail('CRC32 failed') + + expected_crc32 = m.group(1) + + if old_size % page_size: + old_size = int(old_size / page_size + 1) + old_size *= page_size + + if old_size + size > total_size: + size = total_size - old_size + + eraseoffset = int(old_size / erase_size) + eraseoffset *= erase_size + + erasesize = int(size / erase_size + 1) + erasesize *= erase_size + + output = ubman.run_command( + 'nand erase.spread %x %x' % (eraseoffset, erasesize) + ) + assert expected_erase in output + + output = ubman.run_command( + 'nand write %x %x %x' % (addr + total_size, old_size, size) + ) + assert expected_write in output + output = ubman.run_command( + 'nand read %x %x %x' % (addr + total_size + offset, old_size, size) + ) + assert expected_read in output + output = ubman.run_command( + 'crc32 %x %x' % (addr + total_size + offset, size) + ) + assert expected_crc32 in output + old_size = size + [email protected]('cmd_nand') +def test_nand_erase_block(ubman): + """Erase the NAND flash one erase block at a time.""" + + nand_params = nand_pre_commands(ubman) + erase_size = nand_params['erase_size'] + total_size = nand_params['total_size'] + + expected_erase = '100% complete.' + for start in range(0, total_size, erase_size): + output = ubman.run_command( + 'nand erase.spread %x %x' % (start, erase_size) + ) + assert expected_erase in output + [email protected]('cmd_nand') +def test_nand_erase_all(ubman): + """Erase the entire NAND flash in a single operation.""" + + nand_params = nand_pre_commands(ubman) + total_size = nand_params['total_size'] + timeout = nand_params['timeout'] + + expected_erase = '100% complete.' + with ubman.temporary_timeout(timeout): + output = ubman.run_command( + 'nand erase.spread 0 ' + str(hex(total_size)) + ) + assert expected_erase in output -- 2.43.0

