ESP8266 bootloader

Fórumok

Nem sok esélyt látok rá, de hátha. Írt már valaki esp8266 bootloadert?

Idáig jutottam (AI segítségével).

Első körben egy led villogtató, serial print "Hello word" elég lenne, a többi talán már menne.

Fontos hogy IRAM-ból fusson, A 0x100000-0x1FFFFF flest akarom 0x000000-0x0FFFFF helyre másolni vele.

 

bootloader.ld

ENTRY(_start)

MEMORY
{
   /* IRAM: 32 KB-os RAM terület */
   iram (rwx) : ORIGIN = 0x40100000, LENGTH = 32K
}

SECTIONS
{
   .text :
   {
       . = ALIGN(4);
       
       /* 1. LITERÁLOK */
       *(.literal)
       *(.literal.*)
       
       /* 2. KÓD */
       *(.text)            
       *(.text.*)
       
       /* 3. KONSTANS ADATOK */
       *(.rodata)            
       *(.rodata.*)
       
       /* --- ÚJ: Globális Adatok (Inicializált) --- */
       . = ALIGN(4);
       *(.data)
       *(.data.*)
       
       /* --- ÚJ: Globális Adatok (Inicializálatlan, 0-ra kell állítani) --- */
       . = ALIGN(4);
       _bss_start = .;
       *(.bss)
       *(.bss.*)
       . = ALIGN(4);
       _bss_end = .;
       
   } > iram
}

 


bootloader.S

.section .text
.global _start

_start:
   /* Stack Pointer beállítása az IRAM végére: 0x40107FFC */
   movi a1, 0x40107FFC

   /* Ugorjunk a C kód fő belépési pontjához */
   call0 main_entry

   /* Végtelen ciklus (trap) */
hang:
   j hang

 

 


bootloader.c

#include <stdint.h>

// -------------------------------------------------------------------------
// Hardver Regiszterek Definiálása
// -------------------------------------------------------------------------

// System/WDT
#define WDT_CTL_REG             0x600000A0
#define DPORT_BASE_ADDR         0x3FF00000
#define DPORT_CLOCK_GATE_REG    (DPORT_BASE_ADDR + 0x28) // Órajel vezérlés

// GPIO/LED (Wemos D1 Mini)
#define GPIO_BASE_ADDR          0x60000300
#define GPIO_ENABLE_W1TS_REG    (GPIO_BASE_ADDR + 0x14)
#define GPIO_OUT_W1TS_REG       (GPIO_BASE_ADDR + 0x08)
#define GPIO_OUT_W1TC_REG       (GPIO_BASE_ADDR + 0x0C)
#define LED_PIN_MASK            (1 << 2)    // GPIO2 maszk

// -------------------------------------------------------------------------
// Késleltető Segédfüggvény
// -------------------------------------------------------------------------

// Késleltetés 80 MHz-es CPU frekvenciára kalibrálva (Kb. 80000 ciklus/ms)
#define DELAY_CYCLES_PER_MS 40000 // Csökkentett ciklusszám a biztos láthatóságért (kb. 500 ms-ot ad 80 MHz-en)

static void delay_ms(uint32_t ms) {
   volatile uint32_t cycles = ms * DELAY_CYCLES_PER_MS;
   while (cycles > 0) {
       cycles--;
   }
}

// -------------------------------------------------------------------------
// Fő Belépési Pont
// ------------------------------------------------N---------

