CH32V003 gcc und Codegenerierung

#7645094
Lesenswert?

Moin Leute,

Ich hab da gerade ein kleines Problem mit meiner Testumgebung fuer einen CH32V003.

Mein System ist im Prinzip ein Eigenbau der zu 90% hierauf basiert:

https://github.com/cnlohr/ch32v003fun

Ich hab z.b ein kleines Testprogramm das eine LED an einem Pin blinken laesst und es funktioniert.

Ich linke nun ein weiteres Testprogramm hinzu OHNE das ich daraus irgendwas benutzte! Sofort haengt sich mein Testprogramm weg.

Grund dafuer ist folgendes:

1
void SysTick_Handler(void) __attribute__((interrupt));
2
void SysTick_Handler(void)
3
{
4
  msTicks++;
5

6
  SysTick->SR = 0;
7
}

Alleine die deklaration dieses Systick_Handler sorgt dafuer das mein Programm nicht laeuft. Der Grund wird auch klar wenn man ins mapfile schaut:

Ohne Systick:

1
.text.startup.main
2
                0x00000000000000ce      0x100 /tmp/ccRxtkAe.ltrans0.ltrans.o
3
                0x00000000000000ce                main
4
 .text.vector_handler
5
                0x00000000000001ce        0x2 /tmp/ccRxtkAe.ltrans0.ltrans.o
6
                0x00000000000001ce                TIM1_CC_IRQHandler
7
                0x00000000000001ce                HardFault_Handler
8
                0x00000000000001ce                SysTick_Handler
9
                0x00000000000001ce                PVD_IRQHandler
10
                0x00000000000001ce                NMI_Handler
11
                0x00000000000001ce                SPI1_IRQHandler

Und so mit Systick:

1
.text.startup.main
2
                0x00000000000000ce       0xe8 /tmp/ccHcEMIq.ltrans0.ltrans.o
3
                0x00000000000000ce                main
4
 .text.SysTick_Handler
5
                0x00000000000001b6       0x26 /tmp/ccHcEMIq.ltrans0.ltrans.o
6
 .text.vector_handler
7
                0x00000000000001dc        0x2 /tmp/ccHcEMIq.ltrans0.ltrans.o
8
                0x00000000000001dc                TIM1_CC_IRQHandler
9
                0x00000000000001dc                HardFault_Handler
10
                0x00000000000001dc                PVD_IRQHandler
11
                0x00000000000001dc                NMI_Handler
12
                0x00000000000001dc                SPI1_IRQHandler

Der Linker fummelt mir also wohl die Interrupttabelle kaputt.

So sieht die uebersetztung aus:

make riscv-none-embed-gcc --specs=nosys.specs -g -Os -flto -ffunction-sections -static-libgcc -march=rv32ec -mabi=ilp32e -nostdlib -I./riscv_core/extralibs -I./riscv_core -I./riscv_core/inc -I./riscv_core/attic -T ./riscv_core/inc/ch32v003fun.ld -Wl,-Map=mapfile.txt -lc -lm -Wl,--gc-sections -L./riscv_core/misc -lgcc riscv_core/attic/startup_ch32v003.c timer.c main.c -o template.elf riscv-none-embed-objcopy -O ihex template.elf template.hex riscv-none-embed-objcopy -O binary template.elf template.bin

Ich vermute mal das ich da noch irgendeine Option vergessen habe? Oder woran kann das sonst liegen?

Der Handler selbst wird im StartUp-Code weak definiert:

1
void SysTick_Handler( void )             __attribute__((section(".text.vector_handler"))) __attribute((weak,alias("DefaultIRQHandler"))) __attribute__((used));

Vanye

#7645331
Lesenswert?

Zeige bitte den vollständigen Quelltext.

Koennte ich tun, aber zu lang. Ausserdem habt ihr meine Frage nicht verstanden.

Darum noch mal ein Versuch.

1
SRCS = ./riscv_core/attic/startup_ch32v003.c
2
#SRCS +=timer.c 
3
SRCS +=main.c

Das obige ist ein Ausschnitt aus dem Makefile welches zu einem funktionierendem Programm fuehrt, nehme ich timer.c mit ins Programm so funktioniert das Programm nicht. Ich entferne also nur "#" und mache ein make clean;make

