Re: Wrong assembly code generated from C code (ARM Cortex-m3) ?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxx> 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
> ```





[Index of Archives]     [Linux C Programming]     [Linux Kernel]     [eCos]     [Fedora Development]     [Fedora Announce]     [Autoconf]     [The DWARVES Debugging Tools]     [Yosemite Campsites]     [Yosemite News]     [Linux GCC]

  Powered by Linux