Hi there,
so I've been experimenting a bit with machine code, execution from RAM and from flash. I'm using the Arduino Due and the standard Arduino Toolchain.
I have a function calc(), which has a body written in ARM Assembly and calculates a number (calculation on Due takes >1s). Now I tried to execute the raw machine code from RAM, once by defining an array and once by copying that array to a malloc'd region of RAM. Both work fine, yet execution from the malloc'd region is slower than from the array.
Now if I write the machine code to a different block in flash memory and try to execute it from there, it also executes fine, but far slower than when it's executed directly via a regular function call. (About the same speed as the malloc'd region)
#include <DueFlashStorage.h>
DueFlashStorage dueFlashStorage;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
unsigned char *rammem = (unsigned char *)malloc(1000);
unsigned char raw[] = {0x21, 0x21, 0x32, 0xB5, 0x0, 0xF0, 0x3, 0xF8, 0xBD, 0xE8, 0x32, 0x40, 0x70, 0x47, 0x1, 0x25, 0x1, 0x39, 0x2, 0x29, 0xC0, 0xF0, 0xB, 0x80, 0x1, 0x25, 0x22, 0xB5, 0xFF, 0xF7, 0xF7, 0xFF, 0xBD, 0xE8, 0x22, 0x40, 0x2D, 0x18, 0x2, 0x39, 0x1, 0x29, 0x3F, 0xF6, 0xF6, 0xAF, 0x28, 0x0, 0x70, 0x47, 0x70, 0x47};
for(int i=0; i<200; i++) {
*(rammem+i) = raw[i];
}
dueFlashStorage.write(0,(unsigned char *)&raw,200);
timeFnc(calc);
timeFnc((int (*)(void))(IFLASH1_ADDR+1));
timeFnc((int (*)(void))(rammem+1));
timeFnc((int (*)(void))(raw+1));
}
void timeFnc(int (*functionPtr)(void)) {
unsigned long time1 = micros();
int res = (*functionPtr)();
unsigned long time2 = micros();
Serial.print("Address: ");
Serial.print((unsigned int)functionPtr);
Serial.print(" Res: ");
Serial.print(res);
Serial.print(": ");
Serial.print(time2-time1);
Serial.println("us");
}
int calc() {
asm volatile(
"movs r1, #33 \n\t"
"push {r1,r4,r5,lr} \n\t"
"bl .in \n\t"
"pop {r1,r4,r5,lr} \n\t"
"bx lr \n\t"
".in: \n\t"
"movs r5,#1 \n\t"
"subs r1, r1, #1 \n\t"
"cmp r1, #2 \n\t"
"blo .lblb \n\t"
"movs r5,#1 \n\t"
".lbla: \n\t"
"push {r1, r5, lr} \n\t"
"bl .in \n\t"
"pop {r1, r5, lr} \n\t"
"adds r5,r0 \n\t"
"subs r1,#2 \n\t"
"cmp r1,#1 \n\t"
"bhi .lbla \n\t"
".lblb: \n\t"
"movs r0,r5 \n\t"
"bx lr \n\t"
::
);
}
void loop() {
}
The output of the program:
Address: 524617 Res: 3524578: 1354652us
Address: 786433 Res: 3524578: 1954441us
Address: 537334761 Res: 3524578: 1993470us
Address: 537427877 Res: 3524578: 1657181us
As we see, the calculated result is the same for all variants, the addresses confirm that two of the functions reside in RAM and two reside in flash memory. The first line is regular execution by calling the function, second line calls the function pointer pointing to the manually written flash block, third line function pointer to the malloc'd region, fourth line executing from the defined plain array.
The disassembly from arm-none-eabi-objdump for the calc function confirms that the machine code does not differ:
00080148 <_Z4calcv>:
"bhi .lbla \n\t"
".lblb: \n\t"
"movs r0,r5 \n\t"
"bx lr \n\t"
::
);
80148: 2121 movs r1, #33 ; 0x21
8014a: b532 push {r1, r4, r5, lr}
8014c: f000 f803 bl 80156 <.in>
80150: e8bd 4032 ldmia.w sp!, {r1, r4, r5, lr}
80154: 4770 bx lr
00080156 <.in>:
80156: 2501 movs r5, #1
80158: 3901 subs r1, #1
8015a: 2902 cmp r1, #2
8015c: f0c0 800b bcc.w 80176 <.lblb>
80160: 2501 movs r5, #1
00080162 <.lbla>:
80162: b522 push {r1, r5, lr}
80164: f7ff fff7 bl 80156 <.in>
80168: e8bd 4022 ldmia.w sp!, {r1, r5, lr}
8016c: 182d adds r5, r5, r0
8016e: 3902 subs r1, #2
80170: 2901 cmp r1, #1
80172: f63f aff6 bhi.w 80162 <.lbla>
00080176 <.lblb>:
80176: 0028 movs r0, r5
80178: 4770 bx lr
}
8017a: 4770 bx lr
I'm at the end of my wits. What could be going on here?