APS Logo APS_TUNING [ ABOUT ] [ BLOG ]
[ VISSZA A LOGOKHOZ ]

Reverse Engineering Bosch EDC16: Kalibrációs Pointer Rendszer, MPC555 Direkt Címzés és Mapswitch Architektúra

TIMESTAMP: 2026.04.16 | TARGET: BOSCH EDC16 (MPC555, MPC562, MPC563) - BMW | AUTHOR: APS TUNING | v8

A Bosch EDC16 az egyik legelterjedtebb dízel motorvezérlő platform. BMW, VW, Audi, Ford, gyakorlatilag bármelyik európai gyártó járművében találhatsz belőle valamilyen változatot. A szoftver-architektúra közös, de a visszafejtés korántsem egyszerű. A kalibrációs adatok, a térképek, a karakterisztikák és a konstansok többszintű indirekción át rejtőznek a kódban.

Egy pár BMW platformot vettem górcső alá (DDE5, DDE6.0, DDE6.2, két különböző ECU típuson: EDC16CP35 és EDC16C31), és a végén nagyjából ki lehet jelenteni, hogy a BMW EDC16 kalibrációs architektúrája az MPC562 és MPC563 alapú változatokban gyakorlatilag azonos. Ugyanaz a négyszintű pointer rendszer, ugyanaz a dual variant mechanizmus, ugyanaz a flash mirror. A platformok között csak a konkrét címek térnek el, maga a működés azonos. Egyetlen annotáló script, pár konfigurációs paraméter cseréjével, bármelyik MPC562-alapú BMW EDC16-on elboldogul.

Egy fontos kivétel akad: az EDC16C31 DDE5 (a korábbi E60/E61 530d, 525d motorokban), ami MPC555-re épül, és architektúrájában komoly eltéréseket mutat. Erről a bejegyzés végén lesz külön szó.

Bosch EDC16 MPC562-563 (DDE6.0 / DDE6.2)

Az EDC16 hardvere a Motorola/Freescale MPC562-563 processzorra épül. Ez egy 32 bites PowerPC mag, amit kifejezetten autóipari alkalmazásokra terveztek. A bináris két fő részből áll:

BlokkCímMéretTartalom
Külső Flash 0x000000 - 0x1FFFFF2 MBAlkalmazás kód + kalibrációs terület
Belső Flash 0x400000 - 0x480FFF~524 KBBosch Funktionsbibliothek (OS + standard rutinok)

A belső flash a Bosch standard könyvtára. Ez nagyjából minden EDC16-ban egyforma, és az összes alapvető rutint tartalmazza: interpolációt, matematikát, szűrőket, RTOS-t. Az applikációs kód a 2 MB-os flash-ben ezeket bl MPC:FUN_xxxxx utasításokkal hívogatja.

Hardver memória architektúra (A2L validációval)

Az MPC562-ben mindössze 32 KB belső CALRAM van (0x3F8000 - 0x3FFFFF), ebből a Bosch szoftver alkalmazásszinten 26 KB-ot használ (0x3F9800 - 0x3FFFFF). Ezt a területet az RTOS kernel és az időkritikus ISR rutinok foglalják. A tényleges alkalmazás a külső SRAM chipeken fut, amiket az MPC562 External Bus Interface (EBI) kezel.

Az alábbi felosztás egy 5660 változót tartalmazó A2L fájl elemzésén alapul. A változónevek és a prefixek elég egyértelműen kirajzolják, mire valók az egyes RAM területek.

RégióCímtartományMéretFunkció (A2L elemzés alapján)
INT_CALRAM (on-chip)0x3F9800 - 0x3FFFFF26 KBRTOS kernel, ISR stack, időkritikus változók, 0 wait-state hozzáférés
CS0 - External Flash0x000000 - 0x1FFFFF2 MBApplikációs kód + Kalibrációs terület
EXT_SRAM_A0x6F8000 - 0x6F87FF2 KBMérőpont puffer (kis, kiemelt): 99.2% _mp suffix (Messpunkt), ~128 db kritikus measurement point
EXT_SRAM_B0x7F8000 - 0x7FFFFF32 KBFő alkalmazói RAM (SDA, r13-relatív): 2469 futási változó, state machines, FrmMng, InjCrv, Lambda...
EXT_SRAM_C0x800000 - 0x807FFF32 KBStack + RTOS workspace: 0 nevesített változó az A2L-ben, tiszta futásidejű stack terület
EXT_SRAM_D0x8FE000 - 0x8FFFFF8 KBNagy diagnosztikai mérőpont puffer: 3063 db measurement point, 98.2% _mp suffix

Kulcs felismerés: az _mp suffix a Messpunkt (mérőpont) rövidítése. Ezek nem a tényleges vezérlési változók, hanem shadow másolatok, amiket a BMW gyári diagnosztikai rendszer és például a Test-O olvasgat. A motor igazi vezérlése az EXT_SRAM_B régióban történik (r13-relatív címzéssel), és az eredmények másolatai kerülnek át az EXT_SRAM_A és EXT_SRAM_D pufferekbe, hogy kívülről is megfigyelhetők legyenek.

A CAN kommunikációhoz sok helyen egy dedikált dupla-portos SRAM (DPRAM) chip is található a boardon, ezt a CPU és a CAN-vezérlő közötti adatcserére használják. Ghidrában a memória blokkok beállításánál érdemes ezeket a tényleges méretnél egy kicsit nagyobbra definiálni.

A flash belső logikai felosztása

