flat assembler
Message board for the users of flat assembler.

Index > Non-x86 architectures > Could you post some guidance examples of ARM baremetal code?

Author
Thread Post new topic Reply to topic
malpolud



Joined: 18 Jul 2011
Posts: 344
Location: Broken hippocampus
malpolud 03 Jan 2012, 09:43
Hi, I've been writing for ARM Cortex M in C for a while, using GCC toolchain. I'm a bit confused reading manuals, and provided examples with FASMARM Could somebody let me have a look at some code which shows how to build and place a proper vector table, handle IRQ's and so on without an OS?

Is it possible at all with FASMARM? GCC linker took care of placing right code in right sections of the elf file. I can't tell FASM that my ROM starts at 0x08000000 and my code is THUMB so vector table should be adjusted.

Or am I soo wrong? Very Happy
Cheers!
Post 03 Jan 2012, 09:43
View user's profile Send private message Visit poster's website Reply with quote
malpolud



Joined: 18 Jul 2011
Posts: 344
Location: Broken hippocampus
malpolud 06 Jan 2012, 00:48
ok, so here is my attempt to write some code, processor: stm32f103rbt6

Code:
format elf
thumb                  ;this processor supports T2 instruction set
org 08000000h          ;flash starts at 0800 000h


                dw      20005000h                        ; stack pointer
                dw      _Reset_handler + 1               ; reset vector (In thumb mode value loaded to PC must have LSB set)
                dw      _NMI_handler + 1                 ; 
                dw      _HardFault_handler  + 1          ;
                dw      _MemManage_handler + 1           ;
                dw      _BusFault_handler + 1            ;
                dw      _UsageFault_handler + 1          ;

_Reset_handler:

                ldr r6, [RCC_APB2ENR]                    ;GPIOA clock enable
                movs r0, 4h
                str r0, [r6]


                ldr r6, [GPIOA_CRL]                      ;GPIOA config
                ldr r0, [CRL_CFG]
                str r0, [r6]


                movs r2, 0                                ;turn on LED
                movs r3, 4h                               ;turn off LED

                ldr r6, [GPIOA_ODR]                      ;point to Port A output data register
        
loop:
                str r2, [r6]                             ;clear Port A, pin 2, turning on LED
                ldr r1, [LEDDELAY]
delay1:
                subs r1, 1
                bne delay1
        
                str r3, [r6]                             ;set Port A, pin 2, turning off LED
                ldr r1, [LEDDELAY]
delay2:
                subs r1, 1
                bne delay2

        b loop

_dummy:
_NMI_handler:
_HardFault_handler:
_MemManage_handler:
_BusFault_handler:
_UsageFault_handler:
                      b _dummy

;some config
        GPIOA_CRL       dw   0x40010800
        GPIOA_ODR       dw   0x4001080C
        CRL_CFG         dw   0x44443444
        RCC_APB2ENR     dw   0x40021018
        STACKINIT       dw   0x20005000
        
        LEDDELAY        dw    800000           


Got a nice .o file. It's quite late so I'm done for today. The goal is to get an .elf or a .hex file that could be loaded directly to the memory via OpenOCD.
I'm not sure if it just needs to be linked properly or some features must be added to the linker. If an utility must be developed I could try to cope with it. Maybe revolution could help showing the right path? Wink

E: figured out a couple of issues and trying to solve them.
Post 06 Jan 2012, 00:48
View user's profile Send private message Visit poster's website Reply with quote
malpolud



Joined: 18 Jul 2011
Posts: 344
Location: Broken hippocampus
malpolud 06 Jan 2012, 17:53
Hi, sorry for posting 3 times at a row.

I managed to compile the code with FASMARM, link it with Code Sourcery GCC ld and burn it into flash.

The final code looks like this:
Code:
format elf

processor 7FC2080h                                       ;THUMB32 instruction set
thumb

section ".text"                                              
org 08000000h                                            ;both this and placing section in .text is necessary


                dw      20005000h                        ; stack pointer
                dw      _start + 1                       ; reset vector (manually adjust to odd for thumb)
                dw      _NMI_handler + 1                 ; some essential vectors
                dw      _HardFault_handler  + 1          ;
                dw      _MemManage_handler + 1           ;
                dw      _BusFault_handler + 1            ;
                dw      _UsageFault_handler + 1          ;

_start:

                ldr r6, [RCC_APB2ENR]                    ;GPIOA clock enable
                movs r0, 4h
                str r0, [r6]


                ldr r6, [GPIOA_CRL]                      ;GPIOA config
                ldr r0, [CRL_CFG]
                str r0, [r6]


                movs r2, 0                                ;turn on LED
                movs r3, 4h                               ;turn off LED

                ldr r6, [GPIOA_ODR]                       ;point to PortA output data register
        
loop:
                str r2, [r6]                              ;clear Port A, pin 2, turning on LED
                ldr r1, [LEDDELAY]
delay1:
                subs r1, 1
                bne delay1
        
                str r3, [r6]                              ;set Port A, pin 2, turning off LED
                ldr r1, [LEDDELAY]
delay2:
                subs r1, 1
                bne delay2

        b loop

_dummy:
_NMI_handler:
_HardFault_handler:
_MemManage_handler:
_BusFault_handler:
_UsageFault_handler:
                      b _dummy

;some config
        GPIOA_CRL       dw   0x40010800
        GPIOA_ODR       dw   0x4001080C
        CRL_CFG         dw   0x44443444
        RCC_APB2ENR     dw   0x40021018
        STACKINIT       dw   0x20005000
        
        LEDDELAY        dw    800000

section ".data"
    


linker script:
Code:
SECTIONS
{
        /* interrupt vectors start at zero */
        . = 0x8000000;  /* start of flash */

        .text :  {  *(.text)   }

        /* constant data follows code but still in flash */
        .data :
        { 
          *(.data) 
          *(.rom)
        }

        /* internal RAM starts at 0x20000000 */
        . = 0x20000000; 
        .ram : { *(.ram) }

        .bss :
        {
          *(.bss)
          *(.ram)
        }
}            



As I mentioned: the elf file can be burned into flash. The architecture requires that the very first value loaded into flash is the stack pointer. After then an interrupt vector table is placed.

After writing flash and reset: Stack pointer sets the correct value. Program counter goes to 800001Ch which is the right address of the first instruction after label the _start. Unfortunately the instruction stored under 800001Ch is something like:
0011 0000 00011 1000
which doesn't seem to be a valid instruction. I'm pretty sure the problem here is the chosen instruction set.

What really bothers me is why I have to place an offset in my code (I mean the 800 0000h offset), plus tell the linker to offset my .text section? Otherwise it either doesn't respect the memory map placing (starts executing from the beginning of the memory map) or just doesn't burn in flash at all.

Cheers!
Post 06 Jan 2012, 17:53
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20451
Location: In your JS exploiting you and your system
revolution 15 Jan 2012, 09:09
The first instruction is actually:
Code:
F8DF6038     ldr   r6,[r15,0x38]    
It is a 32-bit instruction combined with two 16-bit parts. It is a valid thumb instruction for a v7m capable processor. If you have a v5 processor then you would not be able to execute the v7m instructions.

Note that your processor selection value (7FC2080h) is very unorthodox. It enables the following:
Code:
V4T
X
Z
6M
7M
T2
V7
SYNC
DIV
T2EE
MP    
It would be very unlikely that a CPU will support v6M but not v5T. And also not having v1 through v6 but allowing v7 is just not right, since the v7 spec explicitly mandates the support for all older v1 through v6 instructions. While you can selectively disable certain parts of the instruction set, it is really only for testing purposes where you would want to allow only the new v7 instructions but disable all of the older v1-v6 instructions. There is no processor made that does that, so in production code you would probably not want to do that.
Post 15 Jan 2012, 09:09
View user's profile Send private message Visit poster's website Reply with quote
malpolud



Joined: 18 Jul 2011
Posts: 344
Location: Broken hippocampus
malpolud 15 Jan 2012, 20:06
Thank you revolution actually I was waiting for your help Smile

It's a v7m processor. So what I should do is to enable everything from V1 to V7M? I will check it later, I have to abandon my attempts for a couple of weeks due to intensified university work.

Thanks one more time.
Post 15 Jan 2012, 20:06
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20451
Location: In your JS exploiting you and your system
revolution 15 Jan 2012, 20:55
malpolud wrote:
It's a v7m processor. So what I should do is to enable everything from V1 to V7M?
If your CPU does not support ARM (i.e. only THUMB) instructions then you would enable V4T, X, Z, V5T, V6T, ALIGN, 6M, 7M. Maybe also T2, T2EE, SYNC and/or DIV if your CPU implements them.

Note that it can be disadvantageous to set the processor to a higher setting than the CPU is capable because of the extra encoding alternatives that the assembler has available.
Post 15 Jan 2012, 20:55
View user's profile Send private message Visit poster's website Reply with quote
malpolud



Joined: 18 Jul 2011
Posts: 344
Location: Broken hippocampus
malpolud 20 Feb 2012, 00:06
Ok, I've got my free time back, so I'm back having fun.

I tried to change processor encoding, but it's still not there. After enabling all from V1 to V4 plus V4T, X, Z, V5T, V6T, ALIGN, 6M, 7M (or the ones plus T2, T2EE) the first instruction is: 6054f8df. Or rather f8df? I'm checking ARM_v7m_arm out but it seems I make a mistake somewhere.

Could you explain how to figure out if a certain instruction is 16 or 32 bits? With F8DF6038 we've got five "ones" as bytes 31-27. Ok, 32 bit instruction. But if we run the second configuration, we read a word from 800001C we get 6054f8df. "Ones" only on 31 and 30 so it's a 16 bit instruction. It's a 16 bit instruction so we would rather read a half word. We do and what we get is: f8df which should be a 32bit instruction! Ehhh endianness...
Post 20 Feb 2012, 00:06
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20451
Location: In your JS exploiting you and your system
revolution 20 Feb 2012, 00:16
It depends upon which mode the CPU is in. In THUMB mode the instruction encoding is different from ARM mode. You can't look at a group of 32 bits and say what instruction is encoded until you know the CPU mode. These instruction encode differently in each mode:
Code:
use32 ;ARM mode
mov r1,r9
movs r1,r9
thumb ;thumb mode
mov r1,r9
movs r1,r9    
Also, instructions are always little endian encoded. But beware, in another thread there there is discussion about a system with firmware that reads instructions from an 8-bit eeprom in big-endian format and stores them in 16-bit memory in little-endian format for execution. So check your system to know what the firmware does with your binary code. If you are directly writing the firmware then this issue won't affect you.
Post 20 Feb 2012, 00:16
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20451
Location: In your JS exploiting you and your system
revolution 20 Feb 2012, 00:26
For example of mixing 32-bit and 16-bit thumb instructions:
Code:
thumb
00000000:     4E0E V4T    ldr   r6,[r15,0x38]
00000002:     BF00 6M     nop
00000004: F3AF8000 7M     nop
00000008:     4649 V4T    mov   r1,r9
0000000A: EA5F0109 7M     movs  r1,r9    
Code:
0E 4E 00 BF AF F3 00 80 49 46 5F EA 09 01    
Post 20 Feb 2012, 00:26
View user's profile Send private message Visit poster's website Reply with quote
malpolud



Joined: 18 Jul 2011
Posts: 344
Location: Broken hippocampus
malpolud 20 Feb 2012, 00:29
Thank you for your quick reply.

I used the "Thumb" directive, so it is in thumb mode. But I mean this:

v7m_arm wrote:
The Thumb instruction stream is a sequence of halfword-aligned halfwords. Each Thumb instruction is either a single 16-bit halfword in that stream, or a 32- bit instruction consisting of two consecutive halfwords in that stream.

Last time I decoded a wrong instruction, cause I read a halfword from my "start" address. You said it's wrong, cause I decoded a 16 bit instruction instead of a 32 bit one, and you were right Smile

I'm writing the firmware directly to the built in memory, but I'd like to understand how it works Wink
Post 20 Feb 2012, 00:29
View user's profile Send private message Visit poster's website Reply with quote
malpolud



Joined: 18 Jul 2011
Posts: 344
Location: Broken hippocampus
malpolud 20 Feb 2012, 00:39
Ok, but with 6054f8df we've got:
00: F8DF
02: 6054
DF F8 54 60.

If I read a hw from 00 i get F8DF = 1111100011011111. According to man:
v7m_arm wrote:
If bits [15:11] of the halfword being decoded take any of the following values, the halfword is the first halfword of a 32-bit instruction:
• 0b11101
• 0b11110
• 0b11111


So we think it's a 32 bit instruction, but it isn't?
Post 20 Feb 2012, 00:39
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20451
Location: In your JS exploiting you and your system
revolution 20 Feb 2012, 00:48
malpolud wrote:
Ok, but with 6054f8df we've got:
00: F8DF
02: 6054
DF F8 54 60.

If I read a hw from 00 i get F8DF = 1111100011011111. According to man:
v7m_arm wrote:
If bits [15:11] of the halfword being decoded take any of the following values, the halfword is the first halfword of a 32-bit instruction:
• 0b11101
• 0b11110
• 0b11111


So we think it's a 32 bit instruction, but it isn't?
Yes. The same as above:
revolution wrote:
Code:
F8DF6038     ldr   r6,[r15,0x38]    
with just the offset changed to 0x54
Post 20 Feb 2012, 00:48
View user's profile Send private message Visit poster's website Reply with quote
malpolud



Joined: 18 Jul 2011
Posts: 344
Location: Broken hippocampus
malpolud 20 Feb 2012, 01:37
Ok, finally I got it Smile Thank you for your tips.
revolution wrote:
The first instruction is actually:
Code:
F8DF6038     ldr   r6,[r15,0x38]    

I burned the flash once more and check whats the instruction and what I got after reading a word is 6038F8DF - I was a bit confused why you say 6054F8DF is the same instruction with F8DF6038.

It appears that with both processor directives proper value is loaded to R6. I'll debug the program step by step and search for the wrong instruction after I get some sleep Smile

There is one small thing left. 0x6038=
1100 0000111000
<Rt> <label>

