STM32: Keine Interrupte nach Vector Table Relocation

Gast #5727243
Lesenswert?

Hallo Forum,

ich habe auf einem STM32F103 das Problem, dass nach einer Vector Table 
Relocation keine Interrupte mehr auslösen.

Um diesem Fehler zu ergründen, habe ich mit der aktuellen STM32CubeMX 
eine Testapplikation erstellt, die nur Systick und Timer2 bedient und 
einen IR auslösen kann.
Die ISR von Timer2 inkrementiert eine Variable je um eins.

Diese Applikation läuft ohne Probleme wenn...
...sie ohne VT Offset läuft,
...sie mit VT Offset läuft, die vorgannte Variante aber zuvor nicht aus 
dem Flash gelöscht wurde.

Wenn jedoch der Flash zuerst gelöscht und dann aufgespielt wird, lösen 
die IR nicht mehr aus.

Ich habe die betreffenden Register zwischen funktionierender und nicht 
funktionierender Applikation verglichen...SETENA, CLRENA sind gesetzt 
für Timer2.
PRIMASK, BASEPRI, FAULTMASK sind 0, das ist auch OK.
VTOR berücksichtigt den Offset 0x5000, also auch OK.

Allerdings habe ich bei Programmstop SCB->ICSR einen Unterschied 
feststellen. VECTACTIVE ist dort 0x3. Nach Doku wäre das IR 3-16=-13, 
der gerade aktiv ist. Was für ein Interrupt ist das? Wäre das Hardfault?

Mir fehlt da an der Stelle ein bisschen der Ansatz. Kann mir jemand vllt 
einen Hinweis geben...?

Gruß
Gast #5727266
Lesenswert?

Es reicht nicht den Code an anderer Stelle im Flash abzulegen und den 
Zeiger auf die Vektortabelle anzupassen. Am Anfang des Flash muss 
zusätzlich eigener Code (ein Jump oder eine geeignete VT) stehen, der 
die Code-Ausführung an das Programm weiter gibt.
(Habe lange keine STMs mehr programmiert. Hoffentlich verwechsel ich da 
nichts.)
Gast #5727279
Lesenswert?

Hallo,

ich tue drei Dinge, damit VTOR wirksam wird:

Linkerfile anpassen:
…
MEMORY
{
FLASH (rx)      : ORIGIN = 0x08005000, LENGTH = 108K   // <-hier
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 20K
}
…

System_stm32f1xx.c anpassen:
…
#define VECT_TAB_OFFSET  0x5000 /*!< Vector Table base offset field.
                                  This value must be a multiple of 
0x200. */
…

Target Software Startup Script anpassen (Truestudio):
...
# Reconfig vector table offset reg to match pp location
set *0xe000ed08 = 0x5000

# Get app stackpointer
set $sp = *(unsigned int*)0x5000

# Get app entry point
set $pc = *(unsigned int*)0x5004
...


Habe ich etwas vergessen...?

Gruß
#5727337
Lesenswert?

Jump schrieb:
> Ich meine schon. Der STM32 startet doch immer bei 0x08000000. Oder
> nicht?

Glaube ich auch

Wo befindet sich der Code deines Startup scripts? du könntest im Linker 
Script noch eine section von 0x08000000 bis 0x08005000 definieren in die 
du den startup code legst, der dann entweder gleich alles dort macht 
oder einfach zum echten startup code auf 0x08005000 springt
Gast #5727405
Lesenswert?

Ich entwickle meine Programme immer bei 0x08010000. Erst das Release 
wird für 0x08000000 kompiliert. Grund: Es kommt vor, dass ich zigtausend 
Flashvorgänge bis zum Release brauche. Auf diese Weise lässt selbst ein 
STMF030 (erlaubt wären dort nur 1000 Zyklen) mit gutem Gewissen 
debuggen.

Möglicherweise wusste ich nicht wie man eine "bootloader section" 
erstellt und habe mir deshalb mit einem zweiten Programm geholfen. Das 
zweite Programm müsste nur einmalig als Starterprogramm nach 0x08000000 
geflasht werden. Für "BrilleAuf" könnte es so aussehen:
1
int main(void)
2
{
3
  const void* ImgPtr = (void*)0x08005000;
4
  void (*pMainApp)(void) = *(void (**)(void))(ImgPtr+4);
5
  pMainApp();
6
  return 0;
7
}
Gast #5727414
Lesenswert?