A 2 MB-os külső flash nem homogén, több, funkcionálisan elkülönített régióból áll. A felosztás szoftver-verziónként változhat, szóval minden új binárisnál érdemes a kód és a kalibráció határait ellenőrizni. A kalibrációs adatok jellemzően két különálló régióban laknak:

RégióTartalomHozzáférés
0x040000 - 0x052000Skalár kalibrációk, flag-ek, konfigurációr2/r14-relatív (SDA direct)
0x0C0000 - 0x0FFFFFMap-ek, táblák, karakterisztikákPointer table (r15)

A pontos határok szoftver-verziónként eltérnek, de a két régió közötti funkcionális elkülönítés (skalár vs térkép) minden BMW EDC16-ban ugyanaz. Ha a kalibrációs terület nem összefüggő, az annotáló scriptnek mindkét tartományt ismernie kell.

Az első lépés: Memory Map validáció

Az MPC562-nél a külső flash a CPU 0x000000 címére van mappolva. Ezt egy egyszerű teszttel lehet ellenőrizni: az MPC56x reset vector-a a 0x100 címen található. Ha ott értelmes utasítás van, akkor a WinOLS offsetek és a CPU címek egybeesnek.

Reset Vector @ 0x00000100
0x100:  48 00 01 82

ba      0x180                 ; Abszolút branch a startup kódra

A ba 0x180 utasítás a tényleges inicializáló kódra ugrik. Ez megerősíti, hogy WinOLS offset = CPU cím, nincs eltérés.

⚠ BTT DDE6 quickflash full backup betöltés: a full backup (internal és external flash egyetlen fájlban) az internal flash-t a 0x200000 file offsettől tárolja. Ez a processzor címterében 0x400000-nak felel meg. Ghidra betöltéskor az external flash-t 0x000000-ra, az internal flash-t 0x400000-ra kell mappolni.
⚠ Intel HEX betöltés: egyes intelhex dumpoknál a flash adat nem feltétlenül 0x000000-tól kezdődik. Az E60 535d HEX fájlban például az első adat 0x008000-nál indul, ami 0x8000 byte-os eltolást jelent a WinOLS nyers binárishoz képest. Ghidrában az Intel HEX loader automatikusan a helyes címekre rakja az adatokat, de WinOLS-ben WinOLS cím = CPU cím - 0x8000 korrekció kell.

A startup: Register poisoning és bázisregiszter inicializáció

A reset vector utáni kód először egy register poisoning mintát hajt végre: az összes általános célú regisztert 0x7F7F7F7F értékkel tölti fel:

Register Poisoning - Debug előinicializálás
lis     r1, 0x7f7f
addi    r1, r1, 0x7f7f
or      r2, r1, r1
...                              ; r3-r31 szintén 0x7F7F7F7F
or      r31, r1, r1

Ez egy debug jellegű előinicializálás: ha bármelyik regiszter használat előtt nem kap tényleges értéket, a 0x7F7F7F7F minta azonnal szemet szúr a memória dumpban. A tényleges bázisregiszter beállítás ezután következik a flash alkalmazáskódban:

CPU Init - Bázisregiszterek
lis     r11, 0x80
addi    r1, r11, 0x0         ; r1 = 0x00800000 - EXT_SRAM_C (stack, downward growing)

lis     r13, 0x80
subi    r13, r13, 0x10       ; r13 = 0x007FFFF0 - EXT_SRAM_B (SDA bázis, minden EDC16-ban azonos)

lis     r2, 0x5
subi    r2, r2, 0x7F10       ; r2 = 0x000480F0 - TOC/SDA2 Base (flash konstansok)

lis     r14, 0x4
addi    r14, r14, 0x6454     ; r14 = 0x00046454 - SDA Base (flash konstansok)

Dual Variant architektúra

Minden BMW EDC16 bináris két szoftver-variánst tartalmaz, és a boot során dől el, melyik fog futni. Az r2 és r14 regiszterek a variáns kiválasztásakor kapják meg a végleges értéküket, és a két lehetőség más-más kalibrációs adathalmazra mutat.

A Variant A és B r2/r14 értékei szoftver-verziónként minimálisan eltérnek, de a mechanizmus azonos. Ennek az architektúrának köszönhetően egyetlen firmware bináris több hardverkonfigurációt is ki tud szolgálni. Elég a variant flag-et módosítani, és az ECU máris teljesen más kalibrációs adatokkal dolgozik.

Pointer relokáció a variánsváltáskor

A dual variant váltás nem merül ki az r2 és r14 regiszterek cseréjével. A boot szekvencia egy relokációs rendszert is lefuttat, ami 7 db RAM-ban tárolt pointert átszámol a régi és az új variáns kalibrációs báziscíme közötti különbség alapján:

Pointer relokáció - Számítási logika
r12 = 0x007FE100                ; fix referencia pont (RAM vége közelében)

; Variant A:
r11 = 0x3E98C                  ; fix offset

; Variant B:
r11 = lwz(r14 - 0x7740) + 0x3E98C  ; flash-ből olvasott delta + fix offset

delta = r12 - r11

; Irány 0 (undo):  new_ptr = delta + old_ptr
; Irány 1 (apply): new_ptr = old_ptr - delta

A négyszintű kalibráció-elérési modell

A BMW EDC16 firmware négyféleképpen fér hozzá a kalibrációs adatokhoz. Ezek a Bosch firmware-architektúra sajátosságai, és minden BMW EDC16-ban jelen vannak.

