mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
ttfw_idf: Added IDFFPGADUT class
Provision to burn and reset efuses of ESP32C3 chip on FPGA
This commit is contained in:
parent
ba3168d1d5
commit
65db2d3d80
@ -296,6 +296,13 @@ class IDFApp(App.BaseApp):
|
||||
d[configs[0]] = configs[1].rstrip()
|
||||
return d
|
||||
|
||||
def get_sdkconfig_config_value(self, config_key): # type: (str) -> Any
|
||||
sdkconfig_dict = self.get_sdkconfig()
|
||||
value = None
|
||||
if (config_key in sdkconfig_dict):
|
||||
value = sdkconfig_dict[config_key]
|
||||
return value
|
||||
|
||||
@abstractmethod
|
||||
def _try_get_binary_from_local_fs(self): # type: () -> Optional[str]
|
||||
pass
|
||||
|
@ -13,6 +13,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
""" DUT for IDF applications """
|
||||
import collections
|
||||
import functools
|
||||
import io
|
||||
import os
|
||||
@ -24,6 +25,7 @@ import tempfile
|
||||
import time
|
||||
|
||||
import pexpect
|
||||
import serial
|
||||
|
||||
# python2 and python3 queue package name is different
|
||||
try:
|
||||
@ -43,6 +45,9 @@ except ImportError: # cheat and use IDF's copy of esptool if available
|
||||
sys.path.insert(0, os.path.join(idf_path, 'components', 'esptool_py', 'esptool'))
|
||||
import esptool
|
||||
|
||||
import espefuse
|
||||
import espsecure
|
||||
|
||||
|
||||
class IDFToolError(OSError):
|
||||
pass
|
||||
@ -122,10 +127,18 @@ def _uses_esptool(func):
|
||||
settings = self.port_inst.get_settings()
|
||||
|
||||
try:
|
||||
if not self._rom_inst:
|
||||
self._rom_inst = esptool.ESPLoader.detect_chip(self.port_inst)
|
||||
self._rom_inst.connect('hard_reset')
|
||||
esp = self._rom_inst.run_stub()
|
||||
if not self.rom_inst:
|
||||
if not self.secure_boot_en:
|
||||
self.rom_inst = esptool.ESPLoader.detect_chip(self.port_inst)
|
||||
else:
|
||||
self.rom_inst = self.get_rom()(self.port_inst)
|
||||
self.rom_inst.connect('hard_reset')
|
||||
|
||||
if (self.secure_boot_en):
|
||||
esp = self.rom_inst
|
||||
esp.flash_spi_attach(0)
|
||||
else:
|
||||
esp = self.rom_inst.run_stub()
|
||||
|
||||
ret = func(self, esp, *args, **kwargs)
|
||||
# do hard reset after use esptool
|
||||
@ -158,10 +171,12 @@ class IDFDUT(DUT.SerialDUT):
|
||||
self.allow_dut_exception = allow_dut_exception
|
||||
self.exceptions = _queue.Queue()
|
||||
self.performance_items = _queue.Queue()
|
||||
self._rom_inst = None
|
||||
self.rom_inst = None
|
||||
self.secure_boot_en = self.app.get_sdkconfig_config_value('CONFIG_SECURE_BOOT') and \
|
||||
not self.app.get_sdkconfig_config_value('CONFIG_EFUSE_VIRTUAL')
|
||||
|
||||
@classmethod
|
||||
def _get_rom(cls):
|
||||
def get_rom(cls):
|
||||
raise NotImplementedError('This is an abstraction class, method not defined.')
|
||||
|
||||
@classmethod
|
||||
@ -175,7 +190,7 @@ class IDFDUT(DUT.SerialDUT):
|
||||
"""
|
||||
esp = None
|
||||
try:
|
||||
esp = cls._get_rom()(port)
|
||||
esp = cls.get_rom()(port)
|
||||
esp.connect()
|
||||
return esp.read_mac()
|
||||
except RuntimeError:
|
||||
@ -190,7 +205,7 @@ class IDFDUT(DUT.SerialDUT):
|
||||
def confirm_dut(cls, port, **kwargs):
|
||||
inst = None
|
||||
try:
|
||||
expected_rom_class = cls._get_rom()
|
||||
expected_rom_class = cls.get_rom()
|
||||
except NotImplementedError:
|
||||
expected_rom_class = None
|
||||
|
||||
@ -258,7 +273,7 @@ class IDFDUT(DUT.SerialDUT):
|
||||
else:
|
||||
encrypt_files.append((address, nvs_file))
|
||||
|
||||
self._write_flash(flash_files, encrypt_files, False, encrypt)
|
||||
self.write_flash_data(flash_files, encrypt_files, False, encrypt)
|
||||
finally:
|
||||
for (_, f) in flash_files:
|
||||
f.close()
|
||||
@ -266,7 +281,7 @@ class IDFDUT(DUT.SerialDUT):
|
||||
f.close()
|
||||
|
||||
@_uses_esptool
|
||||
def _write_flash(self, esp, flash_files=None, encrypt_files=None, ignore_flash_encryption_efuse_setting=True, encrypt=False):
|
||||
def write_flash_data(self, esp, flash_files=None, encrypt_files=None, ignore_flash_encryption_efuse_setting=True, encrypt=False):
|
||||
"""
|
||||
Try flashing at a particular baud rate.
|
||||
|
||||
@ -291,8 +306,8 @@ class IDFDUT(DUT.SerialDUT):
|
||||
'flash_freq': self.app.flash_settings['flash_freq'],
|
||||
'addr_filename': flash_files or None,
|
||||
'encrypt_files': encrypt_files or None,
|
||||
'no_stub': False,
|
||||
'compress': True,
|
||||
'no_stub': self.secure_boot_en,
|
||||
'compress': not self.secure_boot_en,
|
||||
'verify': False,
|
||||
'encrypt': encrypt,
|
||||
'ignore_flash_encryption_efuse_setting': ignore_flash_encryption_efuse_setting,
|
||||
@ -369,7 +384,7 @@ class IDFDUT(DUT.SerialDUT):
|
||||
if encrypt_files:
|
||||
encrypt_offs_files = [(offs, open(path, 'rb')) for (offs, path) in encrypt_files]
|
||||
|
||||
self._write_flash(flash_offs_files, encrypt_offs_files, ignore_flash_encryption_efuse_setting, encrypt)
|
||||
self.write_flash_data(flash_offs_files, encrypt_offs_files, ignore_flash_encryption_efuse_setting, encrypt)
|
||||
finally:
|
||||
for (_, f) in flash_offs_files:
|
||||
f.close()
|
||||
@ -562,7 +577,7 @@ class ESP32DUT(IDFDUT):
|
||||
TOOLCHAIN_PREFIX = 'xtensa-esp32-elf-'
|
||||
|
||||
@classmethod
|
||||
def _get_rom(cls):
|
||||
def get_rom(cls):
|
||||
return esptool.ESP32ROM
|
||||
|
||||
|
||||
@ -571,7 +586,7 @@ class ESP32S2DUT(IDFDUT):
|
||||
TOOLCHAIN_PREFIX = 'xtensa-esp32s2-elf-'
|
||||
|
||||
@classmethod
|
||||
def _get_rom(cls):
|
||||
def get_rom(cls):
|
||||
return esptool.ESP32S2ROM
|
||||
|
||||
|
||||
@ -580,7 +595,7 @@ class ESP32S3DUT(IDFDUT):
|
||||
TOOLCHAIN_PREFIX = 'xtensa-esp32s3-elf-'
|
||||
|
||||
@classmethod
|
||||
def _get_rom(cls):
|
||||
def get_rom(cls):
|
||||
return esptool.ESP32S3ROM
|
||||
|
||||
def erase_partition(self, esp, partition):
|
||||
@ -592,7 +607,7 @@ class ESP32C3DUT(IDFDUT):
|
||||
TOOLCHAIN_PREFIX = 'riscv32-esp-elf-'
|
||||
|
||||
@classmethod
|
||||
def _get_rom(cls):
|
||||
def get_rom(cls):
|
||||
return esptool.ESP32C3ROM
|
||||
|
||||
|
||||
@ -601,13 +616,13 @@ class ESP8266DUT(IDFDUT):
|
||||
TOOLCHAIN_PREFIX = 'xtensa-lx106-elf-'
|
||||
|
||||
@classmethod
|
||||
def _get_rom(cls):
|
||||
def get_rom(cls):
|
||||
return esptool.ESP8266ROM
|
||||
|
||||
|
||||
def get_target_by_rom_class(cls):
|
||||
for c in [ESP32DUT, ESP32S2DUT, ESP32S3DUT, ESP32C3DUT, ESP8266DUT, IDFQEMUDUT]:
|
||||
if c._get_rom() == cls:
|
||||
if c.get_rom() == cls:
|
||||
return c.TARGET
|
||||
return None
|
||||
|
||||
@ -654,7 +669,7 @@ class IDFQEMUDUT(IDFDUT):
|
||||
self.flash_image.flush()
|
||||
|
||||
@classmethod
|
||||
def _get_rom(cls):
|
||||
def get_rom(cls):
|
||||
return esptool.ESP32ROM
|
||||
|
||||
@classmethod
|
||||
@ -704,3 +719,133 @@ class IDFQEMUDUT(IDFDUT):
|
||||
class ESP32QEMUDUT(IDFQEMUDUT):
|
||||
TARGET = 'esp32' # type: ignore
|
||||
TOOLCHAIN_PREFIX = 'xtensa-esp32-elf-' # type: ignore
|
||||
|
||||
|
||||
class IDFFPGADUT(IDFDUT):
|
||||
TARGET = None # type: str
|
||||
TOOLCHAIN_PREFIX = None # type: str
|
||||
ERASE_NVS = True
|
||||
FLASH_ENCRYPT_SCHEME = None # type: str
|
||||
FLASH_ENCRYPT_CNT_KEY = None # type: str
|
||||
FLASH_ENCRYPT_CNT_VAL = 0
|
||||
FLASH_ENCRYPT_PURPOSE = None # type: str
|
||||
SECURE_BOOT_EN_KEY = None # type: str
|
||||
SECURE_BOOT_EN_VAL = 0
|
||||
FLASH_SECTOR_SIZE = 4096
|
||||
|
||||
def __init__(self, name, port, log_file, app, allow_dut_exception=False, efuse_reset_port=None, **kwargs):
|
||||
super(IDFFPGADUT, self).__init__(name, port, log_file, app, allow_dut_exception=allow_dut_exception, **kwargs)
|
||||
self.esp = self.get_rom()(port)
|
||||
self.efuses = None
|
||||
self.efuse_operations = None
|
||||
self.efuse_reset_port = efuse_reset_port
|
||||
|
||||
@classmethod
|
||||
def get_rom(cls):
|
||||
raise NotImplementedError('This is an abstraction class, method not defined.')
|
||||
|
||||
def erase_partition(self, esp, partition):
|
||||
raise NotImplementedError()
|
||||
|
||||
def enable_efuses(self):
|
||||
# We use an extra COM port to reset the efuses on FPGA.
|
||||
# Connect DTR pin of the COM port to the efuse reset pin on daughter board
|
||||
# Set EFUSEPORT env variable to the extra COM port
|
||||
if not self.efuse_reset_port:
|
||||
raise RuntimeError('EFUSEPORT not specified')
|
||||
|
||||
# Stop any previous serial port operation
|
||||
self.stop_receive()
|
||||
if self.secure_boot_en:
|
||||
self.esp.connect()
|
||||
self.efuses, self.efuse_operations = espefuse.get_efuses(self.esp, False, False, True)
|
||||
|
||||
def burn_efuse(self, field, val):
|
||||
if not self.efuse_operations:
|
||||
self.enable_efuses()
|
||||
BurnEfuseArgs = collections.namedtuple('burn_efuse_args', ['name_value_pairs', 'only_burn_at_end'])
|
||||
args = BurnEfuseArgs({field: val}, False)
|
||||
self.efuse_operations.burn_efuse(self.esp, self.efuses, args)
|
||||
|
||||
def burn_efuse_key(self, key, purpose, block):
|
||||
if not self.efuse_operations:
|
||||
self.enable_efuses()
|
||||
BurnKeyArgs = collections.namedtuple('burn_key_args',
|
||||
['keyfile', 'keypurpose', 'block',
|
||||
'force_write_always', 'no_write_protect', 'no_read_protect', 'only_burn_at_end'])
|
||||
args = BurnKeyArgs([key],
|
||||
[purpose],
|
||||
[block],
|
||||
False, False, False, False)
|
||||
self.efuse_operations.burn_key(self.esp, self.efuses, args)
|
||||
|
||||
def burn_efuse_key_digest(self, key, purpose, block):
|
||||
if not self.efuse_operations:
|
||||
self.enable_efuses()
|
||||
BurnDigestArgs = collections.namedtuple('burn_key_digest_args',
|
||||
['keyfile', 'keypurpose', 'block',
|
||||
'force_write_always', 'no_write_protect', 'no_read_protect', 'only_burn_at_end'])
|
||||
args = BurnDigestArgs([open(key, 'rb')],
|
||||
[purpose],
|
||||
[block],
|
||||
False, False, True, False)
|
||||
self.efuse_operations.burn_key_digest(self.esp, self.efuses, args)
|
||||
|
||||
def reset_efuses(self):
|
||||
if not self.efuse_reset_port:
|
||||
raise RuntimeError('EFUSEPORT not specified')
|
||||
with serial.Serial(self.efuse_reset_port) as efuseport:
|
||||
print('Resetting efuses')
|
||||
efuseport.dtr = 0
|
||||
self.port_inst.setRTS(1)
|
||||
self.port_inst.setRTS(0)
|
||||
time.sleep(1)
|
||||
efuseport.dtr = 1
|
||||
self.efuse_operations = None
|
||||
self.efuses = None
|
||||
|
||||
def sign_data(self, data_file, key_files, version, append_signature=0):
|
||||
SignDataArgs = collections.namedtuple('sign_data_args',
|
||||
['datafile','keyfile','output', 'version', 'append_signatures'])
|
||||
outfile = tempfile.NamedTemporaryFile()
|
||||
args = SignDataArgs(data_file, key_files, outfile.name, str(version), append_signature)
|
||||
espsecure.sign_data(args)
|
||||
outfile.seek(0)
|
||||
return outfile.read()
|
||||
|
||||
|
||||
class ESP32C3FPGADUT(IDFFPGADUT):
|
||||
TARGET = 'esp32c3'
|
||||
TOOLCHAIN_PREFIX = 'riscv32-esp-elf-'
|
||||
FLASH_ENCRYPT_SCHEME = 'AES-XTS'
|
||||
FLASH_ENCRYPT_CNT_KEY = 'SPI_BOOT_CRYPT_CNT'
|
||||
FLASH_ENCRYPT_CNT_VAL = 1
|
||||
FLASH_ENCRYPT_PURPOSE = 'XTS_AES_128_KEY'
|
||||
SECURE_BOOT_EN_KEY = 'SECURE_BOOT_EN'
|
||||
SECURE_BOOT_EN_VAL = 1
|
||||
|
||||
@classmethod
|
||||
def get_rom(cls):
|
||||
return esptool.ESP32C3ROM
|
||||
|
||||
def erase_partition(self, esp, partition):
|
||||
raise NotImplementedError()
|
||||
|
||||
def flash_encrypt_burn_cnt(self):
|
||||
self.burn_efuse(self.FLASH_ENCRYPT_CNT_KEY, self.FLASH_ENCRYPT_CNT_VAL)
|
||||
|
||||
def flash_encrypt_burn_key(self, key, block=0):
|
||||
self.burn_efuse_key(key, self.FLASH_ENCRYPT_PURPOSE, 'BLOCK_KEY%d' % block)
|
||||
|
||||
def flash_encrypt_get_scheme(self):
|
||||
return self.FLASH_ENCRYPT_SCHEME
|
||||
|
||||
def secure_boot_burn_en_bit(self):
|
||||
self.burn_efuse(self.SECURE_BOOT_EN_KEY, self.SECURE_BOOT_EN_VAL)
|
||||
|
||||
def secure_boot_burn_digest(self, digest, key_index=0, block=0):
|
||||
self.burn_efuse_key_digest(digest, 'SECURE_BOOT_DIGEST%d' % key_index, 'BLOCK_KEY%d' % block)
|
||||
|
||||
@classmethod
|
||||
def confirm_dut(cls, port, **kwargs):
|
||||
return True, cls.TARGET
|
||||
|
@ -23,8 +23,8 @@ from tiny_test_fw import TinyFW, Utility
|
||||
|
||||
from .DebugUtils import CustomProcess, GDBBackend, OCDBackend # noqa: export DebugUtils for users
|
||||
from .IDFApp import UT, ComponentUTApp, Example, IDFApp, LoadableElfTestApp, TestApp # noqa: export all Apps for users
|
||||
from .IDFDUT import (ESP32C3DUT, ESP32DUT, ESP32QEMUDUT, ESP32S2DUT, ESP32S3DUT, # noqa: export DUTs for users
|
||||
ESP8266DUT, IDFDUT)
|
||||
from .IDFDUT import (ESP32C3DUT, ESP32C3FPGADUT, ESP32DUT, ESP32QEMUDUT, ESP32S2DUT, # noqa: export DUTs for users
|
||||
ESP32S3DUT, ESP8266DUT, IDFDUT)
|
||||
from .unity_test_parser import TestFormat, TestResults
|
||||
|
||||
# pass TARGET_DUT_CLS_DICT to Env.py to avoid circular dependency issue.
|
||||
@ -33,6 +33,7 @@ TARGET_DUT_CLS_DICT = {
|
||||
'ESP32S2': ESP32S2DUT,
|
||||
'ESP32S3': ESP32S3DUT,
|
||||
'ESP32C3': ESP32C3DUT,
|
||||
'ESP32C3FPGA': ESP32C3FPGADUT,
|
||||
}
|
||||
|
||||
|
||||
@ -81,7 +82,11 @@ def local_test_check(decorator_target):
|
||||
|
||||
if isinstance(decorator_target, list):
|
||||
if idf_target not in decorator_target:
|
||||
raise ValueError('IDF_TARGET set to {}, not in decorator target value'.format(idf_target))
|
||||
fpga_target = ''.join((idf_target, 'FPGA'))
|
||||
if fpga_target not in decorator_target:
|
||||
raise ValueError('IDF_TARGET set to {}, not in decorator target value'.format(idf_target))
|
||||
else:
|
||||
idf_target = fpga_target
|
||||
else:
|
||||
if idf_target != decorator_target:
|
||||
raise ValueError('IDF_TARGET set to {}, not equal to decorator target value'.format(idf_target))
|
||||
|
Loading…
x
Reference in New Issue
Block a user