Skip to content

Commit f98fbaf

Browse files
authored
[realtek-ambz2] Refactor OTA for better bootloader compatibility (#356)
* [common] Add memory and compile-time check macros * [realtek-amb] Extract OTA utilities from realtek-ambz * [realtek-ambz2] Refactor OTA for better bootloader compatibility * [realtek-ambz2] Fix builder formatting
1 parent 1cc3d0d commit f98fbaf

7 files changed

Lines changed: 256 additions & 122 deletions

File tree

builder/family/realtek-ambz2.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -438,15 +438,18 @@ def encode_public_key(data: bytes) -> str:
438438
image_part_table = "${BUILD_DIR}/image_part_table.${FLASH_PART_TABLE_OFFSET}.bin"
439439
image_bootloader = "${BUILD_DIR}/image_bootloader.${FLASH_BOOT_OFFSET}.bin"
440440
image_firmware_is = "${BUILD_DIR}/image_firmware_is.${FLASH_OTA1_OFFSET}.bin"
441+
part_firmware_is_header = (
442+
"${BUILD_DIR}/part_firmware_is.header.${FLASH_OTA1_OFFSET}.bin"
443+
)
444+
part_firmware_is_data = "${BUILD_DIR}/part_firmware_is.data.${FLASH_OTA1_OFFSET}.bin"
441445
env.Replace(
442446
# linker command (dual .bin outputs)
443447
LINK='${LTCHIPTOOL} link2bin ${BOARD_JSON} "" ""',
444448
# UF2OTA input list
445449
UF2OTA=[
446-
# use unmodified image for flasher
447-
f"{image_firmware_is},{image_firmware_is}=flasher:ota1,ota2",
448-
# use same image for device OTA
449-
f"{image_firmware_is},{image_firmware_is}=device:ota1,ota2",
450+
# use the same image for flasher and device, but flash the 1st sector last
451+
f"{part_firmware_is_data},{part_firmware_is_data}=device:ota1,ota2;flasher:ota1,ota2",
452+
f"{part_firmware_is_header},{part_firmware_is_header}=device:ota1,ota2;flasher:ota1,ota2",
450453
# having flashed an application image, update the bootloader and partition table (incl. keys)
451454
f"{image_bootloader},{image_bootloader}=flasher:boot,boot",
452455
f"{image_part_table},{image_part_table}=flasher:part_table,part_table",

builder/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
board: PlatformBoardConfig = env.BoardConfig()
1919

2020
python_deps = {
21-
"ltchiptool": ">=4.12.2,<5.0",
21+
"ltchiptool": ">=4.13.0,<5.0",
2222
}
2323
env.SConscript("python-venv.py", exports="env")
2424
env.ConfigurePythonVenv()

cores/common/base/api/lt_utils.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818
_a < _b ? _a : _b; \
1919
})
2020

21+
#define LT_MEM32(addr) (*((volatile uint32_t *)(addr)))
22+
23+
// from https://scaryreasoner.wordpress.com/2009/02/28/checking-sizeof-at-compile-time/
24+
// (include/linux/kernel.h)
25+
#define LT_BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2 * !!(condition)]))
26+
#define LT_BUILD_CHECK(condition) ((void)sizeof(char[1 - 2 * !(condition)]))
27+
2128
/**
2229
* @brief Generate random bytes using rand().
2330
*
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/* Copyright (c) Kuba Szczodrzyński 2026-02-01. */
2+
3+
#include <libretiny.h>
4+
#include <sdk_private.h>
5+
6+
#define SYSTEM_DATA_LENGTH 128
7+
8+
/**
9+
* Convert OTA index (1, 2) into partition offset.
10+
*/
11+
bool lt_ota_dual_get_offset(uint8_t index, uint32_t *offset) {
12+
switch (index) {
13+
case 1:
14+
*offset = FLASH_OTA1_OFFSET;
15+
break;
16+
case 2:
17+
*offset = FLASH_OTA2_OFFSET;
18+
break;
19+
default:
20+
return false;
21+
}
22+
return true;
23+
}
24+
25+
/**
26+
* Check which OTA image is active using the bit flag method.
27+
*/
28+
uint8_t lt_ota_dual_get_stored_by_flag() {
29+
// find first non-zero bit of the flag in system data:
30+
// - even count of zero-bits means OTA1, odd count means OTA2
31+
// this allows to switch OTA images by simply clearing next bits,
32+
// without needing to erase the flash
33+
uint32_t ota_counter = LT_MEM32(SPI_FLASH_BASE + FLASH_SYSTEM_OFFSET + 4);
34+
uint8_t bitidx;
35+
for (bitidx = 0; bitidx < 32; bitidx++) {
36+
if (ota_counter & (1 << bitidx))
37+
break;
38+
}
39+
return 1 + (bitidx & 1);
40+
}
41+
42+
/**
43+
* Flip the OTA selection bit flag switch.
44+
*/
45+
bool lt_ota_dual_switch_flag() {
46+
uint32_t bit_flag = LT_MEM32(SPI_FLASH_BASE + FLASH_SYSTEM_OFFSET + 4);
47+
48+
if (bit_flag == 0) {
49+
// allocate memory and read old system data contents
50+
uint32_t *system = malloc(SYSTEM_DATA_LENGTH);
51+
if (!system)
52+
return false;
53+
if (lt_flash_read(FLASH_SYSTEM_OFFSET, (void *)system, SYSTEM_DATA_LENGTH) != SYSTEM_DATA_LENGTH)
54+
goto err;
55+
56+
// set bit flag to OTA 2
57+
system[1] = 0xFFFFFFFE;
58+
59+
// erase and write
60+
if (!lt_flash_erase_block(FLASH_SYSTEM_OFFSET))
61+
goto err;
62+
if (lt_flash_write(FLASH_SYSTEM_OFFSET, (void *)system, SYSTEM_DATA_LENGTH) != SYSTEM_DATA_LENGTH)
63+
goto err;
64+
65+
free(system);
66+
return true;
67+
err:
68+
free(system);
69+
return false;
70+
}
71+
72+
// shift the bit flag and write without erasing
73+
bit_flag <<= 1;
74+
return lt_flash_write(FLASH_SYSTEM_OFFSET + 4, (void *)&bit_flag, 4) == 4;
75+
}

cores/realtek-ambz/base/api/lt_ota.c

Lines changed: 9 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,19 @@
33
#include <libretiny.h>
44
#include <sdk_private.h>
55

6+
// private utilities from realtek-amb core
7+
extern bool lt_ota_dual_get_offset(uint8_t index, uint32_t *offset);
8+
extern uint8_t lt_ota_dual_get_stored_by_flag();
9+
extern bool lt_ota_dual_switch_flag();
10+
611
lt_ota_type_t lt_ota_get_type() {
712
return OTA_TYPE_DUAL;
813
}
914

1015
bool lt_ota_is_valid(uint8_t index) {
1116
uint32_t offset;
12-
switch (index) {
13-
case 1:
14-
offset = FLASH_OTA1_OFFSET;
15-
break;
16-
case 2:
17-
offset = FLASH_OTA2_OFFSET;
18-
break;
19-
default:
20-
return false;
21-
}
17+
if (!lt_ota_dual_get_offset(index, &offset))
18+
return false;
2219
uint8_t *address = (uint8_t *)(SPI_FLASH_BASE + offset);
2320
return memcmp(address, "81958711", 8) == 0;
2421
}
@@ -34,16 +31,7 @@ uint8_t lt_ota_dual_get_stored() {
3431
uint32_t *ota_address = (uint32_t *)0x8009000;
3532
if (*ota_address == 0xFFFFFFFF)
3633
return 1;
37-
uint32_t ota_counter = *((uint32_t *)0x8009004);
38-
// even count of zero-bits means OTA1, odd count means OTA2
39-
// this allows to switch OTA images by simply clearing next bits,
40-
// without needing to erase the flash
41-
uint8_t count = 0;
42-
for (uint8_t i = 0; i < 32; i++) {
43-
if ((ota_counter & (1 << i)) == 0)
44-
count++;
45-
}
46-
return 1 + (count % 2);
34+
return lt_ota_dual_get_stored_by_flag();
4735
}
4836

4937
bool lt_ota_switch(bool revert) {
@@ -55,24 +43,5 @@ bool lt_ota_switch(bool revert) {
5543
if (!lt_ota_is_valid(stored ^ 0b11))
5644
return false;
5745

58-
// - read current OTA switch value from 0x9004
59-
// - reset OTA switch to 0xFFFFFFFE if it's 0x0
60-
// - else check first non-zero bit of OTA switch
61-
// - write OTA switch with first non-zero bit cleared
62-
63-
uint32_t value = HAL_READ32(SPI_FLASH_BASE, FLASH_SYSTEM_OFFSET + 4);
64-
if (value == 0) {
65-
uint8_t *system = (uint8_t *)malloc(64);
66-
lt_flash_read(FLASH_SYSTEM_OFFSET, system, 64);
67-
// reset OTA switch
68-
((uint32_t *)system)[1] = -2;
69-
lt_flash_erase_block(FLASH_SYSTEM_OFFSET);
70-
return lt_flash_write(FLASH_SYSTEM_OFFSET, system, 64);
71-
}
72-
73-
// clear first non-zero bit
74-
value <<= 1;
75-
// write OTA switch to flash
76-
flash_write_word(NULL, FLASH_SYSTEM_OFFSET + 4, value);
77-
return true;
46+
return lt_ota_dual_switch_flag();
7847
}

0 commit comments

Comments
 (0)