SzintMechanizmusPélda
1 - r15 Pointer táblalwz rXX, offset(r15) - pointer tábla entry, kalibrációs blokk base[r15 + 0x5CC] = 0x0F5078
2 - Közvetlen SDAlwz rXX, offset(r2) vagy (r14) - közvetlenül kalibrációs adat[r2 + 0x7034] = 0x04F124
3 - Propagációaddi / mr / or - bázisregiszter továbbvitel függvényen belüladdi r3, r31, 0x4FC = 0x0F5574
4 - Flash mirrorlis rXX, imm ; load rYY, offset(rXX) - 0x01000000+ tükörcímlis r11, 0x10c ; lhz r11, 0x3eea(r11) = 0x0C3EEA

A szintek nem hierarchikusak és nem zárják ki egymást: egy függvényen belül is vegyesen megjelenhet a pointer tábla elérés, a közvetlen SDA hozzáférés és a flash mirror minta. A firmware ezeket párhuzamosan, hibrid módon használja.

Szint 1: Az r15 pointer tábla

Az r15 regiszter instance pointerként viselkedik: a kalibrációs pointer tábla báziscímére mutat. A beállítás mechanizmusa minden BMW EDC16-ban azonos. Egy feltételes logika választ két lehetséges forrás közül (instance A vagy B), aztán az eredmény r15-be kerül:

r15 inicializáció - CAL_SetActiveDataset
; Kritikus szekció belépés (interrupt disable):
bl      FUN_0001f590          ; OSEK TaskEnter

; Instance selector ellenőrzés:
lbz     r12, CAL_MapSelector    ; aktív dataset index
cmpwi   r12, 0x1
bne     else
lbz     r12, CAL_OperatingMode  ; allokációs állapot
cmpwi   r12, 0x4             ; 4 = RAM másolat kész, váltás engedélyezve
bne     else

; Instance A (allokált RAM másolat):
lwz     r31, CAL_ActivePtr_Alt  ; RAM pointer tábla
b       done

else:
; Instance B (default, flash pointer tábla):
lwz     r31, CAL_ActivePtr_Default  ; flash tábla cím

done:
lwz     r12, PTR_TABLE_REF(r2) ; flash pointer tábla referencia cím
addi    r15, r31, 0x0       ; r15 = kiválasztott instance
subf    r12, r12, r31      ; offset = r15 - referencia cím
stw     r12, CAL_ActiveOffset  ; offset mentése

; Kritikus szekció kilépés:
b       FUN_0001f5b8          ; OSEK TaskExit (tail call)

A teljes r15 beállítás egy atomi művelet: interrupt disable/enable párossal van védve (OSEK kritikus szekció), hogy a pointer váltás közben ne történhessen félkész állapotú olvasás. A CAL_SetActiveDataset függvény neve, paraméterei és belső szerkezete minden BMW EDC16-ban azonos, csak a konkrét RAM címek térnek el.

A Pointer Tábla

A pointer tábla egy sűrű, 4 bájtos bejegyzésekből álló tömb a flash-ben. Minden entry formátuma:

[ byte0: dataset ID | 24-bit: abszolút flash cím ]

A pointer tábla báziscíme és az entry-k száma szoftver-verziónként eltér (tipikusan 380-460 entry a 0x050000 - 0x060000 tartományban), de a formátum és a feloldási mechanizmus univerzális. A legmegbízhatóbb út a megtaláláshoz: azonosítsd a CAL_SetActiveDataset függvényt, és oldd fel az r2-relatív referenciát.

Az A2L szimbólum-import után a pointer tábla valódi modulneveket kap. Minden entry egy-egy Bosch szoftver-modul kalibrációs blokkjára mutat, a Bosch prefix konvenció pedig elárulja, mire való: ExeMon a végrehajtás monitor, OvRMon a fordulatszám monitor, FrmMng a CAN frame manager, AirCtl a levegő szabályozás, CoEng a motor koordinátor, InjCrv a befecskendezés, CrCtl a tempomat. A kisebb modulok csak egy-két konstanst tartalmaznak, a nagyobbak (InjCrv, FrmMng, AirCtl) viszont több ezer byte-nyi térképet és karakterisztikát.

Pointer tábla A2L modul nevekkel Ghidrában

A pointer tábla Ghidrában, A2L szimbólum-import után: minden 4 byte-os entry egy Bosch kalibrációs modul nevét és flash címét tartalmazza.

Szint 2: Közvetlen SDA hozzáférés (r2/r14-relatív)

Az r2 és r14 regiszterek az SDA2 és SDA bázisként szolgálnak. Az SDA2 címzési tartomány (r2 plusz/mínusz 0x8000) tipikusan a 0x040000 - 0x052000 kalibrációs régiót fedi le, ahol a skalár konstansok, flag-ek és a pointer tábla bázisa is megtalálható:

Közvetlen SDA hozzáférés - r2-relatív
lwz     r12, 0x7034(r2)      ; r2 + 0x7034 = kalibrációs adat
lhz     r3, -0x1a6e(r2)      ; r2 - 0x1A6E = skalár konstans

A nagy kalibrációs térképek (0x0C0000 - 0x0FFFFF) kívül esnek az r2 címzési hatótávolságán. Ott a pointer tábla (Szint 1) az elsődleges út, míg az r2/r14-relatív hozzáférés inkább a skalár konfigurációkat szolgálja ki.

Szint 4: Flash mirror abszolút hozzáférés

A 2 MB-os külső flash nem csak a 0x000000 - 0x1FFFFF tartományban érhető el, hanem tükörképként a 0x01000000 - 0x011FFFFF címen is. Ez az MPC562 EBI chip select konfigurációjából adódik, és minden EDC16-ban jelen van.

Flash mirror hozzáférés - lis + load minta
lis     r11, 0x10c            ; r11 = 0x010C0000
lhz     r11, 0x3eea(r11)     ; [0x010C3EEA] = flash tükörkép, valós cím: 0x0C3EEA
sth     r11, 0x77e(r13)      ; eredmény a RAM-ba

Ghidrában a 0x01000000 bázison egy 2MB-os overlay memory block létrehozásával a piros (unresolved) referenciák feloldódnak.

A kalibrációs rendszer működés közben

Eddig azt néztük meg, hogyan van felhúzva maga az infrastruktúra: milyen úton-módon jut el a kód a kalibrációs adatokig. Most jöjjön két konkrét modul, amin keresztül látszik, hogy a firmware ténylegesen mihez kezd ezekkel az adatokkal, amikor a motort kell vezérelni.

Eset 1: Main Torque Limiter

A fő nyomaték limiter dolga egyszerű: a fordulatszám és a variant coding alapján megmondja, mennyi nyomatékot szabad kiadni. Egyetlen pointer tábla bejegyzésből dolgozik, semmi több.

Main TQ Limiter Ghidra listing

A Main Torque Limiter wrapper függvény Ghidrában, az annotátor által feloldott pointer referenciákkal.

Main Torque Limiter
lwz     r31, 0x268(r15)        ; PTR[0x268] = kalibrációs blokk (408 byte)
lha     r3, -0x6844(r13)       ; r3 = RPM (X tengely)
lbz     r4, -0x7793(r13)       ; r4 = Variant Coding (Y tengely)
addi    r5, r31, 0xa           ; r5 = map header
subi    r6, r13, 0x4216        ; r6 = interpolátor cache (RAM)
bl      FUN_002541cc             ; 2D INTERPOLÁCIÓ
Main TQ Limiter map WinOLS

A Main Torque Limiter 21x8 térkép a WinOLS-ben. X tengely: variant coding (0-7), Y tengely: RPM (600-5060), Z: nyomaték Nm-ben.

A 2D map struktúrája a flash-ben:

OffsetTípusTartalom
+0x00int16X_size = 21 (RPM tengelypont darabszám)
+0x02int16Y_size = 8 (variant coding tengelypont darabszám)
+0x04int16[21]RPM töréspontok: 600, 800, 1000, ... 5060
+0x2Eint16[8]Variant coding: 0, 1, 2, ... 7
+0x3Eint16[168]Nyomaték értékek (21 x 8, Nm)

A 2D interpolátor bilineáris interpolációt csinál 16.16 fixpontos aritmetikával, cached tengely pozícióval. A "cached" itt azt jelenti, hogy az interpolátor megjegyzi, hol járt legutóbb a tengelyen, és a következő híváskor onnan indul. Ha az RPM az előző hívás óta alig változott, ami motornál elég gyakori helyzet, a szegmens megkeresése szinte azonnal megvan.

2D interpolátor Ghidra listing

A 2D interpolátor függvény eleje: X_size és Y_size olvasás, majd az X tengely keresés indítása.

Az interpoláció után van még egy érdekes kis részlet, egy korrekciós ág:

Korrekciós ág - Enable byte ellenőrzés
lbz     r11, 0x1(r31)         ; korrekciós enable flag
cmpwi   r11, 0x0
beq     skip                  ; ha 0: nincs korrekció

; korrekciós szorzás (SOHA nem fut, mert az enable byte minden kalibrációban 0):
lha     r11, -0x687e(r13)    ; runtime szorzó
mullw   r3, r12, r11        ; result x factor
li      r4, 0x2000           ; 8192 = Q13 fixpont (1.0x)
bl      FUN_0025380c          ; r3 = (result x factor) / 8192, szaturált

skip:
sth     r3, -0x720e(r13)     ; output = Main TQ Limiter eredmény

Ez jól mutatja a Bosch fejlesztési filozófiáját: a kódban ott van egy runtime korrekciós szorzó, de a konkrét kalibrációban ki van kapcsolva. A Bosch modulárisan fejleszt, a funkciókat pedig kapcsolókkal engedi vagy tiltja. Ami az egyik OEM kalibrációban aktív, a másikban simán ki lehet kapcsolva, a kód viszont mindig ott marad, ha esetleg kell.

Eset 2: Boost Pressure Control - Töltőnyomás szabályozás

A boost modul egy tekintélyes méretű függvény, és egyetlen pointer tábla bejegyzésből szedi ki az összes adatát: PTR[0x558], ami egy 4492 bájtos kalibrációs blokkra mutat. Ezen a blokkon belül több mint 20 térkép és konstans van.

Boost module map selection Ghidra

A boost modul map-választó logikája: egy bit-teszt dönti el, hogy a base vagy az alternatív boost target map-et olvassa.

Base boost target kiválasztás
lwz     r31, 0x558(r15)        ; PTR[0x558] = boost kalibráció (4492 byte blokk)
lha     r3, -0x6844(r13)       ; r3 = RPM
lha     r4, -0x6df0(r13)       ; r4 = befecskendezési mennyiség (mg/cyc)

lbz     r12, runtime_flag      ; runtime flag byte
rlwinm. r12, r12, 0x1f, 0x1f, 0x1f  ; bit 1 teszt
beq     use_base              ; bit 1 = 0: base map

; bit 1 = 1: alternatív boost map
addi    r5, r31, 0x568        ; alt boost target
b       call_interp

use_base:
addi    r5, r31, 0x9fe        ; base boost target

