Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions all_local_packages.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
-e ./pytest-embedded-jtag/
-e ./pytest-embedded-qemu/
-e ./pytest-embedded-arduino/
-e ./pytest-embedded-nuttx/
10 changes: 7 additions & 3 deletions pytest-embedded-nuttx/pytest_embedded_nuttx/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ def __init__(
super().__init__(**kwargs)

self.file_extension = file_extension
self.app_file, self.bootloader_file, self.merge_file = self._get_bin_files()
self.app_file, self.bootloader_file, self.merge_file, self.vefuse_file = self._get_bin_files()

def _get_bin_files(self) -> tuple[Path | None, Path | None, Path | None]:
def _get_bin_files(self) -> tuple[Path | None, Path | None, Path | None, Path | None]:
"""
Get path to binary files available in the app_path.
If either the application image or bootloader is not found,
Expand All @@ -37,13 +37,17 @@ def _get_bin_files(self) -> tuple[Path | None, Path | None, Path | None]:
search_pattern = '*' + self.file_extension
bin_files = list(search_path.rglob(search_pattern))
app_file, bootloader_file, merge_file = None, None, None
vefuse_file = None

logging.info('Searching %s', str(search_path))
if not bin_files:
logging.warning('No binary files found with pattern: %s', search_pattern)

for file_path in bin_files:
file_name = str(file_path.stem)
if 'vefuse' in file_name:
vefuse_file = file_path
logging.info('Virtual E-Fuse file: %s', vefuse_file.as_posix())
if 'nuttx' in file_name:
if 'merged' in file_name:
merge_file = file_path
Expand All @@ -59,4 +63,4 @@ def _get_bin_files(self) -> tuple[Path | None, Path | None, Path | None]:
logging.error('App file not found: %s', app_file)
print(bin_files)

return app_file, bootloader_file, merge_file
return app_file, bootloader_file, merge_file, vefuse_file
18 changes: 15 additions & 3 deletions pytest-embedded-nuttx/pytest_embedded_nuttx/serial.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ class NuttxSerial(EspSerial):

# Default offset for the primary MCUBoot slot across
# all Espressif devices on NuttX
MCUBOOT_PRIMARY_SLOT_OFFSET = 0x10000
MCUBOOT_PRIMARY_SLOT_OFFSET = 0x20000
VIRTUAL_EFUSE_OFFSET = 0x10000
SERIAL_BAUDRATE = 115200

binary_offsets: ClassVar[dict[str, int]] = {
Expand Down Expand Up @@ -82,13 +83,19 @@ def get_key_from_value(dictionary, val):
self.flash_mode = 'dio'

@EspSerial.use_esptool()
def flash(self) -> None:
def flash(
self,
virtual_efuse_offset: int = VIRTUAL_EFUSE_OFFSET,
mcuboot_primary_slot_offset: int = MCUBOOT_PRIMARY_SLOT_OFFSET,
) -> None:
"""Flash the binary files to the board."""

flash_files = []
if self.app.bootloader_file:
if self.app.vefuse_file:
flash_files.extend((str(virtual_efuse_offset), self.app.vefuse_file.as_posix()))
flash_files.extend((str(self.binary_offsets[self.target]), self.app.bootloader_file.as_posix()))
flash_files.extend((str(self.MCUBOOT_PRIMARY_SLOT_OFFSET), self.app.app_file.as_posix()))
flash_files.extend((str(mcuboot_primary_slot_offset), self.app.app_file.as_posix()))
else:
flash_files.extend((str(self.binary_offsets[self.target]), self.app.app_file.as_posix()))

Expand All @@ -101,6 +108,11 @@ def flash(self) -> None:
self.flash_freq,
]

print(f'Flash files: {flash_files}')
print(f'Flash settings: {flash_settings}')
print(f'Virtual E-Fuse offset: {hex(virtual_efuse_offset)}')
print(f'MCUBoot primary slot offset: {hex(mcuboot_primary_slot_offset)}')

esptool.main(
[
'--chip',
Expand Down
42 changes: 42 additions & 0 deletions pytest-embedded-nuttx/tests/test_nuttx.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def test_nuttx_app_mcuboot(dut, app, target):
assert '4MB' == dut.serial.flash_size
assert 'dio' == dut.serial.flash_mode
assert None != app.bootloader_file
assert None == app.vefuse_file

def test_nuttx_app_mcuboot_flash(serial, dut):
serial.erase_flash()
Expand All @@ -73,6 +74,47 @@ def test_hello_mcuboot(dut):
result.assert_outcomes(passed=3)


def test_nuttx_app_mcuboot_with_virtual_efuse(testdir):
"""This test takes vefuse.bin in consideration. This is a scenario
where the bootloader is flashed with virtual efuse enabled, which
changes the location in flash for MCUBoot and application.
This was added to NuttX on september 2025.
"""
testdir.makepyfile("""
import pytest

def test_nuttx_app_mcuboot(dut, app, target):
assert 'esp32' == target
assert '40m' == dut.serial.flash_freq
assert '4MB' == dut.serial.flash_size
assert 'dio' == dut.serial.flash_mode
assert None != app.bootloader_file
assert None != app.vefuse_file

def test_nuttx_app_mcuboot_flash(serial, dut):
serial.erase_flash()
serial.flash()
dut.reset_to_nsh()

def test_hello_mcuboot(dut):
dut.reset_to_nsh()
dut.write("ls /dev")
dut.expect("console")
""")

result = testdir.runpytest(
'-s',
'--embedded-services',
'nuttx,esp',
'--target',
'esp32',
'--app-path',
os.path.join(testdir.tmpdir, 'hello_world_nuttx_virtual_efuse'),
)

result.assert_outcomes(passed=3)


qemu_bin_required = pytest.mark.skipif(
shutil.which('qemu-system-xtensa') is None,
reason='Please make sure that `qemu-system-xtensa` is in your PATH env var. Build QEMU for ESP32 locally and then '
Expand Down
Binary file modified tests/fixtures/hello_world_nuttx_mcuboot/mcuboot-esp32.bin
Binary file not shown.
Binary file modified tests/fixtures/hello_world_nuttx_mcuboot/nuttx.bin
Binary file not shown.
Binary file not shown.
Binary file not shown.
Empty file.
Loading