public inbox for gcc-help@gcc.gnu.org
 help / color / mirror / Atom feed
From: "Sébastien Gendre" <seb@k-7.ch>
To: David Brown <david.brown@hesbynett.no>
Cc: gcc-help@gcc.gnu.org
Subject: Re: Wrong assembly code generated from C code (ARM Cortex-m3) ?
Date: Thu, 02 Dec 2021 17:06:37 +0100	[thread overview]
Message-ID: <87lf13geou.fsf@k-7.ch> (raw)
In-Reply-To: <1c1f6b8e-b6ac-042a-aed1-8779b2a1cf83@hesbynett.no>

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


  reply	other threads:[~2021-12-02 16:21 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-12-02 12:38 Sébastien Gendre
2021-12-02 15:44 ` David Brown
2021-12-02 16:06   ` Sébastien Gendre [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87lf13geou.fsf@k-7.ch \
    --to=seb@k-7.ch \
    --cc=david.brown@hesbynett.no \
    --cc=gcc-help@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).