ESPHome 2026.3.0-dev
Loading...
Searching...
No Matches
debug_zephyr.cpp
Go to the documentation of this file.
1#include "debug_component.h"
2#ifdef USE_ZEPHYR
3#include <climits>
4#include "esphome/core/log.h"
6#include <zephyr/drivers/hwinfo.h>
7#include <hal/nrf_power.h>
8#include <cstdint>
9#include <zephyr/storage/flash_map.h>
10
11#define BOOTLOADER_VERSION_REGISTER NRF_TIMER2->CC[0]
12
13namespace esphome::debug {
14
15static const char *const TAG = "debug";
16constexpr std::uintptr_t MBR_PARAM_PAGE_ADDR = 0xFFC;
17constexpr std::uintptr_t MBR_BOOTLOADER_ADDR = 0xFF8;
18
19static inline uint32_t read_mem_u32(uintptr_t addr) {
20 return *reinterpret_cast<volatile uint32_t *>(addr); // NOLINT(performance-no-int-to-ptr)
21}
22
23static inline uint8_t read_mem_u8(uintptr_t addr) {
24 return *reinterpret_cast<volatile uint8_t *>(addr); // NOLINT(performance-no-int-to-ptr)
25}
26
27// defines from https://github.com/adafruit/Adafruit_nRF52_Bootloader which prints those information
28constexpr uint32_t SD_MAGIC_NUMBER = 0x51B1E5DB;
29constexpr uintptr_t MBR_SIZE = 0x1000;
30constexpr uintptr_t SOFTDEVICE_INFO_STRUCT_OFFSET = 0x2000;
31constexpr uintptr_t SD_ID_OFFSET = SOFTDEVICE_INFO_STRUCT_OFFSET + 0x10;
33
34static inline bool is_sd_present() {
35 return read_mem_u32(SOFTDEVICE_INFO_STRUCT_OFFSET + MBR_SIZE + 4) == SD_MAGIC_NUMBER;
36}
37static inline uint32_t sd_id_get() {
39 return read_mem_u32(MBR_SIZE + SD_ID_OFFSET);
40 }
41 return 0;
42}
43static inline uint32_t sd_version_get() {
45 return read_mem_u32(MBR_SIZE + SD_VERSION_OFFSET);
46 }
47 return 0;
48}
49
50const char *DebugComponent::get_reset_reason_(std::span<char, RESET_REASON_BUFFER_SIZE> buffer) {
51 const char *buf = zephyr::get_reset_reason(buffer);
52 ESP_LOGD(TAG, "Reset Reason: %s", buf);
53 return buf;
54}
55
56const char *DebugComponent::get_wakeup_cause_(std::span<char, RESET_REASON_BUFFER_SIZE> buffer) {
57 // Zephyr doesn't have detailed wakeup cause like ESP32
58 return "";
59}
60
61uint32_t DebugComponent::get_free_heap_() { return INT_MAX; }
62
63static void fa_cb(const struct flash_area *fa, void *user_data) {
64#if CONFIG_FLASH_MAP_LABELS
65 const char *fa_label = flash_area_label(fa);
66
67 if (fa_label == nullptr) {
68 fa_label = "-";
69 }
70 ESP_LOGCONFIG(TAG, "%2d 0x%0*" PRIxPTR " %-26s %-24.24s 0x%-10x 0x%-12x", (int) fa->fa_id,
71 sizeof(uintptr_t) * 2, (uintptr_t) fa->fa_dev, fa->fa_dev->name, fa_label, (uint32_t) fa->fa_off,
72 fa->fa_size);
73#else
74 ESP_LOGCONFIG(TAG, "%2d 0x%0*" PRIxPTR " %-26s 0x%-10x 0x%-12x", (int) fa->fa_id, sizeof(uintptr_t) * 2,
75 (uintptr_t) fa->fa_dev, fa->fa_dev->name, (uint32_t) fa->fa_off, fa->fa_size);
76#endif
77}
78
80#if CONFIG_FLASH_MAP_LABELS
81 ESP_LOGCONFIG(TAG, "ID | Device | Device Name "
82 "| Label | Offset | Size");
83 ESP_LOGCONFIG(TAG, "--------------------------------------------"
84 "-----------------------------------------------");
85#else
86 ESP_LOGCONFIG(TAG, "ID | Device | Device Name "
87 "| Offset | Size");
88 ESP_LOGCONFIG(TAG, "-----------------------------------------"
89 "------------------------------");
90#endif
91 flash_area_foreach(fa_cb, nullptr);
92}
93
94static const char *regout0_to_str(uint32_t value) {
95 switch (value) {
96 case (UICR_REGOUT0_VOUT_DEFAULT):
97 return "1.8V (default)";
98 case (UICR_REGOUT0_VOUT_1V8):
99 return "1.8V";
100 case (UICR_REGOUT0_VOUT_2V1):
101 return "2.1V";
102 case (UICR_REGOUT0_VOUT_2V4):
103 return "2.4V";
104 case (UICR_REGOUT0_VOUT_2V7):
105 return "2.7V";
106 case (UICR_REGOUT0_VOUT_3V0):
107 return "3.0V";
108 case (UICR_REGOUT0_VOUT_3V3):
109 return "3.3V";
110 }
111 return "???V";
112}
113
114size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE> buffer, size_t pos) {
115 constexpr size_t size = DEVICE_INFO_BUFFER_SIZE;
116 char *buf = buffer.data();
117
118 // Main supply status
119 const char *supply_status =
120 (nrf_power_mainregstatus_get(NRF_POWER) == NRF_POWER_MAINREGSTATUS_NORMAL) ? "Normal voltage." : "High voltage.";
121 ESP_LOGD(TAG, "Main supply status: %s", supply_status);
122 pos = buf_append_printf(buf, size, pos, "|Main supply status: %s", supply_status);
123
124 // Regulator stage 0
125 if (nrf_power_mainregstatus_get(NRF_POWER) == NRF_POWER_MAINREGSTATUS_HIGH) {
126 const char *reg0_type = nrf_power_dcdcen_vddh_get(NRF_POWER) ? "DC/DC" : "LDO";
127 const char *reg0_voltage = regout0_to_str((NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) >> UICR_REGOUT0_VOUT_Pos);
128 ESP_LOGD(TAG, "Regulator stage 0: %s, %s", reg0_type, reg0_voltage);
129 pos = buf_append_printf(buf, size, pos, "|Regulator stage 0: %s, %s", reg0_type, reg0_voltage);
130#ifdef USE_NRF52_REG0_VOUT
131 if ((NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) >> UICR_REGOUT0_VOUT_Pos != USE_NRF52_REG0_VOUT) {
132 ESP_LOGE(TAG, "Regulator stage 0: expected %s", regout0_to_str(USE_NRF52_REG0_VOUT));
133 }
134#endif
135 } else {
136 ESP_LOGD(TAG, "Regulator stage 0: disabled");
137 pos = buf_append_printf(buf, size, pos, "|Regulator stage 0: disabled");
138 }
139
140 // Regulator stage 1
141 const char *reg1_type = nrf_power_dcdcen_get(NRF_POWER) ? "DC/DC" : "LDO";
142 ESP_LOGD(TAG, "Regulator stage 1: %s", reg1_type);
143 pos = buf_append_printf(buf, size, pos, "|Regulator stage 1: %s", reg1_type);
144
145 // USB power state
146 const char *usb_state;
147 if (nrf_power_usbregstatus_vbusdet_get(NRF_POWER)) {
148 if (nrf_power_usbregstatus_outrdy_get(NRF_POWER)) {
149 usb_state = "ready";
150 } else {
151 usb_state = "connected (regulator is not ready)";
152 }
153 } else {
154 usb_state = "disconnected";
155 }
156 ESP_LOGD(TAG, "USB power state: %s", usb_state);
157 pos = buf_append_printf(buf, size, pos, "|USB power state: %s", usb_state);
158
159 // Power-fail comparator
160 bool enabled;
161 nrf_power_pof_thr_t pof_thr = nrf_power_pofcon_get(NRF_POWER, &enabled);
162 if (enabled) {
163 const char *pof_voltage;
164 switch (pof_thr) {
165 case POWER_POFCON_THRESHOLD_V17:
166 pof_voltage = "1.7V";
167 break;
168 case POWER_POFCON_THRESHOLD_V18:
169 pof_voltage = "1.8V";
170 break;
171 case POWER_POFCON_THRESHOLD_V19:
172 pof_voltage = "1.9V";
173 break;
174 case POWER_POFCON_THRESHOLD_V20:
175 pof_voltage = "2.0V";
176 break;
177 case POWER_POFCON_THRESHOLD_V21:
178 pof_voltage = "2.1V";
179 break;
180 case POWER_POFCON_THRESHOLD_V22:
181 pof_voltage = "2.2V";
182 break;
183 case POWER_POFCON_THRESHOLD_V23:
184 pof_voltage = "2.3V";
185 break;
186 case POWER_POFCON_THRESHOLD_V24:
187 pof_voltage = "2.4V";
188 break;
189 case POWER_POFCON_THRESHOLD_V25:
190 pof_voltage = "2.5V";
191 break;
192 case POWER_POFCON_THRESHOLD_V26:
193 pof_voltage = "2.6V";
194 break;
195 case POWER_POFCON_THRESHOLD_V27:
196 pof_voltage = "2.7V";
197 break;
198 case POWER_POFCON_THRESHOLD_V28:
199 pof_voltage = "2.8V";
200 break;
201 default:
202 pof_voltage = "???V";
203 break;
204 }
205
206 if (nrf_power_mainregstatus_get(NRF_POWER) == NRF_POWER_MAINREGSTATUS_HIGH) {
207 const char *vddh_voltage;
208 switch (nrf_power_pofcon_vddh_get(NRF_POWER)) {
209 case NRF_POWER_POFTHRVDDH_V27:
210 vddh_voltage = "2.7V";
211 break;
212 case NRF_POWER_POFTHRVDDH_V28:
213 vddh_voltage = "2.8V";
214 break;
215 case NRF_POWER_POFTHRVDDH_V29:
216 vddh_voltage = "2.9V";
217 break;
218 case NRF_POWER_POFTHRVDDH_V30:
219 vddh_voltage = "3.0V";
220 break;
221 case NRF_POWER_POFTHRVDDH_V31:
222 vddh_voltage = "3.1V";
223 break;
224 case NRF_POWER_POFTHRVDDH_V32:
225 vddh_voltage = "3.2V";
226 break;
227 case NRF_POWER_POFTHRVDDH_V33:
228 vddh_voltage = "3.3V";
229 break;
230 case NRF_POWER_POFTHRVDDH_V34:
231 vddh_voltage = "3.4V";
232 break;
233 case NRF_POWER_POFTHRVDDH_V35:
234 vddh_voltage = "3.5V";
235 break;
236 case NRF_POWER_POFTHRVDDH_V36:
237 vddh_voltage = "3.6V";
238 break;
239 case NRF_POWER_POFTHRVDDH_V37:
240 vddh_voltage = "3.7V";
241 break;
242 case NRF_POWER_POFTHRVDDH_V38:
243 vddh_voltage = "3.8V";
244 break;
245 case NRF_POWER_POFTHRVDDH_V39:
246 vddh_voltage = "3.9V";
247 break;
248 case NRF_POWER_POFTHRVDDH_V40:
249 vddh_voltage = "4.0V";
250 break;
251 case NRF_POWER_POFTHRVDDH_V41:
252 vddh_voltage = "4.1V";
253 break;
254 case NRF_POWER_POFTHRVDDH_V42:
255 vddh_voltage = "4.2V";
256 break;
257 default:
258 vddh_voltage = "???V";
259 break;
260 }
261 ESP_LOGD(TAG, "Power-fail comparator: %s, VDDH: %s", pof_voltage, vddh_voltage);
262 pos = buf_append_printf(buf, size, pos, "|Power-fail comparator: %s, VDDH: %s", pof_voltage, vddh_voltage);
263 } else {
264 ESP_LOGD(TAG, "Power-fail comparator: %s", pof_voltage);
265 pos = buf_append_printf(buf, size, pos, "|Power-fail comparator: %s", pof_voltage);
266 }
267 } else {
268 ESP_LOGD(TAG, "Power-fail comparator: disabled");
269 pos = buf_append_printf(buf, size, pos, "|Power-fail comparator: disabled");
270 }
271
272 auto package = [](uint32_t value) {
273 switch (value) {
274 case 0x2004:
275 return "QIxx - 7x7 73-pin aQFN";
276 case 0x2000:
277 return "QFxx - 6x6 48-pin QFN";
278 case 0x2005:
279 return "CKxx - 3.544 x 3.607 WLCSP";
280 }
281 return "Unspecified";
282 };
283
284 char mac_pretty[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
286 ESP_LOGD(TAG,
287 "nRF debug info:\n"
288 " Code page size: %u, code size: %u, device id: 0x%08x%08x\n"
289 " Encryption root: 0x%08x%08x%08x%08x, Identity Root: 0x%08x%08x%08x%08x\n"
290 " Device address type: %s, address: %s\n"
291 " Part code: nRF%x, version: %c%c%c%c, package: %s\n"
292 " RAM: %ukB, Flash: %ukB, production test: %sdone",
293 NRF_FICR->CODEPAGESIZE, NRF_FICR->CODESIZE, NRF_FICR->DEVICEID[1], NRF_FICR->DEVICEID[0], NRF_FICR->ER[0],
294 NRF_FICR->ER[1], NRF_FICR->ER[2], NRF_FICR->ER[3], NRF_FICR->IR[0], NRF_FICR->IR[1], NRF_FICR->IR[2],
295 NRF_FICR->IR[3], (NRF_FICR->DEVICEADDRTYPE & 0x1 ? "Random" : "Public"), mac_pretty, NRF_FICR->INFO.PART,
296 NRF_FICR->INFO.VARIANT >> 24 & 0xFF, NRF_FICR->INFO.VARIANT >> 16 & 0xFF, NRF_FICR->INFO.VARIANT >> 8 & 0xFF,
297 NRF_FICR->INFO.VARIANT & 0xFF, package(NRF_FICR->INFO.PACKAGE), NRF_FICR->INFO.RAM, NRF_FICR->INFO.FLASH,
298 (NRF_FICR->PRODTEST[0] == 0xBB42319F ? "" : "not "));
299 bool n_reset_enabled = NRF_UICR->PSELRESET[0] == NRF_UICR->PSELRESET[1] &&
300 (NRF_UICR->PSELRESET[0] & UICR_PSELRESET_CONNECT_Msk) == UICR_PSELRESET_CONNECT_Connected
301 << UICR_PSELRESET_CONNECT_Pos;
302 ESP_LOGD(
303 TAG, " GPIO as NFC pins: %s, GPIO as nRESET pin: %s",
304 YESNO((NRF_UICR->NFCPINS & UICR_NFCPINS_PROTECT_Msk) == (UICR_NFCPINS_PROTECT_NFC << UICR_NFCPINS_PROTECT_Pos)),
305 YESNO(n_reset_enabled));
306 if (n_reset_enabled) {
307 uint8_t port = (NRF_UICR->PSELRESET[0] & UICR_PSELRESET_PORT_Msk) >> UICR_PSELRESET_PORT_Pos;
308 uint8_t pin = (NRF_UICR->PSELRESET[0] & UICR_PSELRESET_PIN_Msk) >> UICR_PSELRESET_PIN_Pos;
309 ESP_LOGD(TAG, " nRESET port P%u.%02u", port, pin);
310 }
311#ifdef USE_BOOTLOADER_MCUBOOT
312 ESP_LOGD(TAG, " Bootloader: mcuboot");
313#else
314 ESP_LOGD(TAG, " Bootloader: Adafruit, version %u.%u.%u", (BOOTLOADER_VERSION_REGISTER >> 16) & 0xFF,
315 (BOOTLOADER_VERSION_REGISTER >> 8) & 0xFF, BOOTLOADER_VERSION_REGISTER & 0xFF);
316 ESP_LOGD(TAG, " MBR bootloader addr 0x%08x, UICR bootloader addr 0x%08x", read_mem_u32(MBR_BOOTLOADER_ADDR),
317 NRF_UICR->NRFFW[0]);
318 ESP_LOGD(TAG, " MBR param page addr 0x%08x, UICR param page addr 0x%08x", read_mem_u32(MBR_PARAM_PAGE_ADDR),
319 NRF_UICR->NRFFW[1]);
320 if (is_sd_present()) {
321 uint32_t const sd_id = sd_id_get();
322 uint32_t const sd_version = sd_version_get();
323
324 uint32_t ver[3];
325 ver[0] = sd_version / 1000000;
326 ver[1] = (sd_version - ver[0] * 1000000) / 1000;
327 ver[2] = (sd_version - ver[0] * 1000000 - ver[1] * 1000);
328
329 ESP_LOGD(TAG, " SoftDevice: S%u %u.%u.%u", sd_id, ver[0], ver[1], ver[2]);
330#ifdef USE_SOFTDEVICE_ID
331#ifdef USE_SOFTDEVICE_VERSION
332 if (USE_SOFTDEVICE_ID != sd_id || USE_SOFTDEVICE_VERSION != ver[0]) {
333 ESP_LOGE(TAG, "Built for SoftDevice S%u %u.x.y. It may crash due to mismatch of bootloader version.",
334 USE_SOFTDEVICE_ID, USE_SOFTDEVICE_VERSION);
335 }
336#else
337 if (USE_SOFTDEVICE_ID != sd_id) {
338 ESP_LOGE(TAG, "Built for SoftDevice S%u. It may crash due to mismatch of bootloader version.", USE_SOFTDEVICE_ID);
339 }
340#endif
341#endif
342 }
343#endif
344 auto uicr = [](volatile uint32_t *data, uint8_t size) {
345 std::string res;
346 char buf[sizeof(uint32_t) * 2 + 1];
347 for (size_t i = 0; i < size; i++) {
348 if (i > 0) {
349 res += ' ';
350 }
351 res += format_hex_pretty<uint32_t>(data[i], '\0', false);
352 }
353 return res;
354 };
355 ESP_LOGD(TAG, " NRFFW %s", uicr(NRF_UICR->NRFFW, 13).c_str());
356 ESP_LOGD(TAG, " NRFHW %s", uicr(NRF_UICR->NRFHW, 12).c_str());
357
358 return pos;
359}
360
361void DebugComponent::update_platform_() {}
362
363} // namespace esphome::debug
364#endif
void log_partition_info_()
Logs information about the device's partition table.
size_t get_device_info_(std::span< char, DEVICE_INFO_BUFFER_SIZE > buffer, size_t pos)
const char * get_wakeup_cause_(std::span< char, RESET_REASON_BUFFER_SIZE > buffer)
const char * get_reset_reason_(std::span< char, RESET_REASON_BUFFER_SIZE > buffer)
constexpr std::uintptr_t MBR_PARAM_PAGE_ADDR
constexpr uintptr_t SD_ID_OFFSET
constexpr std::uintptr_t MBR_BOOTLOADER_ADDR
constexpr uintptr_t MBR_SIZE
constexpr uintptr_t SOFTDEVICE_INFO_STRUCT_OFFSET
constexpr uintptr_t SD_VERSION_OFFSET
constexpr uint32_t SD_MAGIC_NUMBER
const char * get_reset_reason(std::span< char, RESET_REASON_BUFFER_SIZE > buffer)
size_t size
Definition helpers.h:854
size_t size_t pos
Definition helpers.h:854
const char * get_mac_address_pretty_into_buffer(std::span< char, MAC_ADDRESS_PRETTY_BUFFER_SIZE > buf)
Get the device MAC address into the given buffer, in colon-separated uppercase hex notation.
Definition helpers.cpp:817
std::string format_hex_pretty(const uint8_t *data, size_t length, char separator, bool show_length)
Format a byte array in pretty-printed, human-readable hex format.
Definition helpers.cpp:401