Alex D. schrieb:
> Dann wäre es am einfachsten schon jetzt eine bootloader section
> einzubauen und dort nur einen jump zur application machen. Sollte dann
> evtl funktionieren.

Auch wenn er explizit 0x5000 ins VTOR schreibt?

BrilleAuf schrieb:
> Target Software Startup Script anpassen (Truestudio):
> ...
> # Reconfig vector table offset reg to match pp location
> set *0xe000ed08 = 0x5000

Wie gesagt, TrueStudio hab ich nicht.
Also eigentlich bin ich ja auch der Meinung, es ist besser, sofort ein
Bootloader-Placebo zu benutzen.
Dann kann man auch gleich testen ob es ohne Debugger startet.
Man weiss ja nicht so genau, was die da sonst noch so alles
initialisieren.
#5727424
Lesenswert?

Darth Moan schrieb:
> Auch wenn er explizit 0x5000 ins VTOR schreibt?

Denke schon, habe auch mal schnell mit einem Debugger getestet: bei 
normalem boot (boot0 == 0 && boot1 == 0) wird 0x8000000 auf 0x0 
remapped, das heißt auf 0x5000 ist der gleiche speicher wie auf 
0x8005000. Ist vielleicht nicht der beste Stil und ziemlich verwirrend, 
aber sollte (denke ich) funktionieren.
Gast #5727440
Lesenswert?

Es kann doch VTOR nur per -Programm- beschrieben werden. Der Start ist 
dann aber schon geschehen. Die Startadresse in der Vektortabelle wird 
also nicht mehr berücksichtigt. Das heisst, es muss -vorher- für 
passende Daten bei 0x08000000 (eine VT evtl. mit Code) für den Sprung 
zum Programm gesorgt werden. Entweder per zweites Programm oder Einträge 
im Linkerskript evtl. automatisch generiert infolge eines 
Häkchens/Befehls in Truestudio.

"BrilleAuf" hat das Problem vermutlich schon gelöst.
#5727493
Lesenswert?

Du musst zunächst einen Dummy-Bootloader (oder auch schon den echten 
Bootloader) flashen der

* Sicherstellt dass jetzt kein Interrupt mehr kommt
* VTOR umbiegt
* Den Stackpointer mit dem Inhalt von 0x8005000 lädt
* Den Programcounter mit dem Inhalt von 0x8005004 lädt

Beispiel aus meinem CAN-Bootloader:

(APP_START ist bei mir ein makro das die Adresse der verschobenen 
Vektortabelle enthält und beispielhaft deinitialisiere ich hier eine im 
Bootloader benutzte Hardware die ansonsten weiter lustig Interrupts 
generiert hätte)
1
/**
2
 * Prepare everything for running the application and jump
3
 * to its entry point.
4
 */
5
void jump_app(void) {
6
    /*
7
     * de-initialize all interrupt using hardware
8
     * because we are going to relocate the vector
9
     * table now and MUST NOT cause any IRQ anymore
10
     */
11
    flexcan_deinit();
12

13
    // relocate the vector table and simulate reset
14
    SCB->VTOR = APP_START;
15
    asm(" ldr r1, [%0,#0]    \n\t"
16
        " ldr r2, [%0,#4]    \n\t"
17
        " mov sp, r1         \n\t"
18
        " mov pc, r2         \n\t"
19
        ::"r"(APP_START)
20
    );
21
}

Du machst also entweder nen Dummy-Bootloader oder nen echten Bootloader 
der nach einem Reset oder nach dem Einschalten zügig das obige macht und 
flasht den nach 0x8000000

Von dem Moment an kannst Du Deine für 0x8005000 gelinkte Anwendung ganz 
normal in der IDE laden und debuggen so oft Du willst, nur kein chip 
erase machen sonst ist der Bootloader wieder weg.

Deaktiviere falls vorhanden auch jegliche flash security in Deinem 
Bootloader (setze im Bootloader nicht die Lockbits wie sonst üblich bei 
kommerziellen Projekten) sonst schlägt der Bootloader beim ersten Start 
die Tür hinter sich zu und schließt zweimal ab und es wird nichts mit 
debuggen.
Gast #5727687
Lesenswert?

Marvin M schrieb:
> Man braucht kein komplettes Programm, man muss lediglich dafür sorgen,

Bist du sicher?

Ich habe das gerade mit einem BluePill Board probiert und es klappt 
nicht.

Mein vorgehen:
1
1. in CubeMX normales Projekt angelegt, PC13 ist LD1
2
2. SW4STM32 Projekt erzeugt
3
3. in der main.c LD1 Blinky eingebaut
4
3.1. in der system_stm32f1xx.c -> #define VECT_TAB_OFFSET  0x00005000U
5
3.2. in der STM32F103C8Tx_FLASH.ld -> 
6
     FLASH (rx) : ORIGIN = 0x8005000, LENGTH = 64K - 20k
7
4. Projekt bauen
8
5. mit STM32CubeProg GUI kontrolliert, Programm liegt an 0x08005000
9
6. bei disconnet startet STM32CubeProg das Programm, es blinkt
10
7. noch einmal CubeMX aufgerufen, verändert, Projekt erzeugt, 
11
die system_stm32f1xx.c und die STM32F103C8Tx_FLASH.ld behalten die Änderungen und werden nicht überschrieben!
12
8. mit dem CubeProgrammer CLI ab Adresse 0x08005000 gestartet, blinkt
13
Aufruf: ./STM32_Programmer.sh -c port=SWD -g 0x08005000
14

15
Ein schreiben von 20005000 und 08005000 an Adresse 0x08000000 funktioniert allerdings nicht. Das Programm startet nicht nach Reset oder PON.
16

17
Startet mit Punkt 8 aber sofort wieder.
Gast #5728035
Lesenswert?

Darth Moan schrieb:
> Er verwendet aber ein start script im TrueStudio, das VTOR, SP und PC
> explizit setzt. siehe oben.
>
> Die SystemInit will doch auch die 'ganze' Adresse ins VTOR schreiben:
> SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in
> Internal FLASH */
>
> Er setzt da aber nur 0x5000 rein.

Ja, aber im VTOR steht dann 0x8005000. Es geht ja hier nur um vereiste 
ISR. Die App würde sonst nicht laufen.
Gast #5728538
Lesenswert?

Man kann in der Hauptschleife mit dem Debugger stoppen. Dann sehe 
ich,dass Systick und Timerwerte erhöht wurden. Die funktionieren, denke 
ich.

Was ich wirklich nicht sicher klären kann ist, warum der aktuelle IR 0x3 
sein soll und was das bedeutet. Man kann das z.B. aus xPSR lesen...Man 
muss dort noch 16 abziehen, also 3-16=-13 :-\

Scheint vllt so zu sein, dass dieser IR alles blockiert...
Gast #5728564
Lesenswert?

BrilleAuf schrieb:
> Liegst aber schon ganz richtig. Deshalb eine
> Grüne-Wiese-Testapplikation.

Es soll also einen Bootloader geben. Gibt es den?

In "TestISR.VTOR_5k.hex" ist nichts für den Startbereich (ersten 8 Bytes 
von 0x0800000) definiert. Solange Du diesen nicht definierst (Bootloader 
oder Linkerskript), wird das Laufverhalten definitiv undefiniert sein! 
Es ist daher fraglich was du da debuggst.
Gast #5728646
Lesenswert?

Moin,

BrilleAuf schrieb:
> Was ich wirklich nicht sicher klären kann ist, warum der aktuelle IR 0x3
> sein soll und was das bedeutet. Man kann das z.B. aus xPSR lesen...Man
> muss dort noch 16 abziehen, also 3-16=-13 :-\

Na wenn ich mich nicht verzähle müsste das ja dann der Hard Fault sein.
Wenn also aus irgendeinem Grund der Debugger an Adresse 0x5000 nicht das
selbe liest, wie an 0x08005000 dann könnte er ziemlich bald in den Hard
Fault wollen.
Wenn der Vector, den er zu lesen glaubt aber illegal ist, was dann?
Hattest du mal versucht die set Anweisungen mit 0x08005000 und 
0x08005004
auszuführen?
Kannst du dann den Startup code single steppen?

So ein Verhalten hatte ich nur mal als der Hard Fault versehentlich 
mitten
ins Programm gezeigt hatte. Der wurde angesprungen, und alles unterhalb
war damit blockiert. Ich lief dann irgendwann auf meinen BP, aber eben
nur zufällig.
Gast #5728648
Lesenswert?

