How to write at least two `area`s in ARM assembly? - arm

I'm trying to write an area which defines my data on the RAM, and an area for my code. I tried to do that, but I just can't make it work. I also tried using EXPORT and IMPORT but I couldn't solve various errors while using them.
AREA HEAP, READWRITE, ALIGN=3
MYSTR DCB "JaVid",0
AREA RESET, CODE, READONLY
;IMPORT MYSTR
ENTRY
ADR R0, MYSTR ;STRING POINTER
NEXT LDRB R1, [R0] ;CHARACTER HOLDER
CMP R1, #'a'
BLT OK
CMP R1, #'z'
BGT OK
;WE NEED TO SWITCH
SUB R1, #'a'-'A'
OK STRB R1, [R0], #1
B NEXT
END
Would you please give me an example of how it's done?

I suspect you may be having problems because you cannot use ADR to refer to a symbol in another AREA. See this section of the fine manual
You probably want to use LDR r0, =MYSTR instead.
Also, your loop does not seem to terminate.
[Note: It would be helpful it you showed the exact error message you are getting and the command lines you are using with the code above.]

Related

What would be a reason to use ADDS instruction instead of the ADD instruction in ARM assembly?

My course notes always use ADDS and SUBS in their ARM code snippets, instead of the ADD and SUB as I would expect. Here's one such snippet for example:
__asm void my_capitalize(char *str)
{
cap_loop
LDRB r1, [r0] // Load byte into r1 from memory pointed to by r0 (str pointer)
CMP r1, #'a'-1 // compare it with the character before 'a'
BLS cap_skip // If byte is lower or same, then skip this byte
CMP r1, #'z' // Compare it with the 'z' character
BHI cap_skip // If it is higher, then skip this byte
SUBS r1,#32 // Else subtract out difference to capitalize it
STRB r1, [r0] // Store the capitalized byte back in memory
cap_skip
ADDS r0, r0, #1 // Increment str pointer
CMP r1, #0 // Was the byte 0?
BNE cap_loop // If not, repeat the loop
BX lr // Else return from subroutine
}
This simple code for example converts all lowercase English in a string to uppercase. What I do not understand in this code is why they are not using ADD and SUB commands instead of ADDS and SUBS currently being used. The ADDS and SUBS command, afaik, update the APSR flags NZCV, for later use. However, as you can see in the above snippet, the updated values are not being utilized. Is there any other utility of this command then?
Arithmetic instructions (ADD, SUB, etc) don't modify the status flag, unlike comparison instructions (CMP,TEQ) which update the condition flags by default. However, adding the S to the arithmetic instructions(ADDS, SUBS, etc) will update the condition flags according to the result of the operation. That is the only point of using the S for the arithmetic instructions, so if the cf are not going to be checked, there is no reason to use ADDS instead of ADD.
There are more codes to append to the instruction (link), in order to achieve different purposes, such as CC (the conditional flag C=0), hence:
ADDCC: do the operation if the carry status bit is set to 0.
ADDCCS: do the operation if the carry status bit is set to 0 and afterwards, update the status flags (if C=1, the status flags are not overwritten).
From the cycles point of view, there is no difference between updating the conditional flags or not. Considering an ARMv6-M as example, ADDS and ADD will take 1 cycle.
Discard the use of ADD might look like a lazy choice, since ADD is quite useful for some cases. Going further, consider these examples:
SUBS r0, r0, #1
ADDS r0, r0, #2
BNE go_wherever
and
SUBS r0, r0, #1
ADD r0, r0, #2
BNE go_wherever
may yield different behaviours.
As old_timer has pointed out, the UAL becomes quite relevant on this topic. Talking about the unified language, the preferred syntax is ADDS, instead of ADD (link). So the OP's code is absolutely fine (even recommended) if the purpose is to be assembled for Thumb and/or ARM (using UAL).
ADD without the flag update is not available on some cortex-ms. If you look at the arm documentation for the instruction set (always a good idea when doing assembly language) for general purpose use cases that is not available until a thumb2 extension on armv7-m (cortex-m3, cortex-m4, cortex-m7). The cortex-m0 and cortex-m0+ and generally wide compatibility code (which would use armv4t or armv6-m) doesn't have an add without flags option. So perhaps that is why.
The other reason may be to get the 16-bit instruction not the 32, but but that is a slippery slope as it gets even more into assemblers and their syntax (syntax is defined by the assembler, the program that processes assembly language, not the target). For example not syntax unified gas:
.thumb
add r1,r2,r3
Disassembly of section .text:
00000000 <.text>:
0: 18d1 adds r1, r2, r3
The disassembler knows reality but the assembler doesn't:
so.s: Assembler messages:
so.s:2: Error: instruction not supported in Thumb16 mode -- `adds r1,r2,r3'
but
.syntax unified
.thumb
adds r1,r2,r3
add r1,r2,r3
Disassembly of section .text:
00000000 <.text>:
0: 18d1 adds r1, r2, r3
2: eb02 0103 add.w r1, r2, r3
So not slippery in this case, but with the unified syntax you start to get into blahw, blah.w, blah, type syntax and have to spin back around to check to see that the instructions you wanted are being generated. Non-unified has its own games as well, and of course all of this is assembler-specific.
I suspect they were either going with the only choice they had, or were using the smaller and more compatible instruction, especially if this were a class or text, the more compatible the better.

ARM Cortex-M3 boot from RAM initial state

I have two ARM Cortex-M3 chips: STMF103C8T6 and STM32F103VET6.
When set to boot from RAM, initial state of STMF103C8T6 PC register is 0x20000108; 0x200001e0 for STM32F103VET6.
I am unable to find and information about these addresses in the datasheets. Why are they booted this way and where I can find some information about it?
Edit:
To clarify. When chip set to boot from flash, PC register points to the location of the Reset Handler. This address is provided in the reset vector table at address 0x0. But when chip set to boot from RAM, PC points to constant addresses, mentioned above.
Edit 2:
STMF103C8T6 disassembly:
20000000 <Vectors>:
20000000: 20005000 andcs r5, r0, r0
20000004: 2000010f andcs r0, r0, pc, lsl #2
20000008: 2000010d andcs r0, r0, sp, lsl #2
2000000c: 2000010d andcs r0, r0, sp, lsl #2
20000010: 2000010d andcs r0, r0, sp, lsl #2
20000014: 2000010d andcs r0, r0, sp, lsl #2
20000018: 2000010d andcs r0, r0, sp, lsl #2
...
20000108: f000 b801 b.w 2000010e <Reset_Handler>
2000010c <HardFault_Handler>:
2000010c: e7fe b.n 2000010c <HardFault_Handler>
2000010e <Reset_Handler>:
...
STM32F103VET6 disassembly:
20000000 <Vectors>:
20000000: 20005000 andcs r5, r0, r0
20000004: 200001e7 andcs r0, r0, r7, ror #3
20000008: 200001e5 andcs r0, r0, r5, ror #3
2000000c: 200001e5 andcs r0, r0, r5, ror #3
20000010: 200001e5 andcs r0, r0, r5, ror #3
20000014: 200001e5 andcs r0, r0, r5, ror #3
20000018: 200001e5 andcs r0, r0, r5, ror #3
...
200001e0: f000 b801 b.w 200001e6 <Reset_Handler>
200001e4 <HardFault_Handler>:
200001e4: e7fe b.n 200001e4 <HardFault_Handler>
200001e6 <Reset_Handler>:
...
I am unable to find and information about these addresses in the datasheets. Why are they booted this way and where I can find some information about it?
As far as I am aware, there is no official documentation from ST that so much as mentions this behavior, let alone explains it in any detail. The STM32F1 family reference manual states vaguely in section 3.4 ("Boot Configuration") that:
Due to its fixed memory map, the code area starts from address 0x0000 0000 (accessed through the ICode/DCode buses) while the data area (SRAM) starts from address 0x2000 0000 (accessed through the system bus). The Cortex®-M3 CPU always fetches the reset vector on the ICode bus, which implies to have the boot space available only in the code area (typically, Flash memory). STM32F10xxx microcontrollers implement a special mechanism to be able to boot also from SRAM and not only from main Flash memory and System memory.
The only place these addresses and values are referenced at all is in some of their template startup files -- and even then, not all of them. The SPL startup files supplied for the ARM and IAR toolchains lack support for BootRAM; this functionality is only included in the startup files for the GCC and TrueSTUDIO toolchains.
Anyways. Here's my best analysis of the situation.
When a STM32F1 part is reset, the memory block starting at 0x00000000 is mapped based on the configuration of the BOOT pins. When it is set to boot from flash, that block is aliased to flash; when it is set to run from the bootloader, that block is aliased to a block of internal ROM (around or slightly below 0x1FFFF000). However, when it is set to boot from RAM, something very strange happens.
Instead of aliasing that memory block to SRAM, as you would expect, that memory block is aliased to a tiny (16 byte!) ROM. On a STM32F103C8 (medium density) part, this ROM has the contents:
20005000 20000109 20000004 20000004
This data is interpreted as a vector table:
The first word causes the stack pointer to be initialized to 0x20005000, which is at the top of RAM.
The second word is the reset vector, and is set to 0x20000108 (with the low bit set to enable Thumb mode). This address is in RAM as well, a few words beyond the end of the vector table, and it's where you're supposed to put the "magic" value 0xF108F85F. This is actually the instruction ldr.w pc, [pc, #-480], which loads the real reset vector from RAM and branches to it.
The third and fourth words are the NMI and hardfault vectors. They do not have the low bit set, so the processor will double-fault if either of these exceptions occurs while VTOR is still zero. Confusingly, the PC will be left pointing to the vector table in RAM.
The exact contents of this ROM vary slightly from part to part. For example, a F107 (connectivity line) has the ROM contents:
20005000 200001e1 20000004 20000004
which has the same initial SP, but a different initial PC. This is because this part has a larger vector table, and the medium-density address would be inside its vector table.
A full list of the locations and values used is:
Low/medium density: 0x0108 (value: 0xF108F85F)
Low/medium density value line: 0x01CC (value: 0xF1CCF85F)
Note: ST's sample files give the same value as for low/medium density parts. I'm pretty sure this is wrong and have corrected it here, but I don't have any parts of this type to test with. I'd appreciate feedback to confirm if this works.
All others: 0x01E0 (value: 0xF1E0F85F)
Thankfully, this behavior seems to be largely unique to the F103/5/7 family. Newer parts use different methods to control booting which are much more consistent.

ARM Branch Prediction based on code

so I'm trying to study for this test and one of the things on the study guide gives us some ARM Code and tells us to fill in a branch prediction table based on how the code runs.
I can understand how I'm supposed to do it, but with branch prediction the actual outcome comes into play, too, and I can't figure out the outcome (per cycle) from the code. The code is:
MOV r0, #4
B1 MOV r2, #5; Branch 1
SUB r2, r2, r0
B2 SUBS r2, r2, #1; Branch 2
BNE B2
SUBS r0, r0, #1
BNE B1
What's confusing me is the BNE statements. Usually when I see one of those conditional statements there has been a CMP statement earlier in the code, and that way I can know whether to take the branch or not. But I see no compare statement anywhere in this code, so I don't know how to determine if I should take a branch or not.
The SUBS performs a subtraction and a comparison with zero of the result in one instruction. The BNE conditional branches use the condition flags that were set by these SUBS instructions

ARMv7 assembly store values in an array

I'm trying to add a user inputted number to a every element in an array. I had everything working until I realized that the original array was not being updated. Simple, I thought, just store the value back into the array and move on with life. Sadly, this doesn't seem to be quite so simple.
As the title suggests, I'm using ARMv7 and writing assembly. I've been using this guide to understand the basics and to have some good code to look at. When I run the example code given here it works fine: str r2, [r3] puts whatever is in r2 into what r3 points at. The following is my attempt to do the same thing which gives me a Signal 11 occurred: SIGSEGV (Invalid memory segment access) and Execution stopped at: 0x0000580C STR r3,[r5,#0]:
# Loop and add value to all values in array regardless of array length
# Setup loop
# r4 comes from above and the scanf value, I've checked the registers and the value is correct
mov r0, #0
ldr r1, =array_b
ldr r2, addrArr
loop: # Start loop to add inputed number to every value in array
add r3, r2, r0
ldr r3, [r3]
add r3, r3, r4 # Add input to each index in array
add r5, r2, r0 # Pointer to location in array
str r3, [r5] # Put new value into array
cmp r0, r1 # Check for end of array
addne r0, r0, #4 # Not super necessary but it shows one of the cool things ARM can do, condition math
bne loop # Branch if not equal
beq doneLoop # Branch if equal
doneLoop: # End loop
Here are the vars
.align 2
array:
.word 0
.word 1
.word 2
.word 3
.word 4
.word 5
.word 6
.word 7
.equ array_b, .-array
addrArr: .word array
My understanding is that str takes the source first and the destination second (which is different from other instructions for some reason). So r5 is used to calculate where in the array to store the value and r3 has the value from the add instruction. I've checked and the value in r5 is valid, ie: it's the start of the array and the array_b is the proper length (32 in this case). I've also tried doing =array instead of addrArr but they give the same value and the same segfault message.
This is because there is historically in systems two major kind of memories :
ROM, Read Only Memory, cannot be written to and can store only the program and constant data
RAM, Random Access Memory, can be both read and written to. It is used to store variables.
Many systems do no use ROM directly, instead the data can be loaded from an other permanent support, for example a floppy disc, a tape or a hard disc into RAM. In order to avoid a program writing to RAM memory that wasn't supposed to be written, the RAM can be divided in multiple areas, using segmented memory.
Not all system features this, so it really depends on the architecture. If segmented memory is used, it basically makes the processor quit the application when you try to write to a segment of RAM that is designed to be read only. This is exactly what appears to be your problem here.
In order to solve this you should declare your array, which is a variable and should be stocked in RAM, by precedding it by .data.
On the other hand your executable instructions should be placed in the read only segment marked with the assembler directive .text

Cortex m3 first instruction execution

I am using Sourcery CodeBench Lite 2012.03-56 compiler and gdb suite with texane gdb server.
Today I wanted to try FreeRTOS demo example for cheap STM32VLDISCOVERY board, I copied all the source files needed, compiled without errors but the example didn't work. I fired up debugger and noticed that example fails when it is trying to dereference pointer to GPIO registers. Global array variable which contains pointers to GPIO registers:
GPIO_TypeDef* GPIO_PORT[LEDn] = {LED3_GPIO_PORT, LED4_GPIO_PORT};
was not properly initialized and was filled with some random values. I checked preprocessor defines LED3_GPIO_PORT and LED3_GPIO_PORT and they were valid.
After some researching where the problem might be I looked at the start-up file provided for trueSTUDIO found in CMSIS lib. Original startup_stm32f10x_md_vl.S file:
.section .text.Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
/* Copy the data segment initializers from flash to SRAM */
movs r1, #0
b LoopCopyDataInit
CopyDataInit:
ldr r3, =_sidata
ldr r3, [r3, r1]
str r3, [r0, r1]
adds r1, r1, #4
LoopCopyDataInit:
ldr r0, =_sdata
ldr r3, =_edata
adds r2, r0, r1
cmp r2, r3
bcc CopyDataInit
ldr r2, =_sbss
b LoopFillZerobss
...
During the debugging I noticed that register r1 is never initialized to zero by first instruction movs r1, #0. Register r1 is used as a counter in loop so when the execution reaches loop LoopCopyDataInit it never enters the loop since register r1 is loaded with some garbage data from previous execution. As the result of this the startup code never initializes the .data section.
When I placed two nop instructions before movs r1, #0 instruction then register r1 was initialized to 0 and the example began to work:
Modified part of startup_stm32f10x_md_vl.S file:
/* Copy the data segment initializers from flash to SRAM */
nop
nop
movs r1, #0
b LoopCopyDataInit
This is disassembly of relevant parts of final code:
Disassembly of section .isr_vector:
08000000 <g_pfnVectors>:
8000000: 20002000 andcs r2, r0, r0
8000004: 08000961 stmdaeq r0, {r0, r5, r6, r8, fp}
...
Disassembly of section .text:
...
8000960 <Reset_Handler>:
8000960: 2100 movs r1, #0
8000962: f000 b804 b.w 800096e <LoopCopyDataInit>
08000966 <CopyDataInit>:
8000966: 4b0d ldr r3, [pc, #52] ; (800099c <LoopFillZerobss+0x16>)
8000968: 585b ldr r3, [r3, r1]
800096a: 5043 str r3, [r0, r1]
800096c: 3104 adds r1, #4
As you can see the ISR vector table is properly pointing to Reset_Handler address. So, what is happening? Why the first instruction movs r1, #0 was never executed in original start-up code?
EDIT:
The original code works when I power off the board and power it back on again. I can reset the MCU multiple times and it works. When I start gdb-server then the code doesn't work, even after reset. I have to power cycle it again to work. I guess this is some debugger weirdness going on.
NOTE:
I had a look what start-up code are other people using with this MCU and they either disable interrupts or load SP register with a linker defined value which is in both cases redundant. If they got hit by this odd behaviour they would not notice it, ever.
Sounds like a bug in your debugger. Probably it sets a breakpoint on the first instruction and either skips it completely or somehow reexecuting it doesn't work properly. The issue could be complicated by the fact that it's a reset vector, maybe it's just not possible to reliably stop at the first instruction. Since the NOPs help, I'd recommend leaving them in place while you're developing your program.
However, there is an alternative solution. Since it's unlikely that you'll need to modify the array, you don't really need it in writable section. To have the compiler put the array in the flash, usually it's enough to declare it as const:
GPIO_TypeDef* const GPIO_PORT[LEDn] = {LED3_GPIO_PORT, LED4_GPIO_PORT};
Nothing is hopping out right away as to what could be wrong there. First, how are you debugging this code? Are you attaching a debugger and then issuing a reset to the processor via JTAG? I would try putting b Reset_Handler in there right after your Reset_Handler: label as your first instruction, flash it on, turn on the board, then connect the JTAG just so you can minimize any possible weirdness from the debugger. Then set your PC to that mov instruction and see if it works. Is a bootloader or boot ROM launching this code? It could be something weird going on with the instruction or data cache.

Resources