void main_entry(void) {
   
   // 0.1. MEGSZAKÍTÁSOK KIKAPCSOLÁSA
   __asm__ __volatile__ (
       "rsr a2, PS\n"          
       "movi a3, 0xFFFFFFFC\n" 
       "and a2, a2, a3\n"
       "wsr a2, PS\n"          
       "esync\n"
   );

   // 0.2. WATCHDOG TIMER (WDT) KIKAPCSOLÁSA
   volatile uint32_t *wdt_ctl = (volatile uint32_t *)WDT_CTL_REG;
   *wdt_ctl = 0x73;
   *wdt_ctl = 0x83;

   // 0.3. ⚡ CPU ÓRAJELEL KÉNYSZERÍTÉSE 80 MHz-RE
   // A DPORT_CLOCK_GATE_REG (0x3FF00028) beállítása:
   // Bit 0: CPU_CLK_SEL (0=40MHz, 1=80MHz) - Ez a legfontosabb
   // Bitek 1 és 2 is fontosak lehetnek a stabil órajelhez.
   volatile uint32_t *dport_clk = (volatile uint32_t *)DPORT_CLOCK_GATE_REG;
   // Feltételezve, hogy a 80 MHz bit 0, de a teljes órajel inicializációhoz biztonságosabb a ROM hívás, 
   // de mivel ezt elvetettük, a regiszterekkel próbálkozunk.
   // **FIGYELEM:** Mivel a pontos bare-metal CPU_CLK regisztercím eltérhet, de az
   // *ETS_RTC_SET_CONFIG()* ROM hívás pontos. Maradjunk a direkt beavatkozásnál, 
   // de tegyünk egy biztonsági késleltetést.
   
   // Ideiglenes késleltetés az órajel stabilizálására, mielőtt a GPIO-t beállítjuk
   volatile int i;
   for (i = 0; i < 50000; i++) {}


   // 0.4. 💡 GPIO2 INITIALIZÁLÁSA (LED vezérlés)
   volatile uint32_t *gpio_enable_w1ts = (volatile uint32_t *)GPIO_ENABLE_W1TS_REG;
   volatile uint32_t *gpio_out_w1ts = (volatile uint32_t *)GPIO_OUT_W1TS_REG;
   
   *gpio_enable_w1ts = LED_PIN_MASK; // GPIO2 beállítása kimenetnek
   *gpio_out_w1ts = LED_PIN_MASK;    // GPIO2 HIGH (LED KI) - Kezdő állapot
   
   // 1. Végtelen ciklus (LED villogtatás)
   volatile uint32_t *gpio_out_w1tc = (volatile uint32_t *)GPIO_OUT_W1TC_REG;

   while (1) {
       // LED BE (LOW)
       *gpio_out_w1tc = LED_PIN_MASK; 
       delay_ms(500); // 500 ms késleltetés

       // LED KI (HIGH)
       *gpio_out_w1ts = LED_PIN_MASK; 
       delay_ms(500); // 500 ms késleltetés
   }
}

 


bootloader.cmd

@echo off
setlocal enabledelayedexpansion

:: === CONFIGURE these paths ===
set "TOOLCHAIN_DIR=F:\asm\bin"
set "SRC=bootloader.c"
set "LD=bootloader.ld"
set "SC=bootloader.S"
set "ESPPORT=COM9"
:: =============================

echo Checking toolchain...
set "PATH=%TOOLCHAIN_DIR%;%PATH%"
where xtensa-lx106-elf-gcc >nul 2>&1
if errorlevel 1 (
 echo [ERROR] xtensa toolchain not found in PATH: %TOOLCHAIN_DIR%
 pause
 exit /b 1
)

echo Building...

xtensa-lx106-elf-gcc -c %SC% -o startup.o -Os
xtensa-lx106-elf-gcc -c %SRC% -o bootloader.o -Os -nostdlib -fno-builtin -mlongcalls
xtensa-lx106-elf-ld -T %LD% startup.o bootloader.o -o bootloader.elf

esptool.exe elf2image bootloader.elf

esptool.exe --port %ESPPORT% --baud 921600 write_flash 0x00000 bootloader.elf-0x00000.bin


pause

 

Ezt működő állapotba tudná hozni valaki?

Hozzászólások

Az ESP8266 bootloader bele van égetve gyárilag ROM-ba, azt a gyárban megkapta a chip és az cserélhetetlen. Ha még mindig azon pörögsz, hogy OTA frissítést akarsz fájlrendszerről ilyen másolgatásokkal, akkor nem fog így menni, mert ez, amit te bootloader-nek nevezel, ez a futó két programterület közül az egyiken lesz és nem tudja írni se a másik területet, se a saját területét, miközben fut.