Darth Moan schrieb:
> Na wenn ich mich nicht verzähle müsste das ja dann der Hard Fault sein.
Ohne Bootloader wäre das eigentlich nicht unerwartet.

BrilleAuf schrieb:
> Kann es sein, dass der MSP hier noch her muss? :-|

Zur Zeit wird zwar der neue Code geflasht, der aktive ist aber 
irgendwas. Dein Linkerskript hat zur Zeit keine oder keine passenden 
Einträge, da im  Hex-File nichts für 0x08000000 definiert ist.

Das Anpassen des Linkerskripts (etwas Code ist auch nötig) wäre zwar 
weniger Arbeit, ist aber deutlich schwerer (viele Fehlerquellen -> 
dauert länger). Da du sowieso einen Bootloader verwenden willst, würde 
ich an deiner Stelle eher das tun. Du bräuchtes dann ein zweites 
Projekt. Beispiel für main.c vom Bootloader:
1
int main(void)
2
{
3
  const void* ImgPtr = (void*)0x08005000;
4

5
  // Set VectorTable
6
  SCB->VTOR = (uint32_t)ImgPtr;
7

8
  // Set Stack Pointer
9
  __set_MSP((*(uint32_t*)ImgPtr));
10

11
  // Set Program Pointer
12
  void (*pMainApp)(void) = *(void (**)(void))(ImgPtr+4);
13
  pMainApp(); // ist eigentlich ein Call statt Jump, aber egal
14
  return 0;
15
}


Da evtl. nach dem Bootloader noch irgendwo falsche Werte für VTOR 
gesetzt werden, füge bitte zu Testzwecken in deiner main.c vom 
Hauptprogramm eine erneute Zuweisung der Vektortabelle durch:
1
SCB->VTOR = 0x08005000;
Beitrag #5728653 wurde von einem Moderator gelöscht.
Gast #5728669
Lesenswert?

Jump schrieb:
> Warum "komisch"???
>
> Da wird irgendwas ausgeführt, da der Bootloader fehlt. Erstelle erst
> einen Bootloader!

Er setzt im Debugger explizit den PC auf seinen Applikations-Einstieg 
und
lässt dann den Core loslaufen. Wenn der Debugger richtig funktioniert,
wovon ich ausgehe, sollte er nicht erst den Core durch den Reset 
schicken.
Aber wer weiss.
Daher die Frage nach dem Steppen.
Gast #5728897
Lesenswert?

Jump schrieb:
>> Er setzt im Debugger explizit den PC auf seinen Applikations-Einstieg
>> und
> Wirklich? Hat er das geschrieben?

BrilleAuf schrieb:
> Target Software Startup Script anpassen (Truestudio):
> ...
> # Get app entry point
> set $pc = *(unsigned int*)0x5004

Also ich interpretiere das TrueStudio Start Script genau so.
Aber ich habe TrueStudio nicht, also erstmal nur Vermutung.
Vielleicht sollte ich TrueStudio mal ausprobieren.
Gast #5728952
Lesenswert?

Ok, er hat es wirklich geschrieben. Das "Startup Script" betrifft also 
den Debugger und nicht wie ich glaubte den Compiler/Linker. Es sollte 
dann eigentlich ohne Bootloader laufen.

Im "Startup Script" setzt er VTOR, Stackpointer und Entry point. Das 
sieht alles richtig aus. Aber - möglicherweise wird VTOR beim Startup 
seines Programms (EntryPoint bis main.c) unbewusst verändert (bei 
"EmBlocks" würde das nämlich in  SystemInit() passieren). Man könnte 
einen Breakpoint in der main.c setzen und prüfen, ob VTOR (*0xe000ed08) 
tatsächlich noch 0x08005000 ist.
Gast #5729323
Lesenswert?

Bernd K. schrieb:
> EmBlocks ist eine IDE, was hat die damit zu tun was der OP in seiner
> SystemInit macht?

Bei EmBlocks wird beim Anlegen eines Standardprojekts Code 
zusammengestellt. Die SystemInit() steht in der Datei 
"system_stm32f10x.c". Als Autor wird "MCD Application Team" genannt. Der 
Code kann bei allen IDE's laufen, muss aber nicht (z.B. geht auch reines 
ASM).

