* Wrong assembly code generated from C code (ARM Cortex-m3) ? @ 2021-12-02 12:38 Sébastien Gendre 2021-12-02 15:44 ` David Brown 2021-12-08 5:51 ` Sébastien Gendre 0 siblings, 2 replies; 7+ messages in thread From: Sébastien Gendre @ 2021-12-02 12:38 UTC (permalink / raw) To: gcc-help Hello, I got a problem with an assembly code generated by GCC from a C code. A little bit of context: I try to write a minimal firmware for a micro-controller, the LPC1769. It use ARM Cortex-M3 CPU. My firmware is simple: * Run a function named "reset()" at CPU reset * In this function, I declare 3 locals variables: 3 pointers to 3 differents registers, initialized with their address * Then, I write a value in each register The problem: The CPU reset after the first value I write in a register This is the code of the function "reset()": ``` c void reset() { // Define some registers // Register to define mode of the pins P0.16 to P0.26 unsigned int *PINMODE1 = (unsigned int *)0x4002C044; // Register to define GPIO direction of pins P0.0 to P0.31 unsigned int *FIO0DIR = (unsigned int *)0x2009C000; // Register to define GPIO value of pins P0.0 to P0.31 unsigned int *FIO0SET = (unsigned int *)0x2009C018; // Config the GPIO to drive a LED // Enable P0.22 pin to put the line to ground *PINMODE1 = 0x00003000; // Set P0.22 as GPIO output *FIO0DIR = 0x400000; // To the eternity while (1) { // Wait for (int i = 0; i < 500000; ++i); // Toggle the LED via P0.22 *FIO0SET ^= (0b1<<22); } } ``` The problem is at the line `*PINMODE1 = 0x00003000;`. This is the assembly code generated by GCC and view from GDB: ``` assembly => 0x0000001a <+18>: ldr r3, [r7, #8] 0x0000001c <+20>: strb r6, [r0, #28] 0x0000001e <+22>: ; <UNDEFINED> instruction: 0xf590601a 0x00000022 <+26>: ldr r3, [r7, #4] ``` The first assembly code, ldr, load the address `0x4002C044` into the register `r3`. But the second asm code, `strb`, is it store the value from the register `r6` to the address made frome the value of register `r0` plus a shift of 28 ? Why not simply copy the value `0x00003000` to the adress stored in `r3` register ? Why this undefined instruction `0xf590601a` ? This third asm code, the "undefied" `0xf590601a` is the instruction that make the CPU reset. I'm not an expert in assemply and I don't know if it's me that don't understand it of if the assembly code generated by GCC is wrong. Any help is welcome. ;) Thank you. :) This is the version of arm-none-eabi-gcc I use: 11.1.0 (Fedora 11.1.0-2.fc35) Version of GDB: 11.1 (Fedora 11.1-2.fc35) Version of OpenOCD: 0.11.0 (from Fedora repo) This is the full C code: ``` c /* reset Function run right after the reset of the CPU */ void reset() { // Define some registers // Register to define mode of the pins P0.16 to P0.26 unsigned int *PINMODE1 = (unsigned int *)0x4002C044; // Register to define GPIO direction of pins P0.0 to P0.31 unsigned int *FIO0DIR = (unsigned int *)0x2009C000; // Register to define GPIO value of pins P0.0 to P0.31 unsigned int *FIO0SET = (unsigned int *)0x2009C018; // Config the GPIO to drive a LED // Enable P0.22 pin to put the line to ground /* *PINMODE1 |= (0x1<<12); */ /* *PINMODE1 |= (0x1<<13); */ *PINMODE1 = 0x00003000; // Set P0.22 as GPIO output /* *FIO0DIR |= (0x1<<22); */ *FIO0DIR = 0x400000; // To the eternity while (1) { // Wait for (int i = 0; i < 500000; ++i); // Toggle the LED via P0.22 *FIO0SET ^= (0b1<<22); } } int STACK[256]; const void *vectors[] __attribute__ ((section (".vectors"))) = { STACK + sizeof(STACK) / sizeof(*STACK), reset }; ``` This is my link script: ``` MEMORY { flash (RX) : ORIGIN = 0x00000000, LENGTH = 512K sram (RW!X) : ORIGIN = 0x10000000, LENGTH = 32K } SECTIONS { .vectors : { *(.vectors) } >flash .text : { *(.text) } >flash .rodata : { *(.rodata) } >flash .bss : { *(.bss) } >sram } ``` This is my Makefile: ``` CFLAGS = -g -O0 -Wall CFLAGS += -mthumb -mcpu=cortex-m3 CC = arm-none-eabi-gcc LD = arm-none-eabi-ld OBJCPY = arm-none-eabi-objcopy all: blink.bin blink.bin: blink.elf @echo "Make the binary" $(OBJCPY) -O binary blink.elf blink.bin blink.elf: blink.o @echo "Linkage" $(LD) -T blink.ld -o blink.elf blink.o blink.o: blink.c @echo "Compile the code" $(CC) -c $(CFLAGS) -o blink.o blink.c clean: @echo "Clean the working dir" rm blink.o blink.elf blink.bin debug: blink.elf gdb -x gdbconfig blink.elf debug-mi: blink.elf gdb -i=mi -x gdbconfig blink.elf ``` ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Wrong assembly code generated from C code (ARM Cortex-m3) ? 2021-12-02 12:38 Wrong assembly code generated from C code (ARM Cortex-m3) ? Sébastien Gendre @ 2021-12-02 15:44 ` David Brown 2021-12-02 16:06 ` Sébastien Gendre 2021-12-08 5:51 ` Sébastien Gendre 1 sibling, 1 reply; 7+ messages in thread From: David Brown @ 2021-12-02 15:44 UTC (permalink / raw) To: Sébastien Gendre, gcc-help On 02/12/2021 13:38, Sébastien Gendre wrote: > Hello, > > I got a problem with an assembly code generated by GCC from a C code. > > > A little bit of context: > > I try to write a minimal firmware for a micro-controller, the LPC1769. > It use ARM Cortex-M3 CPU. My firmware is simple: > * Run a function named "reset()" at CPU reset > * In this function, I declare 3 locals variables: 3 pointers to 3 > differents registers, initialized with their address > * Then, I write a value in each register > This is absolutely /not/ the way to write code for a modern microcontroller. I would recommend that you put this all to the side at the moment. Download the mcuxpresso tools from NXP's website, and start with a wizard-generated example project. (For those that don't know, NXP is the manufacturer of the LPC microcontrollers, and mcuxpresso is their bundle of Eclipse, gdb, gcc, libraries, etc. It is available for Linux and Windows according to preference.) There are so many things that could be wrong with the approach you have taken that it is hard to guess. (The idea of writing a makefile-based project rather than one that relies on an IDE is fine in the long run, but start with an IDE-generated project. It gets the details right regarding linker setups, libraries, startup code, etc.) Don't define the registers yourself - use the headers from the NXP tools. Microcontroller programming is not the same as C programming on "big" systems. For a start, you need to understand and embrace "volatile". And I recommend that you make heavy use of the <stdint.h> types. The usenet group comp.arch.embedded can be a useful resource, though it doesn't have much chatter there at the moment. Start a thread there and the lurkers will crawl out of the woodwork to help. Your issues are, I think, more general microcontroller programming than gcc-specific. (As for the reason the code does not seem to make sense - I doubt you are actually successfully loading the code you expect at the address you expect on the device.) David > > The problem: > > The CPU reset after the first value I write in a register > > This is the code of the function "reset()": > ``` c > void reset() { > // Define some registers > // Register to define mode of the pins P0.16 to P0.26 > unsigned int *PINMODE1 = (unsigned int *)0x4002C044; > // Register to define GPIO direction of pins P0.0 to P0.31 > unsigned int *FIO0DIR = (unsigned int *)0x2009C000; > // Register to define GPIO value of pins P0.0 to P0.31 > unsigned int *FIO0SET = (unsigned int *)0x2009C018; > > // Config the GPIO to drive a LED > // Enable P0.22 pin to put the line to ground > *PINMODE1 = 0x00003000; > // Set P0.22 as GPIO output > *FIO0DIR = 0x400000; > > // To the eternity > while (1) { > // Wait > for (int i = 0; i < 500000; ++i); > > // Toggle the LED via P0.22 > *FIO0SET ^= (0b1<<22); > } > > } > ``` > > The problem is at the line `*PINMODE1 = 0x00003000;`. This is the > assembly code generated by GCC and view from GDB: > > ``` assembly > => 0x0000001a <+18>: ldr r3, [r7, #8] > 0x0000001c <+20>: strb r6, [r0, #28] > 0x0000001e <+22>: ; <UNDEFINED> instruction: 0xf590601a > 0x00000022 <+26>: ldr r3, [r7, #4] > ``` > > The first assembly code, ldr, load the address `0x4002C044` into the > register `r3`. But the second asm code, `strb`, is it store the value > from the register `r6` to the address made frome the value of register > `r0` plus a shift of 28 ? Why not simply copy the value `0x00003000` > to the adress stored in `r3` register ? Why this undefined instruction > `0xf590601a` ? This third asm code, the "undefied" `0xf590601a` is the > instruction that make the CPU reset. > > I'm not an expert in assemply and I don't know if it's me that don't > understand it of if the assembly code generated by GCC is wrong. Any > help is welcome. ;) > > Thank you. :) > > This is the version of arm-none-eabi-gcc I use: > 11.1.0 (Fedora 11.1.0-2.fc35) > > Version of GDB: > 11.1 (Fedora 11.1-2.fc35) > > Version of OpenOCD: > 0.11.0 (from Fedora repo) > > This is the full C code: > ``` c > /* reset > > Function run right after the reset of the CPU > */ > void reset() { > // Define some registers > // Register to define mode of the pins P0.16 to P0.26 > unsigned int *PINMODE1 = (unsigned int *)0x4002C044; > // Register to define GPIO direction of pins P0.0 to P0.31 > unsigned int *FIO0DIR = (unsigned int *)0x2009C000; > // Register to define GPIO value of pins P0.0 to P0.31 > unsigned int *FIO0SET = (unsigned int *)0x2009C018; > > // Config the GPIO to drive a LED > // Enable P0.22 pin to put the line to ground > /* *PINMODE1 |= (0x1<<12); */ > /* *PINMODE1 |= (0x1<<13); */ > *PINMODE1 = 0x00003000; > // Set P0.22 as GPIO output > /* *FIO0DIR |= (0x1<<22); */ > *FIO0DIR = 0x400000; > > // To the eternity > while (1) { > // Wait > for (int i = 0; i < 500000; ++i); > > // Toggle the LED via P0.22 > *FIO0SET ^= (0b1<<22); > } > > } > > int STACK[256]; > > const void *vectors[] __attribute__ ((section (".vectors"))) = { > STACK + sizeof(STACK) / sizeof(*STACK), > reset > }; > ``` > > > This is my link script: > ``` > MEMORY { > flash (RX) : ORIGIN = 0x00000000, LENGTH = 512K > sram (RW!X) : ORIGIN = 0x10000000, LENGTH = 32K > } > SECTIONS { > .vectors : { *(.vectors) } >flash > .text : { *(.text) } >flash > .rodata : { *(.rodata) } >flash > .bss : { *(.bss) } >sram > } > ``` > > This is my Makefile: > ``` > CFLAGS = -g -O0 -Wall > CFLAGS += -mthumb -mcpu=cortex-m3 > > CC = arm-none-eabi-gcc > LD = arm-none-eabi-ld > OBJCPY = arm-none-eabi-objcopy > > > all: blink.bin > > > blink.bin: blink.elf > @echo "Make the binary" > $(OBJCPY) -O binary blink.elf blink.bin > > blink.elf: blink.o > @echo "Linkage" > $(LD) -T blink.ld -o blink.elf blink.o > > blink.o: blink.c > @echo "Compile the code" > $(CC) -c $(CFLAGS) -o blink.o blink.c > > clean: > @echo "Clean the working dir" > rm blink.o blink.elf blink.bin > > debug: blink.elf > gdb -x gdbconfig blink.elf > > debug-mi: blink.elf > gdb -i=mi -x gdbconfig blink.elf > ``` > ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Wrong assembly code generated from C code (ARM Cortex-m3) ? 2021-12-02 15:44 ` David Brown @ 2021-12-02 16:06 ` Sébastien Gendre 2021-12-02 19:26 ` Stefan Ring 2021-12-02 21:58 ` David Brown 0 siblings, 2 replies; 7+ messages in thread From: Sébastien Gendre @ 2021-12-02 16:06 UTC (permalink / raw) To: David Brown; +Cc: gcc-help Hello David, Thanks for your reply. Writing a firmware from scatch and not using the official IDE is the challenge. It's based on this video of Nikolay Kondrashov who do the same thing for another microcontroller with the same arch: https://media.ccc.de/v/ASG2019-161-microcontroller-firmware-from-scratch I started by reading the arch and microcontroller manuals to see how its start, how the memory are mapped, which clock and peripherals are enabled after a reset, etc. I define local variables because I didn't have the time, for now, to implement the loading of the values for the initialized global variables. I defined volatile ones before, but with the same reset from the CPU at the same moment. I tested read and write in the registers with OpenOCD directly and it work. I'm sure I can flash the microcontroller because if I read the data at addresse 0x0 I can read the vector table I defined in my code (the one with stack pointer and first instruction pointer). And I'm sure I can run code on the microcontroller because if I don't declare the pointers to the registers and I just declare an int variable and set its values multiple times it work (I can run it, inspect variables, place breakpoints, etc). For debug I use GDB. David Brown <david.brown@hesbynett.no> writes: > On 02/12/2021 13:38, Sébastien Gendre wrote: >> Hello, >> >> I got a problem with an assembly code generated by GCC from a C code. >> >> >> A little bit of context: >> >> I try to write a minimal firmware for a micro-controller, the LPC1769. >> It use ARM Cortex-M3 CPU. My firmware is simple: >> * Run a function named "reset()" at CPU reset >> * In this function, I declare 3 locals variables: 3 pointers to 3 >> differents registers, initialized with their address >> * Then, I write a value in each register >> > > This is absolutely /not/ the way to write code for a modern > microcontroller. > > I would recommend that you put this all to the side at the moment. > Download the mcuxpresso tools from NXP's website, and start with a > wizard-generated example project. > > (For those that don't know, NXP is the manufacturer of the LPC > microcontrollers, and mcuxpresso is their bundle of Eclipse, gdb, gcc, > libraries, etc. It is available for Linux and Windows according to > preference.) > > There are so many things that could be wrong with the approach you have > taken that it is hard to guess. (The idea of writing a makefile-based > project rather than one that relies on an IDE is fine in the long run, > but start with an IDE-generated project. It gets the details right > regarding linker setups, libraries, startup code, etc.) > > Don't define the registers yourself - use the headers from the NXP tools. > > Microcontroller programming is not the same as C programming on "big" > systems. For a start, you need to understand and embrace "volatile". > And I recommend that you make heavy use of the <stdint.h> types. > > The usenet group comp.arch.embedded can be a useful resource, though it > doesn't have much chatter there at the moment. Start a thread there and > the lurkers will crawl out of the woodwork to help. Your issues are, I > think, more general microcontroller programming than gcc-specific. > > (As for the reason the code does not seem to make sense - I doubt you > are actually successfully loading the code you expect at the address you > expect on the device.) > > > David > >> >> The problem: >> >> The CPU reset after the first value I write in a register >> >> This is the code of the function "reset()": >> ``` c >> void reset() { >> // Define some registers >> // Register to define mode of the pins P0.16 to P0.26 >> unsigned int *PINMODE1 = (unsigned int *)0x4002C044; >> // Register to define GPIO direction of pins P0.0 to P0.31 >> unsigned int *FIO0DIR = (unsigned int *)0x2009C000; >> // Register to define GPIO value of pins P0.0 to P0.31 >> unsigned int *FIO0SET = (unsigned int *)0x2009C018; >> >> // Config the GPIO to drive a LED >> // Enable P0.22 pin to put the line to ground >> *PINMODE1 = 0x00003000; >> // Set P0.22 as GPIO output >> *FIO0DIR = 0x400000; >> >> // To the eternity >> while (1) { >> // Wait >> for (int i = 0; i < 500000; ++i); >> >> // Toggle the LED via P0.22 >> *FIO0SET ^= (0b1<<22); >> } >> >> } >> ``` >> >> The problem is at the line `*PINMODE1 = 0x00003000;`. This is the >> assembly code generated by GCC and view from GDB: >> >> ``` assembly >> => 0x0000001a <+18>: ldr r3, [r7, #8] >> 0x0000001c <+20>: strb r6, [r0, #28] >> 0x0000001e <+22>: ; <UNDEFINED> instruction: 0xf590601a >> 0x00000022 <+26>: ldr r3, [r7, #4] >> ``` >> >> The first assembly code, ldr, load the address `0x4002C044` into the >> register `r3`. But the second asm code, `strb`, is it store the value >> from the register `r6` to the address made frome the value of register >> `r0` plus a shift of 28 ? Why not simply copy the value `0x00003000` >> to the adress stored in `r3` register ? Why this undefined instruction >> `0xf590601a` ? This third asm code, the "undefied" `0xf590601a` is the >> instruction that make the CPU reset. >> >> I'm not an expert in assemply and I don't know if it's me that don't >> understand it of if the assembly code generated by GCC is wrong. Any >> help is welcome. ;) >> >> Thank you. :) >> >> This is the version of arm-none-eabi-gcc I use: >> 11.1.0 (Fedora 11.1.0-2.fc35) >> >> Version of GDB: >> 11.1 (Fedora 11.1-2.fc35) >> >> Version of OpenOCD: >> 0.11.0 (from Fedora repo) >> >> This is the full C code: >> ``` c >> /* reset >> >> Function run right after the reset of the CPU >> */ >> void reset() { >> // Define some registers >> // Register to define mode of the pins P0.16 to P0.26 >> unsigned int *PINMODE1 = (unsigned int *)0x4002C044; >> // Register to define GPIO direction of pins P0.0 to P0.31 >> unsigned int *FIO0DIR = (unsigned int *)0x2009C000; >> // Register to define GPIO value of pins P0.0 to P0.31 >> unsigned int *FIO0SET = (unsigned int *)0x2009C018; >> >> // Config the GPIO to drive a LED >> // Enable P0.22 pin to put the line to ground >> /* *PINMODE1 |= (0x1<<12); */ >> /* *PINMODE1 |= (0x1<<13); */ >> *PINMODE1 = 0x00003000; >> // Set P0.22 as GPIO output >> /* *FIO0DIR |= (0x1<<22); */ >> *FIO0DIR = 0x400000; >> >> // To the eternity >> while (1) { >> // Wait >> for (int i = 0; i < 500000; ++i); >> >> // Toggle the LED via P0.22 >> *FIO0SET ^= (0b1<<22); >> } >> >> } >> >> int STACK[256]; >> >> const void *vectors[] __attribute__ ((section (".vectors"))) = { >> STACK + sizeof(STACK) / sizeof(*STACK), >> reset >> }; >> ``` >> >> >> This is my link script: >> ``` >> MEMORY { >> flash (RX) : ORIGIN = 0x00000000, LENGTH = 512K >> sram (RW!X) : ORIGIN = 0x10000000, LENGTH = 32K >> } >> SECTIONS { >> .vectors : { *(.vectors) } >flash >> .text : { *(.text) } >flash >> .rodata : { *(.rodata) } >flash >> .bss : { *(.bss) } >sram >> } >> ``` >> >> This is my Makefile: >> ``` >> CFLAGS = -g -O0 -Wall >> CFLAGS += -mthumb -mcpu=cortex-m3 >> >> CC = arm-none-eabi-gcc >> LD = arm-none-eabi-ld >> OBJCPY = arm-none-eabi-objcopy >> >> >> all: blink.bin >> >> >> blink.bin: blink.elf >> @echo "Make the binary" >> $(OBJCPY) -O binary blink.elf blink.bin >> >> blink.elf: blink.o >> @echo "Linkage" >> $(LD) -T blink.ld -o blink.elf blink.o >> >> blink.o: blink.c >> @echo "Compile the code" >> $(CC) -c $(CFLAGS) -o blink.o blink.c >> >> clean: >> @echo "Clean the working dir" >> rm blink.o blink.elf blink.bin >> >> debug: blink.elf >> gdb -x gdbconfig blink.elf >> >> debug-mi: blink.elf >> gdb -i=mi -x gdbconfig blink.elf >> ``` >> ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Wrong assembly code generated from C code (ARM Cortex-m3) ? 2021-12-02 16:06 ` Sébastien Gendre @ 2021-12-02 19:26 ` Stefan Ring 2021-12-02 19:35 ` Stefan Ring 2021-12-02 21:58 ` David Brown 1 sibling, 1 reply; 7+ messages in thread From: Stefan Ring @ 2021-12-02 19:26 UTC (permalink / raw) To: gcc-help On Thu, Dec 2, 2021 at 5:22 PM Sébastien Gendre <seb@k-7.ch> wrote: > > And I'm sure I can run code on the microcontroller because if I don't > declare the pointers to the registers and I just declare an int variable > and set its values multiple times it work (I can run it, inspect > variables, place breakpoints, etc). For debug I use GDB. I would at least not use Thumb in the beginning. This is notoriously annoying to debug in gdb because you can never be sure that the CPU's and the debugger's opinion on the current operating mode agree. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Wrong assembly code generated from C code (ARM Cortex-m3) ? 2021-12-02 19:26 ` Stefan Ring @ 2021-12-02 19:35 ` Stefan Ring 0 siblings, 0 replies; 7+ messages in thread From: Stefan Ring @ 2021-12-02 19:35 UTC (permalink / raw) To: gcc-help On Thu, Dec 2, 2021 at 8:26 PM Stefan Ring <stefanrin@gmail.com> wrote: > I would at least not use Thumb in the beginning. This is notoriously > annoying to debug in gdb because you can never be sure that the CPU's > and the debugger's opinion on the current operating mode agree. Ok, forget about that: cc1: error: target CPU does not support ARM mode ;) ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Wrong assembly code generated from C code (ARM Cortex-m3) ? 2021-12-02 16:06 ` Sébastien Gendre 2021-12-02 19:26 ` Stefan Ring @ 2021-12-02 21:58 ` David Brown 1 sibling, 0 replies; 7+ messages in thread From: David Brown @ 2021-12-02 21:58 UTC (permalink / raw) To: Sébastien Gendre; +Cc: gcc-help On 02/12/2021 17:06, Sébastien Gendre wrote: > Hello David, > > Thanks for your reply. > > Writing a firmware from scatch and not using the official IDE is the > challenge. > > It's based on this video of Nikolay Kondrashov who do the same thing for > another microcontroller with the same arch: > https://media.ccc.de/v/ASG2019-161-microcontroller-firmware-from-scratch > > I started by reading the arch and microcontroller manuals to see how its > start, how the memory are mapped, which clock and peripherals are > enabled after a reset, etc. > OK - but again, this is not the list for such things, as you are not facing gcc issues. I had a look at some of that video (not all of it as yet). I can agree that it can be fun and educational to do things from scratch, and see how things fit together at the lowest level. But I don't think you should start there. And it is wrong to suggest that this is all somehow necessary in order to use open source tools - when you use vendor-supplied tools these days, the solid majority of the parts are open source. They are simply put together by the vendor. Also note that if you are trying to run code directly from the reset vector, you don't have a full C environment. Your static data is not initialised or zeroed. Functions such as "printf" are unlikely to work. If you are using C++, static constructors or initialisers that require functions are not run. The reset vector is useful for things that need to be initialised before this - perhaps enabling clocks, memories, etc. It is not appropriate for "real" code. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Wrong assembly code generated from C code (ARM Cortex-m3) ? 2021-12-02 12:38 Wrong assembly code generated from C code (ARM Cortex-m3) ? Sébastien Gendre 2021-12-02 15:44 ` David Brown @ 2021-12-08 5:51 ` Sébastien Gendre 1 sibling, 0 replies; 7+ messages in thread From: Sébastien Gendre @ 2021-12-08 5:51 UTC (permalink / raw) To: gcc-help So, I did apply some modifications in my C code and in my Makefile and now it's working: The generated assembly code is correct. But I don't understand why. Maybe I misused GCC. If someone can help me. First, I added theses flags to GCC: -fmessage-length=0 -fno-builtin -ffunction-sections -fdata-sections -fmerge-constants -fstack-usage I take them from the flags used by the official IDE for this microcontroller. But this give the same result of the CPU reset. And the same assembly code generated for the C code line `*PINMODE1 = 0x00003000;`. I know, it's not a good idea to just copy compiler flags without look in the manual to see what they do. But everyone I ask around me for help just say "do as the official IDE do". So I do, without success. Then, I've declared some error handlers functions, add them on the reset vector table and enable them with the corresponding register. Like this, when an error occur, the CPU do not reset any more and run one of the corresponding error handler function. So, I can look in the debug registers and inspect the microcontroller. These handlers simply run an infinite loop. And suddenly, when I tried to reproduce the error, no more CPU reset. I don't understand why, because I do no modification on the C code line that cause the CPU reset. Before my modifications, this C code line inside the "reset()" function: ``` *PINMODE1 = 0x00003000; ``` Give this assembly: ``` => 0x0000001a <+18>: ldr r3, [r7, #8] 0x0000001c <+20>: strb r6, [r0, #28] 0x0000001e <+22>: ; <UNDEFINED> instruction: 0xf590601a 0x00000022 <+26>: ldr r3, [r7, #4] ``` Now it give: ``` => 0x0000004e <+18>: ldr r3, [r7, #8] 0x00000050 <+20>: mov.w r2, #12288 ; 0x3000 0x00000054 <+24>: str r2, [r3, #0] ``` The first group of assembly code lines give a CPU reset, the second one do not. How, without any modification in a line of C code, the assembly code generated can be different ? This is the full C code: ``` /* Blink.c A simple LED blinking on the LPC1769 */ // Functions declarations, see their full code declaration for // documentation void reset(); void NMI_Handler(void); void HardFault_Handler(void); void MemoryManagementFault_Handler(void); void BusFault_Handler(void); void UsageFault_Handler(void); /* NMI_Handler Function run to handle NMI */ void NMI_Handler(void) { while (1) { } } /* HardFault_Handler Function run to handle Hard fault */ void HardFault_Handler(void) { while (1) { } } /* MemoryManagementFault_Handler Function run to handle memory managment fault */ void MemoryManagementFault_Handler(void) { while (1) { } } /* BusFault_Handler Function run to handle Bus fault */ void BusFault_Handler(void) { while (1) { } } /* UsageFault_Handler Function run to handle Usage fault */ void UsageFault_Handler(void) { while (1) { } } /* reset Function run right after the reset of the CPU */ void reset() { // Define some registers // Register to define mode of the pins P0.16 to P0.26 volatile unsigned int *PINMODE1 = (unsigned int *)0x4002C044; // Register to define GPIO direction of pins P0.0 to P0.31 volatile unsigned int *FIO0DIR = (unsigned int *)0x2009C000; // Register to define GPIO value of pins P0.0 to P0.31 volatile unsigned int *FIO0SET = (unsigned int *)0x2009C000; // Config the GPIO to drive a LED // Enable P0.22 pin to put the line to ground /* *PINMODE1 |= (0x1<<12); */ /* *PINMODE1 |= (0x1<<13); */ *PINMODE1 = 0x3000; // Set P0.22 as GPIO output /* *FIO0DIR |= (0x1<<22); */ *FIO0DIR = 0x400000; // To the eternity while (1) { // Wait for (int i = 0; i < 500000; ++i); // Toggle the LED via P0.22 *FIO0SET ^= (0b1<<22); } } int STACK[256]; const void *vectors[] __attribute__ ((section (".vectors"))) = { STACK + sizeof(STACK) / sizeof(*STACK), reset, NMI_Handler, HardFault_Handler, MemoryManagementFault_Handler, BusFault_Handler, UsageFault_Handler, }; ``` And the full Makefile: ``` CFLAGS = -ggdb -Wall CFLAGS += -mthumb -mcpu=cortex-m3 -O0 -fmessage-length=0 -fno-builtin -ffunction-sections -fdata-sections -fmerge-constants -fstack-usage CC = arm-none-eabi-gcc LD = arm-none-eabi-ld OBJCPY = arm-none-eabi-objcopy all: blink.bin blink.bin: blink.elf @echo "Make the binary" $(OBJCPY) -O binary blink.elf blink.bin blink.elf: blink.o @echo "Linkage" $(LD) -T blink.ld -o blink.elf blink.o blink.o: blink.c @echo "Compile the code" $(CC) -c $(CFLAGS) -o blink.o blink.c clean: @echo "Clean the working dir" rm blink.o blink.elf blink.bin debug: blink.elf gdb -x gdbconfig blink.elf debug-mi: blink.elf gdb -i=mi -x gdbconfig blink.elf ``` The link script is the same. Best regards ------- Gendre Sébastien Sébastien Gendre <seb@k-7.ch> writes: > Hello, > > I got a problem with an assembly code generated by GCC from a C code. > > > A little bit of context: > > I try to write a minimal firmware for a micro-controller, the LPC1769. > It use ARM Cortex-M3 CPU. My firmware is simple: > * Run a function named "reset()" at CPU reset > * In this function, I declare 3 locals variables: 3 pointers to 3 > differents registers, initialized with their address > * Then, I write a value in each register > > > The problem: > > The CPU reset after the first value I write in a register > > This is the code of the function "reset()": > ``` c > void reset() { > // Define some registers > // Register to define mode of the pins P0.16 to P0.26 > unsigned int *PINMODE1 = (unsigned int *)0x4002C044; > // Register to define GPIO direction of pins P0.0 to P0.31 > unsigned int *FIO0DIR = (unsigned int *)0x2009C000; > // Register to define GPIO value of pins P0.0 to P0.31 > unsigned int *FIO0SET = (unsigned int *)0x2009C018; > > // Config the GPIO to drive a LED > // Enable P0.22 pin to put the line to ground > *PINMODE1 = 0x00003000; > // Set P0.22 as GPIO output > *FIO0DIR = 0x400000; > > // To the eternity > while (1) { > // Wait > for (int i = 0; i < 500000; ++i); > > // Toggle the LED via P0.22 > *FIO0SET ^= (0b1<<22); > } > > } > ``` > > The problem is at the line `*PINMODE1 = 0x00003000;`. This is the > assembly code generated by GCC and view from GDB: > > ``` assembly > => 0x0000001a <+18>: ldr r3, [r7, #8] > 0x0000001c <+20>: strb r6, [r0, #28] > 0x0000001e <+22>: ; <UNDEFINED> instruction: 0xf590601a > 0x00000022 <+26>: ldr r3, [r7, #4] > ``` > > The first assembly code, ldr, load the address `0x4002C044` into the > register `r3`. But the second asm code, `strb`, is it store the value > from the register `r6` to the address made frome the value of register > `r0` plus a shift of 28 ? Why not simply copy the value `0x00003000` > to the adress stored in `r3` register ? Why this undefined instruction > `0xf590601a` ? This third asm code, the "undefied" `0xf590601a` is the > instruction that make the CPU reset. > > I'm not an expert in assemply and I don't know if it's me that don't > understand it of if the assembly code generated by GCC is wrong. Any > help is welcome. ;) > > Thank you. :) > > This is the version of arm-none-eabi-gcc I use: > 11.1.0 (Fedora 11.1.0-2.fc35) > > Version of GDB: > 11.1 (Fedora 11.1-2.fc35) > > Version of OpenOCD: > 0.11.0 (from Fedora repo) > > This is the full C code: > ``` c > /* reset > > Function run right after the reset of the CPU > */ > void reset() { > // Define some registers > // Register to define mode of the pins P0.16 to P0.26 > unsigned int *PINMODE1 = (unsigned int *)0x4002C044; > // Register to define GPIO direction of pins P0.0 to P0.31 > unsigned int *FIO0DIR = (unsigned int *)0x2009C000; > // Register to define GPIO value of pins P0.0 to P0.31 > unsigned int *FIO0SET = (unsigned int *)0x2009C018; > > // Config the GPIO to drive a LED > // Enable P0.22 pin to put the line to ground > /* *PINMODE1 |= (0x1<<12); */ > /* *PINMODE1 |= (0x1<<13); */ > *PINMODE1 = 0x00003000; > // Set P0.22 as GPIO output > /* *FIO0DIR |= (0x1<<22); */ > *FIO0DIR = 0x400000; > > // To the eternity > while (1) { > // Wait > for (int i = 0; i < 500000; ++i); > > // Toggle the LED via P0.22 > *FIO0SET ^= (0b1<<22); > } > > } > > int STACK[256]; > > const void *vectors[] __attribute__ ((section (".vectors"))) = { > STACK + sizeof(STACK) / sizeof(*STACK), > reset > }; > ``` > > > This is my link script: > ``` > MEMORY { > flash (RX) : ORIGIN = 0x00000000, LENGTH = 512K > sram (RW!X) : ORIGIN = 0x10000000, LENGTH = 32K > } > SECTIONS { > .vectors : { *(.vectors) } >flash > .text : { *(.text) } >flash > .rodata : { *(.rodata) } >flash > .bss : { *(.bss) } >sram > } > ``` > > This is my Makefile: > ``` > CFLAGS = -g -O0 -Wall > CFLAGS += -mthumb -mcpu=cortex-m3 > > CC = arm-none-eabi-gcc > LD = arm-none-eabi-ld > OBJCPY = arm-none-eabi-objcopy > > > all: blink.bin > > > blink.bin: blink.elf > @echo "Make the binary" > $(OBJCPY) -O binary blink.elf blink.bin > > blink.elf: blink.o > @echo "Linkage" > $(LD) -T blink.ld -o blink.elf blink.o > > blink.o: blink.c > @echo "Compile the code" > $(CC) -c $(CFLAGS) -o blink.o blink.c > > clean: > @echo "Clean the working dir" > rm blink.o blink.elf blink.bin > > debug: blink.elf > gdb -x gdbconfig blink.elf > > debug-mi: blink.elf > gdb -i=mi -x gdbconfig blink.elf > ``` ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2021-12-08 6:29 UTC | newest] Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2021-12-02 12:38 Wrong assembly code generated from C code (ARM Cortex-m3) ? Sébastien Gendre 2021-12-02 15:44 ` David Brown 2021-12-02 16:06 ` Sébastien Gendre 2021-12-02 19:26 ` Stefan Ring 2021-12-02 19:35 ` Stefan Ring 2021-12-02 21:58 ` David Brown 2021-12-08 5:51 ` Sébastien Gendre
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).