Nem adom fel. Igazad van, a rom bootol, a flash 0x000000 címről inditja a kódot ha az megfelel a rom által megkövetelt formátumnak. Ilyet szeretnék egy hello word mintával. Az Arduino által fordított kód is 0x000000 címtől kezdődik, ezt indítja a rom, ami működik. Serial print, erase flash, write flash direktben hívható a bedrótozott romból. Felbaszta az agyam, azért is megcsinálom.

Az ESP8266 bootloader bele van égetve gyárilag ROM-ba, azt a gyárban megkapta a chip és az cserélhetetlen.

 

Egyes mobiltelefonoknál alternatív ROM felrakása előtt ki kell "nyitni"

a bootloadert - jelentsen ez bármit is - egyiknél a gyártótól kapott, a szériaszámból generált kóddal. Az miben más? Az (át)írható is? 

Ez is ESP csak más verzió, itt fash-eli

https://github.com/espressif/esptool/blob/master/docs/en/advanced-topic…

Szerintem a boot loader pont nem az, amit AI-val kellene íratni. Mondjuk mást sem.

Olyan apróságok például, hogy mi van a megszakítás vektortáblával?

ESP-re még nem írtam boot loader-t, de PIC32-re és PIC18-ra már igen.

A letöltött program entry pointját vagy a linker scripttel mondod meg, vagy valahogy elmeséled a fejlesztői környezetnek, hogy offset-elje feljebb a kódot, de ügyes megoldás lehet, ha meg tudod kérni a fejlesztői környezetet arra, hogy a hex file-ba írjon SLA-t (0x05: Start Linear Address), s ide adod a vezérlést.

Az ESP-t nem ismerem, emiatt nem tudok konkrét lenni.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Az alapgondolat egyszeru is tud lenni. Felosztod a programmemoriat (legalabb) ket reszre. Egyik resze bootol be, es azon egy olyan kod fut ami egyreszt lehetove teszi az eszkoz elereset egy olyan adott halozati(bb) interface-en amin keresztul a progremmemoria masik resze(i)t frissitheted, masreszt pedig a kello pillanatban atadja oda ugy a vezerlest a programmemoria masodik reszere hogy ezzel parhuzamosan meghamisitja/visszaallitja a CPU-t a reset allapothoz minel kozelebbi allapotba. Az ordog a reszletekben rejlik - mi(k) is lesz(nek) ez a halozati interface(k), hogyan kulonbozteted meg a "bootloader" es a "bebootolt" allapotokat, hogy allitod vissza a CPU-t reset (kozeli) allapotba, a memory layout-tol fuggo particionalasa a programmemorianak, flash page alignmentek, linker script modositasa,  vektor tabla cimenek modositasa, periferia/interrupt/stb inicializalasok visszahivasa, .rodata/.data/.bss section-ok ismerete, .rodata tulajdonsagai (ld. Harvard vs. Neumann problemak), stbstbstb - de ezek mar elegge architektura-fuggoek tudnak lenni. Cserebe ezek a reszletek jol behatarolhato, kulon-kulon is jol letesztelheto lepesek.

Hajra :) 

Az EspOS már tudja hogy ha firmware.bin fált kap feltöltéskor akkor leállítja az SPIFFS-t, a beérkező cuccot felírja 0x100000 címtől. Utána egy uint8_t tömböt (ez lenne a bootloader) felír 0x00000-0x00FFF címre. Nincs crash. Reboot után szeretnék látni egy soros "Hello word"-ot.

Az alapgondolat egyszeru is tud lenni. Felosztod a programmemoriat (legalabb) ket reszre. Egyik resze bootol be, es azon egy olyan kod fut ami egyreszt lehetove teszi az eszkoz elereset egy olyan adott halozati(bb) interface-en amin keresztul a progremmemoria masik resze(i)t frissitheted, masreszt pedig a kello pillanatban atadja oda ugy a vezerlest a programmemoria masodik reszere hogy ezzel parhuzamosan meghamisitja/visszaallitja a CPU-t a reset allapothoz minel kozelebbi allapotba.