Bernd K. schrieb:
> Ich verstehe nicht was der Zirkus soll. Flash doch einfach schonmal den
> Bootloader drauf, danach...

Nein. Wenn über Debugger (richtig) gestartet, dann braucht man den 
Bootloader nicht. Dort liegt nicht das Problem. (Ich selber würde es 
aber auch über den Bootloader machen.)
#5733152
Lesenswert?

Jump schrieb:
> Nein. Wenn über Debugger (richtig) gestartet, dann braucht man den
> Bootloader nicht.

Der Debugger müsste dann aber auch VTOR umstellen bevor er es startet. 
Oder das Programm muss irgendwo beim Start selbst das VTOR für sich 
umbiegen was aber ebenfalls unelegant ist.

Und warum ist es mit Bootloader eleganter? Ganz einfach: Wenn man das 
alles den Bootloader machen lässt und selber die VTOR unangetastet lässt 
dann ist der einzige Unterschied zwischen Standalone-Anwendung und 
verschobener Anwendung die Angabe eines anderen Linkerscripts im 
Makefile. Man muß weder in der IDE noch in den Debuggereinstellungen 
irgendwelche Einstellungen ändern um es mal in der einen und mal in der 
anderen Form zu debuggen, das halte ich für eleganter und in der Praxis 
auch einfacher zu handhaben wenn weder irgendwelcher Code in der 
Anwendung noch die Debuggereinstellungen irgendwas davon wissen müssen 
mit welchem Linkerscript zufällig gerade gelinkt wurde und an welcher 
Stelle im Flash die Anwendung zu liegen kommt.

Einzige Voraussetzung: Wenn die bootloaderfähgige Konfiguration debuggt 
werden soll muss auch ein Bootloader drauf sein, was auch einen 
ziemlichen Sinn ergibt denn man will ja auch mal das Gesamtgebilde 
debuggen wie es später mal laufen soll.
Gast #5733342
Lesenswert?

Joe J. schrieb:
> ...Alignement...

Gefordert sind 0x200 Alignment und "BrilleAuf" verwendet 0x5000. -> 
passt

Bernd K. schrieb:
> Der Debugger müsste dann aber auch VTOR umstellen bevor er es startet.
> Oder das Programm muss irgendwo beim Start selbst das ...

Unterstreiche ich alles.

Ich habe da noch einen Speziallfall...
Ich verwende den STMF030 sehr häufig. Da der Flash nur 1000 Zyklen 
verträgt, verschiebe ich das Programm beim Debuggen in den oberen 
Flash-Bereich. Ein separater Bootloader ist für diesen speziellen Fall 
ziemlich umständlich. Deshalb pfusche ich den Bootloader direkt mit in 
das Projekt hinein. (Das Ganze macht natürlich nur dann Sinn, wenn der 
Flasher nicht grundsätzlich den gesamten Flash neu beschreibt, sondern 
unveränderte Flash-Seiten erkennt und überspringt.)


Ergänzung bzw. Änderung im Linkerskript:
1
MEMORY
2
{
3
    ROMB (rx) : ORIGIN = 0x08000000, LENGTH = 64   /* neue Zeile */
4
    ROM  (rx) : ORIGIN = 0x0801ac00, LENGTH = 4K   /* bestehende Zeile geändert */
5
    ...
6
}


Ergänzung im Linkerskript:
1
SECTIONS
2
{
3
    .myboot :
4
    {
5
        KEEP(*(.myboot.vtable));
6
        KEEP(*(.myboot.loader));
7
    } > ROMB
8
    ...
9
}



Ergänzung im Quellcode:
1
// Bootloader-Source
2
void __attribute__((naked, optimize("-Os"), section(".myboot.loader"))) MyBoot_Loader()
3
{
4
  extern uint32_t __isr_vector;
5
  SCB->VTOR = (uint32_t)&__isr_vector;
6
  goto *(void*)((&__isr_vector)[1]);
7
}
8
// Bootloader-vtable
9
extern uint32_t __StackTop;
10
__attribute__((section(".myboot.vtable"))) void* MyBoot_VectorTable[2] =
11
{
12
  &__StackTop,
13
  MyBoot_Loader
14
};

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren