192 lines
5.8 KiB
Python
192 lines
5.8 KiB
Python
# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton,
|
|
# Espressif Systems (Shanghai) CO LTD, other contributors as noted.
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
from ..loader import ESPLoader
|
|
from ..util import FatalError, NotImplementedInROMError
|
|
|
|
|
|
class ESP8266ROM(ESPLoader):
|
|
"""Access class for ESP8266 ROM bootloader"""
|
|
|
|
CHIP_NAME = "ESP8266"
|
|
IS_STUB = False
|
|
|
|
CHIP_DETECT_MAGIC_VALUE = [0xFFF0C101]
|
|
|
|
# OTP ROM addresses
|
|
ESP_OTP_MAC0 = 0x3FF00050
|
|
ESP_OTP_MAC1 = 0x3FF00054
|
|
ESP_OTP_MAC3 = 0x3FF0005C
|
|
|
|
SPI_REG_BASE = 0x60000200
|
|
SPI_USR_OFFS = 0x1C
|
|
SPI_USR1_OFFS = 0x20
|
|
SPI_USR2_OFFS = 0x24
|
|
SPI_MOSI_DLEN_OFFS = None
|
|
SPI_MISO_DLEN_OFFS = None
|
|
SPI_W0_OFFS = 0x40
|
|
|
|
UART_CLKDIV_REG = 0x60000014
|
|
|
|
XTAL_CLK_DIVIDER = 2
|
|
|
|
FLASH_SIZES = {
|
|
"512KB": 0x00,
|
|
"256KB": 0x10,
|
|
"1MB": 0x20,
|
|
"2MB": 0x30,
|
|
"4MB": 0x40,
|
|
"2MB-c1": 0x50,
|
|
"4MB-c1": 0x60,
|
|
"8MB": 0x80,
|
|
"16MB": 0x90,
|
|
}
|
|
|
|
FLASH_FREQUENCY = {
|
|
"80m": 0xF,
|
|
"40m": 0x0,
|
|
"26m": 0x1,
|
|
"20m": 0x2,
|
|
}
|
|
|
|
BOOTLOADER_FLASH_OFFSET = 0
|
|
|
|
MEMORY_MAP = [
|
|
[0x3FF00000, 0x3FF00010, "DPORT"],
|
|
[0x3FFE8000, 0x40000000, "DRAM"],
|
|
[0x40100000, 0x40108000, "IRAM"],
|
|
[0x40201010, 0x402E1010, "IROM"],
|
|
]
|
|
|
|
def get_efuses(self):
|
|
# Return the 128 bits of ESP8266 efuse as a single Python integer
|
|
result = self.read_reg(0x3FF0005C) << 96
|
|
result |= self.read_reg(0x3FF00058) << 64
|
|
result |= self.read_reg(0x3FF00054) << 32
|
|
result |= self.read_reg(0x3FF00050)
|
|
return result
|
|
|
|
def _get_flash_size(self, efuses):
|
|
# rX_Y = EFUSE_DATA_OUTX[Y]
|
|
r0_4 = (efuses & (1 << 4)) != 0
|
|
r3_25 = (efuses & (1 << 121)) != 0
|
|
r3_26 = (efuses & (1 << 122)) != 0
|
|
r3_27 = (efuses & (1 << 123)) != 0
|
|
|
|
if r0_4 and not r3_25:
|
|
if not r3_27 and not r3_26:
|
|
return 1
|
|
elif not r3_27 and r3_26:
|
|
return 2
|
|
if not r0_4 and r3_25:
|
|
if not r3_27 and not r3_26:
|
|
return 2
|
|
elif not r3_27 and r3_26:
|
|
return 4
|
|
return -1
|
|
|
|
def get_chip_description(self):
|
|
efuses = self.get_efuses()
|
|
is_8285 = (
|
|
efuses & ((1 << 4) | 1 << 80)
|
|
) != 0 # One or the other efuse bit is set for ESP8285
|
|
if is_8285:
|
|
flash_size = self._get_flash_size(efuses)
|
|
max_temp = (
|
|
efuses & (1 << 5)
|
|
) != 0 # This efuse bit identifies the max flash temperature
|
|
chip_name = {
|
|
1: "ESP8285H08" if max_temp else "ESP8285N08",
|
|
2: "ESP8285H16" if max_temp else "ESP8285N16",
|
|
}.get(flash_size, "ESP8285")
|
|
return chip_name
|
|
return "ESP8266EX"
|
|
|
|
def get_chip_features(self):
|
|
features = ["WiFi"]
|
|
if "ESP8285" in self.get_chip_description():
|
|
features += ["Embedded Flash"]
|
|
return features
|
|
|
|
def flash_spi_attach(self, hspi_arg):
|
|
if self.IS_STUB:
|
|
super(ESP8266ROM, self).flash_spi_attach(hspi_arg)
|
|
else:
|
|
# ESP8266 ROM has no flash_spi_attach command in serial protocol,
|
|
# but flash_begin will do it
|
|
self.flash_begin(0, 0)
|
|
|
|
def flash_set_parameters(self, size):
|
|
# not implemented in ROM, but OK to silently skip for ROM
|
|
if self.IS_STUB:
|
|
super(ESP8266ROM, self).flash_set_parameters(size)
|
|
|
|
def chip_id(self):
|
|
"""
|
|
Read Chip ID from efuse - the equivalent of the SDK system_get_chip_id() func
|
|
"""
|
|
id0 = self.read_reg(self.ESP_OTP_MAC0)
|
|
id1 = self.read_reg(self.ESP_OTP_MAC1)
|
|
return (id0 >> 24) | ((id1 & 0xFFFFFF) << 8)
|
|
|
|
def read_mac(self):
|
|
"""Read MAC from OTP ROM"""
|
|
mac0 = self.read_reg(self.ESP_OTP_MAC0)
|
|
mac1 = self.read_reg(self.ESP_OTP_MAC1)
|
|
mac3 = self.read_reg(self.ESP_OTP_MAC3)
|
|
if mac3 != 0:
|
|
oui = ((mac3 >> 16) & 0xFF, (mac3 >> 8) & 0xFF, mac3 & 0xFF)
|
|
elif ((mac1 >> 16) & 0xFF) == 0:
|
|
oui = (0x18, 0xFE, 0x34)
|
|
elif ((mac1 >> 16) & 0xFF) == 1:
|
|
oui = (0xAC, 0xD0, 0x74)
|
|
else:
|
|
raise FatalError("Unknown OUI")
|
|
return oui + ((mac1 >> 8) & 0xFF, mac1 & 0xFF, (mac0 >> 24) & 0xFF)
|
|
|
|
def get_erase_size(self, offset, size):
|
|
"""Calculate an erase size given a specific size in bytes.
|
|
|
|
Provides a workaround for the bootloader erase bug."""
|
|
|
|
sectors_per_block = 16
|
|
sector_size = self.FLASH_SECTOR_SIZE
|
|
num_sectors = (size + sector_size - 1) // sector_size
|
|
start_sector = offset // sector_size
|
|
|
|
head_sectors = sectors_per_block - (start_sector % sectors_per_block)
|
|
if num_sectors < head_sectors:
|
|
head_sectors = num_sectors
|
|
|
|
if num_sectors < 2 * head_sectors:
|
|
return (num_sectors + 1) // 2 * sector_size
|
|
else:
|
|
return (num_sectors - head_sectors) * sector_size
|
|
|
|
def override_vddsdio(self, new_voltage):
|
|
raise NotImplementedInROMError(
|
|
"Overriding VDDSDIO setting only applies to ESP32"
|
|
)
|
|
|
|
|
|
class ESP8266StubLoader(ESP8266ROM):
|
|
"""Access class for ESP8266 stub loader, runs on top of ROM."""
|
|
|
|
FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c
|
|
IS_STUB = True
|
|
|
|
def __init__(self, rom_loader):
|
|
self.secure_download_mode = rom_loader.secure_download_mode
|
|
self._port = rom_loader._port
|
|
self._trace_enabled = rom_loader._trace_enabled
|
|
self.cache = rom_loader.cache
|
|
self.flush_input() # resets _slip_reader
|
|
|
|
def get_erase_size(self, offset, size):
|
|
return size # stub doesn't have same size bug as ROM loader
|
|
|
|
|
|
ESP8266ROM.STUB_CLASS = ESP8266StubLoader
|