Mit anderen Worten es wird KEINE einzige Programmzeile aus dieser Datei genutzt. Sie ist dann einfach nur vorhanden, nichts daraus wird aufgerufen.

Lediglich die Codegenerierung vom Linker aendert sich. Also die Position oder Eintraege von Interrupttabellen. Aber es wird NICHTS davon genutzt! Kein Eintrag wird aufgerufen, nichts ist aktiviert. Und mein eigener Code laeuft auch garnicht erst! Selbst ein Initalisieren eines GPIO und high setzen in der ersten Zeile funktioniert dann nicht mehr. main wird also nicht mehr erreicht.

Vanye

#7645369
Lesenswert?

Vanye R. schrieb:

Ausserdem habt ihr meine Frage nicht verstanden.

Du hast Dich nun auch sehr merkwürdig ausgedrückt:

Vanye R. schrieb:

Ich linke nun ein weiteres Testprogramm hinzu OHNE das ich daraus irgendwas benutzte!

Vanye R. schrieb:

Aber es wird NICHTS davon genutzt!

Du scheinst einen Interrupthandler angelegt zu haben. Was soll den der Linker damit Deiner Ansicht nach machen? Ob Du die nötige Peripherie initialisierst, die den Interrupt schlussendlich auslöst, das kann der Linker nicht wissen. Also muss er den Interrupthandler wie einen Interrupthandler behandeln.

Und dabei kommt er wohl aus den von Oliver beschriebenen Gründen durcheinander.

Worin bestehen denn die 10% Unterschied zum Code von cnlohr? Vielleicht liegen die in genau diesem Linkerskript und dem Drumherum?

#7645524
Lesenswert?

Vanye R. schrieb:

Der Linker fummelt mir also wohl die Interrupttabelle kaputt.

Die RISC-V Toolchain ist mir fremd, aber ich nehme an, was da passiert ist analog zu ARM.

Der Startup-Code enthält weak Stubs für die Interrupt-Service Routinen. Sobald Du gleichnamige "echte" Routinen mit gleichem Namen dazulinkst, werden die aktiv.

Das ist nicht "Kaputtfummeln", sondern ein Dienst am Programmierer.

#7645536
Lesenswert?

Markus F. schrieb:

Sobald Du gleichnamige "echte" Routinen mit gleichem Namen dazulinkst, werden die aktiv.

Dann müsste allerdings die Reihenfolge der Einträge für vector_handler und systick_handler andersherum sein, sofern man davon ausgehen kann, daß die beiden zu Beginn gezeigten Tabellen in ihrer Reihenfolge aufsteigend nach Vektor sortiert sind. "vector_handler" sollte daher in beiden Fällen an der gleichen Stelle stehen, und das tuts nicht.

Da kann ich vanyes Irritation nachvollziehen.

#7645539
Lesenswert?

Der Startup-Code enthält weak Stubs für die Interrupt-Service Routinen. Sobald Du gleichnamige "echte" Routinen mit gleichem Namen dazulinkst, werden die aktiv.

DAs weiss ich. Ich habs ja im Ausgangspost selber zitiert. Hier die Datei aus der ich zitiert habe und die 1:1 bei mir verwendet wird:

https://github.com/cnlohr/ch32v003fun/blob/master/attic/ch32v003evt/startup_ch32v003.c

Und im uebrigen sollte da nichts aktiv werden. Der Linker sollte die Adresse meines Codes dann anstelle des weak-defaults einsetzen. Das passiert nicht korrekt, aber selbst das sollte noch egal sein weil man den Interrupt dann ja auch noch aktivieren muss. Das mache ich aber nicht wie ich schon zweimal betont habe. Aber kann sein das ich da was verwechselt habe (Tabelle und Position der Firmware). Ich muss mir das am Wochenende nochmal genauer anschauen.

Vanye

#7645549
Lesenswert?

Vanye R. schrieb:

Das passiert nicht korrekt, aber selbst das sollte noch egal sein weil man den Interrupt dann ja auch noch aktivieren muss. Das mache ich aber nicht wie ich schon zweimal betont habe.

Der linker analysiert keine Programme, um herauszufinden, ob du da irgendwo einen Interrupt aktivierst oder nicht. Selbst der Compiler macht und kann das nicht.

Oliver

#7645612
Lesenswert?

Selbst der Compiler macht und kann das nicht.

Eben, deshalb ja mein hinweis das mein Programm uebersetzt wird wenn timer.c includiert wird aber auch wenn nicht! Da sind also schon Funktionen drin, genauer gesagt die Initialisierung fuer den Timer und die Interruptfunktion. Aber die werden eben nicht aufgerufen/genutzt! Sonst wuerde der Linker ja sofort mit einem Fehler aussteigen sobald ich die Funktion entferne.

im Startup Code raus und lege einfach ein array an die Stelle

Ja, ich fand das auch etwas komisch und kenne das sonst etwas anders, allerdings ist der Source ja nicht von mir und scheint in anderen Projekten zu funktionieren.

Vanye

#7645617
Lesenswert?

Vanye R. schrieb:

Da sind also schon Funktionen drin, genauer gesagt die Initialisierung fuer den Timer und die Interruptfunktion. Aber die werden eben nicht aufgerufen/genutzt!

Ja, aber das ist völlig irrelevant. Du hast einen Interrupthandler definiert, der wird in die Interrupttabelle eingetragen, egal, ob Du irgendeine Initialisierung aufrufst oder nicht.

#7645652
Lesenswert?

Ja, aber das ist völlig irrelevant. Du hast einen Interrupthandler definiert, der wird in die Interrupttabelle eingetragen, egal, ob Du irgendeine Initialisierung aufrufst oder nicht.

Dessen bin ich mir bewusst, aber das ist ja irrelevant solange der Interrupt nicht eingeschaltet wird, was nicht der Fall ist.

Wenn ich keine Interruptfunktion im Programm habe wird ja folgendes in der Tabelle eingetragen:

void DefaultIRQHandler( void ) { // Infinite Loop
asm volatile( "1: j 1b" ); }

Damit funktioniert mein Programm weil auch das natuerlich niemals aufgerufen wird. Es ist ja schliesslich nicht aktiviert.

Vanye

#7645687
Lesenswert?

Irgendwie wird das gerade verwirrender....

Hier seht ihr ja den Startcode:

https://github.com/cnlohr/ch32v003fun/blob/master/attic/ch32v003evt/startup_ch32v003.c

Default ist also

1
void DefaultIRQHandler( void )
2
{
3
  // Infinite Loop                                                                         
4
  asm volatile( "1: j 1b" );
5
}

des weiteren wird das der Systick_Handler so vordefiniert:

1
void SysTick_Handler( void )
2
__attribute__((section(".text.vector_handler")))
3
__attribute((weak,alias("DefaultIRQHandler"))) __attribute__((used));

Daraus schliesse ich das der SystTick_Handler erstmal nicht aufgerufen wird, denn dann wuerde es ja sofort in obiger Endlosschleife enden. Zustimmung?

In meiner main schalte ich dann eine LED ein und aus und hab dazwischen eine brutale Wartefunktion die einfach Zyklen verschwendet. Ich sehe dann die LED wie erwartet blinken. Alles laeuft!

Jetzt binde ich meine timer.c im Makefile ein und sorge dafuer das in timer.c keine Funktion befindet die SysTick_Handler heisst. Alles laeuft, led blinkt.

Jetzt schreibe ich da folgendes rein:

1
void SysTick_Handler(void)
2
{
3
}

Alles laeuft. LED blinkt. Warum auch nicht. Sollte ja nicht aufgerufen werden oder? Allerdings wenn doch, hat das ja keine Auswirkungen.

Okay, jetzt folgendes

1
static volatile uint32_t msTicks;
2

3
void SysTick_Handler(void)
4
{
5
  //   msTicks++;                                                                              
6

7
  volatile int i;
8
  asm volatile( "nop");
9
  i++;
10

11
  //    SysTick->SR = 0;                                                                       
12
}

Meine LED blinkt, alles geht!

Dann kommentiere ich die msTicks Zeile aus und das Programm haengt sich weg!