Az alapgondolat olyannyira egyszerű, hogy az ESP8266 esetén ez a viselkedés bele van égetve a chip-be, definiálni kell kettő programterületet (dual partition layout), amelyek közül az egyik aktív, a másik meg írható a bootloader megfelelő hívásával. Az a dolog viszont nem megy, hogy csak egy programterületet definiálsz, és azt írod konkrétan a futó program alatt. Ami itt bootloader néven van nevezve, az maga a futó program... OP kolléga viszont nem így szeretné, ahogy meg szeretné, az úgy nem megy.

Az a dolog viszont nem megy, hogy csak egy programterületet definiálsz, és azt írod konkrétan a futó program alatt.

Ez nem illendo, igen. Legalabbis ugy altalaban mehetni mehet de azert nem csinalnam :) 

Ettol fuggetlenul az ilyen gyari ROM-os bootloaderek azok lehet hogy jatszani jok meg par dolgot egyszerusitenek (programozo helyett hasznalhatsz egy szokvanyosabb portot, mondjuk UART-ot), de elesben, ha egy adott interface-n egy adott protokoll szerint kell frissiteni (ami lehet full egyedi is, mert epp' az a kovetelmeny), akkor... akkor jo kerdes hogy mire mesz ezekkel a beleegetett ROM-okkal. 

De sokat nem akarok okoskodni, az ESP8266-t csak igy futolag futottam at. De a masik ~feltucat architekturanal ahol ilyeneket csinaltam ott a fentebbi meglatasok mukodtek. Es valojaban itt sem latom hogy miert ne mukodne. 

De sokat nem akarok okoskodni, az ESP8266-t csak igy futolag futottam at. De a masik ~feltucat architekturanal ahol ilyeneket csinaltam ott a fentebbi meglatasok mukodtek. Es valojaban itt sem latom hogy miert ne mukodne. 

Mert az ESP8266 esetén van egy gyárilag beleégetett bootloader, ami egy vagy két partícióról tud firmware-t indítani és két partícióval tud OTA frissítést (az aktív partíció tudja írni a passzív partíciót, reboot és az újabb indul el). Ez ilyen. És mivel ilyen, ezért nincs mozgástered. Az ESP8266 12 éve jött ki, 12 év alatt nem írt senki olyat, amit OP szeretne (egy partíciót használ, letölti az új firmware-t a fájlrendszerbe és onnan átmásolja maga alá az éppen futó program). Nem azért nem írt senki ilyet, mert nem gondoltak rá, hanem azért, mert ESP8266 esetén ezt nem lehet megoldani. Ha meg is lehetne oldani, akkor is egy csomó nem várt probléma miatt az eszköz frissíthetetlen lesz.

Az egyetlen megoldás kb. az, hogy aszimmetrikus méretű a két partíció, az egyikben van egy OTA frissítő, a másikban meg van a futó firmware, de OP ezt már korábban elvetette.

Igen, a RAM-bol valo futtatast szoktak hasznalni, de szerintem nem pont erre. Sokszor azert kell, mert az eszkoz egyaltalan nem kepes a flashbol olvasni (pl. betolteni a kovetkezo utasitast) es irni bele.

De itt masrol van szo, az OTA eseten az egesz stacket be kellene huzni a RAM-ba, hiszen az vegzi a vezetek nelkuli kommunikaciot, ami Wi-Fi / BLE / akarmi eseten nem csak egy par byte. Ugye mi tortenik, amikor pont azt a reszet irod felul a flash-ben a Wi-Fi stacknek ami eppen a csomagokat kuldozgeti? ;-)

/sza2

Digital? Every idiot can count to one - Bob Widlar

En nem lattam sehol, hogy over the air updatet akar csinalni. Linkermagic, es gany, de azt is egyszeruen nem piszkalod a flashben es mikor ujraindul akkor irod at.

De igen, valoban arra szoktak hasznalni, hogy irasra lockolt flasht tudjanak irni, hisz abbol nem lehet olvasni (ha pl. van flash tukor akkor igy lesz atomikus a muvelet).

"Az EspOS már tudja hogy ha firmware.bin fált kap feltöltéskor akkor leállítja az SPIFFS-t, a beérkező cuccot felírja 0x100000 címtől. Utána egy uint8_t tömböt (ez lenne a bootloader) felír 0x00000-0x00FFF címre." Ez működik.

0x00000-0x00FFF szeretnék egy bootolható kódot ami a 0x100000-0x1FFFFF tartományt átmásolja 0x000000-0x0FFFFF

A kerdes mar csak az, hogy az miert nem jo, ami a tamogatott megoldas. Random fiokbol elohuzott ESP8266 boardon rajta van maga a wifis mikrokontroller, meg egy "bergmicro 25Q80ASSIG" feliratu 8 labu IC. Utobbi egy 8 Mbites (1 MiB) SPI-on elerheto flash, ebben tarolja a programjat, meg ami meg van asset, azt is. Ha ez keves, ezt kicserelve fel lehet menni 25Q256-ig, ami megabitben ennyi, 32 MiB flash. Annak a fele is eleg jelentos. (lehet, hogy az ESP-ben valami flaget kell allitgatni hozza, hogy lassa az egeszet, ennyire nem melyedtem bele) Extrem esetben kulso hardware-rel is megoldhato az egesz, ha nagyon nincs mas megoldas, vegulis egy kulso flash, es az SPI-t akar kezzel is lehet irni egy kapcsoloval es egy prellmentesitett nyomogombbal - nem ajanlott persze.

A strange game. The only winning move is not to play. How about a nice game of chess?

Egyetertve az elottem szolokkal, szertintem is az a megoldas, hogy van egy custom bootloader (ugy ertem nem az amivel UART-on lehet letolteni az applikaciot), illetve van hely az eppen futo applikacionak es az upgrade image-nek. Kicsit szemleletesebben:

|----|-------------|-------------|
| BL |     APP     |     IMG     |

Nalunk a fenti sema szerint van megoldva. a bootloader (BL) ad az applikacio (APP) szamara fuggvenyeket, mint iras / olvasas / torles / ujarinditas bootloader modban / image ellenorzes / stb. Az applikacio ami fut kepes ra, hogy OTA letoltson egy (frissebb / masabb) applikaciot az image storage teruletre (IMG). Ha ez megtortent (persze celszeru ellenorizni, hogy az image nem serult-e (pl. valami hash-t szamolni ra)) akkor meghivja a megfelelo fuggvenyt a bootloaderbol, ami ujrainditja az eszkozt es atmasolja az IMG teruletrol az APP teruletre a letoltott image-et majd ismet ujrainditja az eszkozt. Nalunk a RAM-ban van beirva egy byte, ami alapjan a bootloader eldonti, hogy reboot eseten az applikacio induljon el vagy a bootloader irja felul az eredeti applikaciot az ujjal.

 

Szerintem ez a modszer elegge elterjedt az OTA update-re. Hatranya, hogy ketszer annyi hely kell, mintha nem lenne OTA update lehetoseg (jo, lehet az image-et tomoriteni is).

/sza2

Digital? Every idiot can count to one - Bob Widlar

ESP8266 eseten ez nem tud mukodni?

Nem tud. ESP8266 esetén a bootloader egy fix gyárilag beégetett cucc, ami tud egy partícióról firmware-t indítani vagy kettő partícióról firmware-t indítani, és a maradék flash lehet fájlrendszer. Nincs benne ilyen rugalmasság, sokan próbálták megkerülni, nem tudták.

Amit eddig csináltam boot loader-t, abban teljesen önálló kód a boot loader és a letöltött, majd futtatott kód. Tehát a letöltött firmware nem támaszkodik a boot loader függvényeire és viszont, azaz a boot loaderben is megvan minden magában.

Reset után elindul a boot loader. Ha nem szólítják meg és a firmware entry point-on nem csupa 0xff van, akkor adott timeout letelte után átadja a vezérlést a firmware-nek. Ha a timeout letelte előtt megszólítják akár csak egy státusz kérdezéssel, akkor a timeout végtelenné válik, nem kell attól tartani, hogy elugrik a firmware-re. Lehet neki azt mondani, hogy nesze, itt a firmware, írd be a flash memóriába. Lehet csinálni ellenőrzést, hogy kiderüljön, sikerült-e a flash-be írás. Aztán át lehet adni a vezérlést a firmware-nek.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE