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 |
|
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? E: figured out a couple of issues and trying to solve them. |
|||
06 Jan 2012, 00:48 |
|
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! |
|||
06 Jan 2012, 17:53 |
|
revolution 15 Jan 2012, 09:09
The first instruction is actually:
Code: F8DF6038 ldr r6,[r15,0x38] 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 |
|||
15 Jan 2012, 09:09 |
|
malpolud 15 Jan 2012, 20:06
Thank you revolution actually I was waiting for your help
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. |
|||
15 Jan 2012, 20:06 |
|
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? 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. |
|||
15 Jan 2012, 20:55 |
|
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... |
|||
20 Feb 2012, 00:06 |
|
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 |
|||
20 Feb 2012, 00:16 |
|
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 |
|||
20 Feb 2012, 00:26 |
|
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 I'm writing the firmware directly to the built in memory, but I'd like to understand how it works |
|||
20 Feb 2012, 00:29 |
|
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: So we think it's a 32 bit instruction, but it isn't? |
|||
20 Feb 2012, 00:39 |
|
revolution 20 Feb 2012, 00:48
malpolud wrote: Ok, but with 6054f8df we've got: revolution wrote:
|
|||
20 Feb 2012, 00:48 |
|
malpolud 20 Feb 2012, 01:37
Ok, finally I got it Thank you for your tips.
revolution wrote: The first instruction is actually: 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 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! |
|||
20 Feb 2012, 01:37 |
|
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. malpolud wrote: There is one small thing left. 0x6038= 0x6 (register r6) shl 12 + 0x038 (offset) |
|||
20 Feb 2012, 02:10 |
|
malpolud 20 Feb 2012, 07:18
Well I shouldn't ask my last question
Thanks for help revolution |
|||
20 Feb 2012, 07:18 |
|
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 (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! And for all your precious tips. |
|||
21 Feb 2012, 19:40 |
|
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. |
|||
22 Feb 2012, 04:54 |
|
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. |
|||
22 Feb 2012, 07:46 |
|
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. |
|||
26 Feb 2012, 19:54 |
|
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. |
|||
29 Aug 2012, 15:09 |
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.