Es gibt also zwei Merkwuerdigkeiten:

  1. Wieso wird Systick_Handler ueberhaubt aufgerufen?
  2. Wenn schon, wieso ist gerade der zugriff auf msTicks so giftig?

Ich seh schon kommen das ich RiscV Assembler lernen muss, seufz!

Vanye

#7645697
Lesenswert?

Vanye R. schrieb:

void SysTick_Handler(void) attribute((interrupt));

wozu ist das Attribut interrupt? Stimmen die damit nicht überein und wird dann das weak DefaultHandler nicht überschrieben?

Beim Default Handler wird das auch nicht gemacht.

Edit: Mr. Lohr schreibt das interrupt crucial wäre, aber beim Default Handler ist die ISR naked. Doku zu Attribut interrupt sagt das da noch ein Argument hingehört. Bzw. bei RISC-V ist default machine. https://gcc.gnu.org/onlinedocs/gcc/RISC-V-Function-Attributes.html

ok, naked ist beim DefaultHandler valid weil das nur eine Endlos asm Schleife ist.

Persönliche Seite #7645913
Lesenswert?

Vanye R. schrieb:

Ja, ich fand das auch etwas komisch und kenne das sonst etwas anders, allerdings ist der Source ja nicht von mir und scheint in anderen Projekten zu funktionieren.

Mein Fehler, hab erst jetzt gesehen dass besagtes Vektorarray im Startup Code angelegt ist.

Vielleicht gibt es einen Hardfault? Du kannst ja mal probieren deine LED im

1
void HardFault_Handler(void)

einzuschalten

#7934270
Lesenswert?

Fehler gefunden!

Ich habe den Startupcode den ich verwende im Internet "gefunden" und der enthaelt eine fiesen Bug.

1
  // Careful: Use registers to prevent overwriting of self-data.                                           
2
  // This clears out BSS.                                                                                  
3
  register uint32_t * tempout = _sbss;
4
  register uint32_t * tempend = _ebss;
5
  while( tempout < tempend )
6
    *(tempout++) = 0;
7

8

9
  // Load data section from flash to RAM                                                                   
10
  register uint32_t * tempin = _data_lma;
11
  tempout = _data_vma;
12
  tempend = _edata;
13
  while( tempout != tempend )
14
    *(tempout++) = *(tempin++);

Der Source SOLL die Variablen auf 0 setzen und globale Variablen initialisieren.

Das macht er aber nicht. Er nutzt naemlich nicht die vom Linker uebergebenen Adressen sondern die Inhalte die an den Adressen stehen.

So funktionier es dann...

1
  register uint32_t * tempout = (uint32_t*) &_sbss;
2
  register uint32_t * tempend = (uint32_t*) &_ebss;
3
  while( tempout < tempend ) *(tempout++) = 0;
4

5
  register uint32_t * tempin = (uint32_t*) &_data_lma;                               
6
  tempout = (uint32_t*) &_data_vma;                                
7
  tempend = (uint32_t*) &_edata;  
8
  while( tempout != tempend ) *(tempout++) = *(tempin++);

Das erklaert auch wieso es bei mir manchmal funktioniert hat und manchmal zum hardfault fuehrt. Eigentlich verlaesst sich niemand darauf das Variablen mit 0 initialisiert sind und globale Variablen nutzen man auch eher selten. Aber je nachdem welchen Wert eine globale Variable hatte fuehrt das dann zum HardFault weil irgendwelche verbotenen Adressen beschrieben werden.

Jaja, wenn man nicht alles selber macht. :-D

Vanye

Persönliche Seite #7934331
Lesenswert?

Vanye R. schrieb:

1
>   register uint32_t * tempout = _sbss;

[...] Er nutzt naemlich nicht die vom Linker übergebenen Adressen sondern die Inhalte die an den Adressen stehen.

So ist das mit Symbolen :-) Eine Möglichkeit ist, Deklarationen zu verwenden wie:

1
extern uint32_t _sbss[]; // Symbol defined in ld script.

Und "register" ist deprecated, das hat keine Funktion mehr. Außer den Leser zu verwirren (oder mit asm).

: Bearbeitet durch User

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