call_interp:
bl      FUN_004541cc          ; 2D INTERPOLÁCIÓ, r3 = boost target [mbar]
Base boost target map WinOLS

A base boost target 16x16 térkép a WinOLS-ben. X tengely: befecskendezés (mg/cyc, 0.01 faktor), Y tengely: RPM, Z: kívánt töltőnyomás mbar-ban.

A base boost target után a kód gyors egymásutánban két DPF regenerációhoz kötődő boost map-et is beolvas:

DPF regen boost maps Ghidra

A DPF regeneráció phase 1 és phase 2 boost map hívások egymás után.

DPF Regen boost map-ek - ugyanazon pointer tábla bejegyzésből
; DPF Regen Phase 1 boost target:
addi    r5, r31, 0x572        ; regen phase 1 boost
bl      FUN_004541cc          ; 2D INTERPOLÁCIÓ

; DPF Regen Phase 2 boost target:
addi    r5, r31, 0x7b6        ; regen phase 2 boost
bl      FUN_004541cc          ; 2D INTERPOLÁCIÓ

Tehát három boost target fekszik párhuzamosan kiolvasva (base/alt, regen phase 1, regen phase 2), és a modul későbbi logikája dönti el, melyiket küldi tovább célértékként a szabályozóra.

A boost blokk barometrikus korrekciói

A boost blokkon belül négy olyan térkép is van, ami a légköri nyomást használja Y tengelyként (RPM x baro):

Boost limit vs atmospheric pressure WinOLS

A boost limit vs légköri nyomás térkép a WinOLS-ben: az egyetlen ténylegesen kalibrált barometrikus térkép ebben a szoftverben.

Négyből viszont csak egy aktív. Háromféle korrekciós mechanizmus van beépítve a kódba (additív, multiplikatív, és egy abszolút limit), de ebben a BMW kalibrációban a BMW kalibrátorok csak a limit táblát kapcsolták be. Szokásos Bosch mintázat, mindent beletesznek a szoftverbe, aztán az OEM kalibrátor eldönti, melyikkel akar dolgozni.

A boost blokk teljes térképe

Egyetlen pointer tábla entry mögött 22+ kalibrációs objektum rejtőzik:

OffsetFunkció
+0x3CBaro korrekció (additív, inaktív)
+0xE0Baro korrekció (multiplikatív, semleges)
+0x1841D karakterisztika
+0x19E2D map
+0x202, +0x224, +0x23E1D karakterisztikák
+0x25C, +0x3B0, +0x5042D map-ek (mg/cyc x RPM)
+0x568Alt boost target
+0x572DPF regen phase 1 boost
+0x7B6DPF regen phase 2 boost
+0x9FEBase boost target
+0xC42Baro korrekció (additív, inaktív)
+0xE86Boost limit vs légköri nyomás
+0x10CE2D map (RPM x mg/cyc)
+0x1134, +0x113832-bit kompárátorok
+0x1144Rate limiter / ramp
+0x1150, +0x1154Threshold konstans + 1D karakterisztika

Automatizáció: A Ghidra Annotáló Script

Miután a manuális nyomkövetéssel megértettük a rendszert, egy Ghidra Python script segítségével az egészet automatizálhatjuk. A script mind a négy szinten dolgozik egyszerre:

4 level script Ghidra

A Ghidra annotáló script eredménye.

SzintMit csinál
L1: r15 pointer táblaMinden lwz rX, offset(r15) mellé beírja a feloldott pointert és méretet
L2: r2/r14 SDA directMinden r2/r14-relatív load/store mellé kiírja a kalibrációs címet
L3: Regiszter propagációaddi, mr, or másolásokat követ, A2L symbol lookup
L4: lis + load mirrorlis rX, imm ; load rY, offset(rX) feloldja a mirror címet

A script egyetlen konfigurációs blokkból dolgozik, amelyben 5 paramétert kell az adott binárishoz beállítani: pointer tábla báziscím, r2 érték, r14 érték, és a két kalibrációs tartomány határai. Ez az összes. A négy szint felismerési logikája és maga az annotáció generálás teljesen univerzális, nem kell hozzányúlni.

A script háromféle módban tud futni, attól függően, hogy milyen kalibrációs információ van a kezünk ügyében:

Teljes hybrid konfigurációval futtatva a script ~17,800 annotációt szór szét a kódban, ~1,700 függvényben, egy ~3,870 függvényből álló binárisban. Ez a szám minden tesztelt BMW EDC16 binárison szinte hajszálpontosan ugyanaz - ami nem véletlen egybeesés, hanem az egységes architektúra egyik legjobb bizonyítéka.

A kódbázis regiszter-statisztikájából is szépen kilátszik a multi-regiszter architektúra:

RegiszterFunkciók számaMegjegyzés
r31190Leggyakoribb (hagyományos base regiszter)
r12178Scratch regiszter, közvetlen pointer felhasználás
r3085Második leggyakoribb dedikált base
r2948Pl. CoEng (DPF nyomatékkorlátozás) modul
r2829Pl. Rail Pressure Control modul
r27-r2438Ritkábban használt base regiszterek

DAMOS integráció

Ahol a DAMOS (a Bosch kalibrációs adatbázisa) rendelkezésre áll, ott a pointer címek helyére beírhatók az igazi funkcionális nevek:

DAMOS annotáció példa - Légmennyiségmérő diagnosztika
lwz     r12, 0x80(r15)       PTR[0x80], AFSCD_DebPlHiSetyDrftDef_C
addi    r4, r12, 0x27e      calib: AFSCD_swtSensInstVal_C
li      r3, 0x15
bl      FUN_0007df84

Térképstruktúra és testreszabás

Eddig arról volt szó, hogyan találjuk meg a kalibrációs adatokat. De mit kezdjünk velük, ha egyszer megvannak? A Bosch 2D interpolátor a térkép méretét dinamikusan olvassa a headerből, és ez elképesztően hasznos tulajdonság: azt jelenti, hogy a térképeket bővíthetjük anélkül, hogy a kódhoz hozzá kellene nyúlnunk.

A 2D térkép bináris felépítése

Minden 2D kalibrációs térkép pontosan ugyanúgy néz ki a flash-ben. A 2D interpolátor futásidőben olvassa ki a méreteket a header első két szavából, és ebből számolja ki, hol vannak a tengelyek és a Z adatok:

2D Map bináris struktúra (Bosch II header)
+0x00:  int16    ; X_size (pl. 0x0010 = 16 pont)
+0x02:  int16    ; Y_size (pl. 0x0010 = 16 pont)
+0x04:  int16[X] ; X tengely töréspontok (pl. RPM: 500, 750, 1000, ...)
+0x04+X*2: int16[Y] ; Y tengely töréspontok (pl. Nm: 0, 20, 40, ...)
+0x04+X*2+Y*2: int16[X*Y] ; Z értékek (interpolált kimeneti adat)

Egy térkép összmérete tehát: 2 + X*2 + Y*2 + X*Y*2 byte (a 4 byte-os header, plusz a két tengely, plusz a Z mátrix). A lényeg az, hogy a kód sehol nem tartalmaz beégetett méretet - mindent menet közben, a headerből olvas ki. Ez lesz a kulcsa annak, hogy a térképeket bővíteni is tudjuk, de erről mindjárt.

Az interpolátor működése

A 2D interpolátor (FUN_00457794 és társai) lépésről lépésre kb. ezt csinálja:

2D interpolátor - Működési elv
; 1. Header olvasás:
lhz     r11, 0x0(r5)          ; X_size = header[0]
lhz     r12, 0x2(r5)          ; Y_size = header[1]

; 2. X tengely keresés (cached):
;    Lineáris keresés a töréspontok között, az előző hívás
;    indexéből indulva (gyors, ha az RPM alig változott)

; 3. Y tengely keresés (cached):
;    Ugyanaz, a Y_size alapján számított offset-ről

; 4. Bilineáris interpoláció:
;    16.16 fixpontos szorzás a 4 szomszédos Z értékből
;    Eredmény: int16 a hívó regiszterbe (r3)

A cached tengely keresés lényege, hogy az interpolátor eltárolja az előző hívás X és Y szegmens indexét egy RAM-ban lévő cache struktúrában (amit r6-ban kap paraméterként). Ha a bemenet alig változott - márpedig motornál ez a tipikus, az RPM és a terhelés nem szokott ciklusról ciklusra ugrálni - a keresés 1-2 összehasonlítás után már meg is van.

Térképek bővítése

Mivel az interpolátor dinamikus, a térkép méretét simán megváltoztathatjuk a header átírásával. Mondjuk ha az eredeti térkép 16x16-os, és mi 32x16-ra akarjuk nyújtani:

16x16 (eredeti)32x16 (bővített)
Header4 byte4 byte
X tengely32 byte64 byte
Y tengely32 byte32 byte
Z adat512 byte1024 byte
Összesen580 byte1124 byte

A Bosch fejlesztési folyamatban a kalibrátorok menet közben hangolgatják a térképméreteket. Ehhez a kalibrációs blokkokban a térképek mögött sokszor padding (tartalék terület) van hagyva, ezekben az ecukban jellemzően 50-200 byte a számunkra releváns térképenként (NM-IQ konverzió vagy injektor kivezérlési idő). Ha a bővítés belefér ebbe a paddingbe, elég a headert és az adatokat átírni, a pointer offset-ek és a szomszédos térképek nyugodtan maradhatnak a helyükön.

Ha viszont a bővítés nem fér bele a paddingbe, akkor a megnövelt térképet szabad flash területre kell átköltöztetni, és a pointer/offset referenciát átírni az új címre. Ez assembly szintű patch-csel elméletileg megoldható: az eredeti addi r5, r31, offset utasítást kicseréljük egy lis r5, HI; ori r5, r5, LO abszolút címzésre, de a buktatók majd alább.

Gyakorlati példa: Nyomaték/Mennyiség átváltás bővítése

Az Nm/IQ (nyomaték/befecskendezési mennyiség) átváltó térkép az egyik legfontosabb kalibrációs objektum a motorvezérlőben. Az eredeti 16x16-os változatban 550 Nm-ig vannak töréspontok, ami egy gyári kalibrációhoz tökéletesen elég - de ha Stage 2+ tuningot csinálunk, ennyi már kevés.

32x16 Nm-IQ conversion map

A kibővített 32x16 Nm-IQ átváltó térkép: az X tengely 0-tól 1400 Nm-ig, 32 töréspont. Az eredeti 16 pontos tengely dupla felbontásra bővítve, a 650 Nm feletti tartomány extrapolált értékekkel feltöltve.

A bővítés menete:

LépésMit csinálunk
1. Padding ellenőrzésMegnézzük, mennyi szabad hely van a térkép vége és a következő objektum headere között
2. Maximum méret kalkulációA szabad hely alapján: (hely - 2 - Y*2) / (1 + Y*2) = max X_size
3. Header átírásX_size módosítása 16-ról 32-re (egyetlen szó a flash-ben)
4. X tengely bővítésÚj töréspontok hozzáadása (pl. 650, 700, 750, ... 1400 Nm)
5. Z adat kitöltésA meglévő adatokból lineáris extrapoláció az új tartományra

A következő induláskor az interpolátor a 32-es X_size-t olvassa ki a headerből, és 32 töréspontot keres az X tengelyen. A kód nem változott, csak az adat.

A maximális bővítés mértéke mindig a padding méretétől függ. Egy 624 értéknyi szabad hely mellett, 16 pontos Y tengellyel, a max X méret 35. A 32x16 a gyakorlatban ideális kompromisszum: van elég felbontás a kiterjesztett nyomatéktartományhoz, és még marad is tartalék a paddingben.

Kivétel: EDC16C31 DDE5 - Az MPC5xx világ

Az eddig tárgyalt EDC16 platformok (CP35, DDE6) mind MPC562/MPC563 processzorra épülnek, 2 MB külső flash-sel és az r15 pointer táblás kalibrációs címzéssel. A korábbi generációs EDC16C31 DDE5 (BMW E46/E60/E61 20d, 25d, 30d első szériák) viszont teljesen más hardveren fut: a Motorola MPC555 processzoron.

MPC555: Más hardver, más világ

Az MPC555 az MPC500 családba tartozik, és az MPC562-höz képest komolyan eltér a memória-architektúrája:

JellemzőEDC16 MPC562/563 (DDE6.0/DDE6.2)EDC16C31 MPC555 (DDE5)
ProcesszorMPC562/MPC563MPC555
Internal Flash0x400000 - 0x480FFF (~512 KB)0x000000 - 0x06FFFF (448 KB)
External Flash0x000000 - 0x1FFFFF (2 MB)0x800000 - 0x8FFFFF (1 MB)
Internal SRAM0x3F8000 - 0x3FFFFF (32 KB CALRAM)SRAM A: 0x3F9800 - 0x3FBFFF (10 KB)
SRAM B: 0x3FC000 - 0x3FFFFF (16 KB)
External SRAM0x6F8000 - 0x8FFFFF (több chip)0x400000 - 0x401FFF (~8 KB)
r130x007FFFF0 (EXT_SRAM_B)0x004017F0 (EXT_SRAM)
Kalibráció címzésr15 pointer táblaKözvetlen (direkt) címzés

Direkt kalibrációs címzés - r15 pointer tábla nélkül

A legfontosabb architekturális különbség: az MPC555 alapú EDC16C31-ben nincs r15 pointer tábla. A kód közvetlenül, abszolút címzéssel hivatkozik a flash kalibrációs területre:

MPC563 (r15 pointer tábla) vs MPC555 (direkt címzés)
; MPC563 EDC16CP35 DDE6 
; Két lépcsős indirekcó:
lwz     r31, 0x268(r15)      ; 1. pointer tábla - blokk cím
addi    r5, r31, 0xa         ; 2. blokkon belüli offset
bl      interpolate_2D

; MPC555 EDC16C31 DDE5
; Közvetlen flash hivatkozás:
lis     r5, 0x8C              ; r5 = 0x008C0000 (ext flash kalibrációs terület)
addi    r5, r5, 0x3FE4       ; r5 = 0x008C3FE4 (konkrét térkép cím)
bl      interpolate_2D

Az MPC555 DDE5-ben a kalibrációs címek egyenesen bele vannak égetve az applikációs kódba. Nincs pointer tábla, nincs indirekcó, nincs CAL_SetActiveDataset. Minden térkép, karakterisztika és konstans egy fix lis + addi (vagy lis + ori) abszolút címmel elérhető.

Ez az egyszerűbb felépítés a statikus elemzésnek kifejezetten kedvez (Ghidrában azonnal látszik, hova mutat egy hivatkozás), viszont a kalibrációs terület módosítását nehezíti: ha áthelyezünk egy térképet, az összes kódreferenciát meg kell keresni és átírni. Az MPC562 r15 pointer táblás megoldásánál ehhez képest egyetlen pointer entry módosítása elég.

Az MPC555 DDE5 bináris két részből áll a flash fájlban: az internal flash (0x000000 - 0x06FFFF, 448 KB) és az external flash (file offset 0x070000-tól, CPU cím 0x800000 - 0x8FFFFF, 1 MB). Ghidrába külön memory block-ként kell betölteni őket, az external flash-t 0x800000 báziscímre mappolva.

Ghidra r13 konfigurálás MPC555-höz

Az MPC555-nél az r13 = 0x004017F0, és a signed 16-bit offset tartomány (+-0x8000) mindkét SRAM tartományt lefedi: a belső SRAM-ot negatív offset-tel (pl. r13 - 0x4D08 = 0x3FCAE8, ez SRAM B-be esik), a külső SRAM-ot meg pozitív offset-tel. Ghidrában a regiszter beállításnál erre kell figyelni: az assume r13 = 0x4017F0 a helyes érték, nem a 0x7FFFF0, amit az MPC562-nél használunk.

Mapswitch: Miért nehéz?

A tuning közösségben a "mapswitch" (futásidőben több kalibrációs térkép közötti váltás) az egyik legkeresettebb funkció. Elméletben egyszerűnek hangzik: ha az ECU a pointer táblán vagy közvetlen címzésen át éri el a kalibrációt, irányítsuk át a pointert vagy a címet egy másik térképre, és kész. A gyakorlatban viszont ez jóval bonyolultabb mint gondolnánk.

A Bosch nyomaték-pipeline és a konzisztencia probléma

A modern Bosch EDC16 firmware nem egy egyszerű "térkép be - output ki" rendszer. A nyomatékvezérlés egy többszintű pipeline: több párhuzamos alrendszer számol nyomatéklimiteket (füst limiter, boost limiter, váltóvédelem, motorvédelem), és ezeket egy arbiter MIN-eli össze. Ráadásul a Bosch egy belső konzisztencia-ellenőrzőt is üzemeltet, ami figyeli, hogy a különböző alrendszerek számolt értékei összhangban vannak-e.

Ha egy mapswitch patch csak egyetlen alrendszert piszkál meg (pl. a fő nyomaték limitert), miközben a többi szépen a stock kalibrációból számolgat tovább, a konzisztencia-ellenőrzés eltérést fog észlelni. Az eredmény nem szép: ECU leállás, derate mód, vagy CAN kommunikáció korrupció.

Az r15 base swap csapda

Az egyik kézenfekvőnek tűnő ötlet: az r15 pointer tábla báziscímét futásidőben átírjuk egy klón pointer tábla címére, ahol a kívánt térképek más adatokra mutatnak. Ez elméletben az összes r15-felhasználót egyszerre irányítaná át. A baj ott van, hogy a Bosch variant switch infrastruktúra (a CAL_SetActiveDataset és a hozzá tartozó callback dispatch) egy atomi műveletet hajt végre bootkor:

A Bosch variant switch atomi szekvencia
; 1. Base pointer kiválasztás és tárolás:
stw     r3, CAL_BasePtr(r13) ; r15 base pointer - RAM

; 2. Callback dispatch (12 entry, 12 byte/entry):
bl      CAL_NotifySubscribers  ; minden feliratkozott modul értesítése
; Eredmény: 12 derived érték frissítve a RAM-ban
; A base pointer ÉS a 12 derived érték EGYÜTT konzisztens

Ha egy mapswitch patch csak a base pointert írja felül, de nem hívja meg a callback dispatch-et, a 12 derived érték stale állapotban marad. A Bosch belső logika erre rájön: az r15 base az "új" kalibrációra mutat, de a derived értékek még a "régiből" valók. A tesztjeim alapján az eredmény: CAN frame korrupció, gázpedál nem reagál, hamis riasztás motor túlmelegedés miatt.

A megoldás: végső output cella felülírás

A megbízható megközelítés nem a pointer-szintű átirányítás, hanem a nyomaték-pipeline végső kimeneti cellájának felülírása. A Bosch torque pipeline végén van egyetlen RAM cella, ami az interpolált nyomaték limiter értéket tartalmazza. Ezt egyetlen downstream modul olvassa, és ami a lényeg: nincs rajta konzisztencia-ellenőrzés.

Ha a mapswitch patch az összes Bosch számolást békén hagyja (stock pointer tábla, stock base, stock variant switch), és csak ezt a végső output cellát írja felül egy kívánt értékkel, semmilyen belső ellenőrzés nem sérül. A többi alrendszer (füst limiter, boost limit, váltóvédelem) változatlanul működik, a motor védelme biztosított marad.

Végső output cella felülírás (SOI-stílusú RAM override)
; Hook: közvetlenül a stock nyomaték-kimenet írása UTÁN
lbz     r12, map_counter(r13)   ; aktív térkép index (0/1/2)
cmpwi   r12, 0x0
beq     skip                    ; map 0 → stock, ne módosíts

; Kalibráció-vezérelt felülírás:
lis     r11, CAL_TQ_HI
ori     r11, r11, CAL_TQ_LO
lha     r3, 0x0(r11)           ; r3 = TQ override érték (kalibrációból)
sth     r3, TQ_output(r13)     ; felülír a kívánt értékkel

skip:
; Stolen instrukciók (a hook által kiszedett eredeti kód):
addi    r1, r1, 0x10           ; stack frame felszabadítás
blr                              ; visszatérés

Az override érték a flash kalibrációs területén van tárolva, WinOLS-ben map definícióként kalibrálható, és maga a kód soha nem módosul. Ez a "data driven patch" filozófia: a logika a cave-ben van, az értékek meg a kalibrációs területen. Ha változtatni akarunk, csak az adatot piszkáljuk, a patch-hez nem kell hozzányúlni.

Záró gondolat

Négy BMW platform, két ECU típus (CP35, C31), négy szoftver-verzió, egy végeredmény: a BMW EDC16 kalibrációs architektúra az MPC562/563-alapú változatokban egységes. Ugyanaz a pointer tábla, ugyanaz a dual variant, ugyanaz a flash mirror, ugyanaz a script - csak más paraméterekkel. Az MPC555-ös DDE5 egy korábbi generáció, ami direkt címzéssel dolgozik: egyszerűbb statikus elemzés, de nehezebb testreszabás.

A mapswitch fejezet megmutatta, miért nem triviális a többtérképes konfiguráció: a Bosch konzisztencia-mechanizmusai nem tűrik a félkész módosítást. A megbízható út nem a pointer rendszer megkerülése, hanem a pipeline végső output cellájának kontrollált felülírása - ott, ahol a Bosch belső logika már nem ellenőriz.

A tanulság: ha egy megközelítés több platformon is ugyanazt az eredményt adja, az nem véletlen, hanem rendszerszintű igazolás. Az annotáló script bármelyik MPC562-alapú BMW EDC16-on működik, a konfigurációs paraméterek cseréjével. A gépezet ugyanaz, csak a címek mások. Az MPC555-höz a direkt címzési mód felismerése kell, de az annotálási logika ott is adaptálható.

< VISSZA A LOGOKHOZ