111000 = 0x38 but 1100 =0xC so I would expect the value would be loaded to R12 not R6? Of course it is loaded to R6. Where should I search for the answer?
Cheers, and many thanks one more time!
Post 20 Feb 2012, 01:37
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20451
Location: In your JS exploiting you and your system
revolution 20 Feb 2012, 02:10
malpolud wrote:
I burned the flash once more and check whats the instruction and what I got after reading a word is 6038F8DF - I was a bit confused why you say 6054F8DF is the same instruction with F8DF6038.
I show the hex codes in the same format as the ARM manual shows. With each of the 16-bit portions shown in the order as they appear in the binary output, and within each 16-bit portion written as normal hex number with the MSD first. You should really think of a thumb 32-bit instruction as two separate 16-bit portions and not as one monolithic 32-bit portion, that way it will make more sense to show it in the format I give above.
malpolud wrote:
There is one small thing left. 0x6038=
1100 0000111000
<Rt> <label>

111000 = 0x38 but 1100 =0xC so I would expect the value would be loaded to R12 not R6? Of course it is loaded to R6. Where should I search for the answer?
You missed one bit. The offset is 12 bits.

0x6 (register r6) shl 12 + 0x038 (offset)
Post 20 Feb 2012, 02:10
View user's profile Send private message Visit poster's website Reply with quote
malpolud



Joined: 18 Jul 2011
Posts: 344
Location: Broken hippocampus
malpolud 20 Feb 2012, 07:18
Well I shouldn't ask my last question Smile

Thanks for help revolution
Post 20 Feb 2012, 07:18
View user's profile Send private message Visit poster's website Reply with quote
malpolud



Joined: 18 Jul 2011
Posts: 344
Location: Broken hippocampus
malpolud 21 Feb 2012, 19:40
Well after a couple whiles of debugging and finding out that FASMARM outputs perfect binary file that is executed correctly I figured out that the example provided does work. I've just mistaken the port I should toggle Very Happy (8h instead of 4h)

What I'd love to achieve: possibility to build a vector table that doesn't have to be manually adjusted to work correctly with T2 instruction set. Perhaps a directive should be added? And I want to use FASM's linker.

Thank you revolution for FASMARM, it's so exciting! Smile And for all your precious tips.
Post 21 Feb 2012, 19:40
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20451
Location: In your JS exploiting you and your system
revolution 22 Feb 2012, 04:54
There is a problem with automatically adding one to the thumb addresses. Namely that the assembler does not know for what purpose you are generating the address. For executing and mode switching then you need to add one. For data access you wouldn't want to add one.

I would suggest that you use something like a proc macro to define an extra label (perhaps with some extension like .thumb added to the label) to indicate that the label is an entry point. Then you can build jump tables and check whether each label is for thumb code entry or for data access.
Post 22 Feb 2012, 04:54
View user's profile Send private message Visit poster's website Reply with quote
malpolud



Joined: 18 Jul 2011
Posts: 344
Location: Broken hippocampus
malpolud 22 Feb 2012, 07:46
I already did something like:
Code:
macro vector name {dw name + 1}
    

but it's not sophisticated at all. I will try to do it the way you said.
Post 22 Feb 2012, 07:46
View user's profile Send private message Visit poster's website Reply with quote
malpolud



Joined: 18 Jul 2011
Posts: 344
Location: Broken hippocampus
malpolud 26 Feb 2012, 19:54
Ok, I have no idea how to do that, revolution you mean something like this:
Code:
vectors.thumb:
                    vector_1
                    ....
                    vector_n
endvect                          ;optionally
    

?
I've been looking at the source, but unfortunately I can not figure out how to do that. Could you give me a hint - maybe piece of proper FASM's source code?
Cheers


E: I forgot to mention I came up with something like this:
Code:
macro default_handler repeat
{
        rept repeat
        {
                dw _Default_handler + 1
        \}
}        
    

to skip unused interrupts. Will test it right now.

E2: These macrodefinitions are sufficient for now. Later on I'll think about an automatic vector table which checks whether the handler was defined or not.
Post 26 Feb 2012, 19:54
View user's profile Send private message Visit poster's website Reply with quote
malpolud



Joined: 18 Jul 2011
Posts: 344
Location: Broken hippocampus
malpolud 29 Aug 2012, 15:09
Hello. I solved most of the issues that were bothering me if anyone is curious you can check at my homepage http://openefi.blogspot.com/2012/05/arm-stm32-blink-led-and-interrupt.html

revolution wrote:
I would suggest that you use something like a proc macro to define an extra label (perhaps with some extension like .thumb added to the label) to indicate that the label is an entry point. Then you can build jump tables and check whether each label is for thumb code entry or for data access.


Could you please say something more about it? I don't quite understand yet how proc works, trying to gather some information.

_________________
There's nothing special about it,
It's either there when you're born or not.
Post 29 Aug 2012, 15:09
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.