Не уверен, сколько из вас действительно превысило размер ответа при переполнении стека. Я сделал это снова здесь, так что этот второй ответ является демонстрацией того, что может повлиять на результаты производительности, которые может видеть OP, и примерами возможного тестирования этих
STM32F103C8 синей таблетки.
полный исходный код:
fla sh .ld
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
fla sh .s
.cpu cortex-m0
.thumb
.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.thumb_func
reset:
bl notmain
b hang
.thumb_func
hang: b .
.align
.thumb_func
.globl PUT32
PUT32:
str r1,[r0]
bx lr
.thumb_func
.globl GET32
GET32:
ldr r0,[r0]
bx lr
.thumb_func
.globl dummy
dummy:
bx lr
test.s
.cpu cortex-m0
.thumb
.word 0,0,0
.word 0,0,0,0
.thumb_func
.globl TEST
TEST:
bx lr
notmain. c
//PA9 TX
//PA10 RX
void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
void dummy ( unsigned int );
#define USART1_BASE 0x40013800
#define USART1_SR (USART1_BASE+0x00)
#define USART1_DR (USART1_BASE+0x04)
#define USART1_BRR (USART1_BASE+0x08)
#define USART1_CR1 (USART1_BASE+0x0C)
#define USART1_CR2 (USART1_BASE+0x10)
#define USART1_CR3 (USART1_BASE+0x14)
//#define USART1_GTPR (USART1_BASE+0x18)
#define GPIOA_BASE 0x40010800
#define GPIOA_CRH (GPIOA_BASE+0x04)
#define RCC_BASE 0x40021000
#define RCC_APB2ENR (RCC_BASE+0x18)
#define STK_CSR 0xE000E010
#define STK_RVR 0xE000E014
#define STK_CVR 0xE000E018
#define STK_MASK 0x00FFFFFF
static void uart_init ( void )
{
//assuming 8MHz clock, 115200 8N1
unsigned int ra;
ra=GET32(RCC_APB2ENR);
ra|=1<<2; //GPIOA
ra|=1<<14; //USART1
PUT32(RCC_APB2ENR,ra);
//pa9 TX alternate function output push-pull
//pa10 RX configure as input floating
ra=GET32(GPIOA_CRH);
ra&=~(0xFF0);
ra|=0x490;
PUT32(GPIOA_CRH,ra);
PUT32(USART1_CR1,0x2000);
PUT32(USART1_CR2,0x0000);
PUT32(USART1_CR3,0x0000);
//8000000/16 = 500000
//500000/115200 = 4.34
//4 and 5/16 = 4.3125
//4.3125 * 16 * 115200 = 7948800
PUT32(USART1_BRR,0x0045);
PUT32(USART1_CR1,0x200C);
}
static void uart_putc ( unsigned int c )
{
while(1)
{
if(GET32(USART1_SR)&0x80) break;
}
PUT32(USART1_DR,c);
}
static void hexstrings ( unsigned int d )
{
//unsigned int ra;
unsigned int rb;
unsigned int rc;
rb=32;
while(1)
{
rb-=4;
rc=(d>>rb)&0xF;
if(rc>9) rc+=0x37; else rc+=0x30;
uart_putc(rc);
if(rb==0) break;
}
uart_putc(0x20);
}
static void hexstring ( unsigned int d )
{
hexstrings(d);
uart_putc(0x0D);
uart_putc(0x0A);
}
void soft_delay(void) {
for (volatile unsigned int i=0; i<2000000; ++i) { }
}
int notmain ( void )
{
PUT32(STK_CSR,4);
PUT32(STK_RVR,0x00FFFFFF);
PUT32(STK_CVR,0x00000000);
PUT32(STK_CSR,5);
uart_init();
hexstring(0x12345678);
hexstring(GET32(0xE000E018));
hexstring(GET32(0xE000E018));
return(0);
}
build
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m3 flash.s -o flash.o
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m3 test.s -o test.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -mthumb -mcpu=cortex-m0 -march=armv6-m -c notmain.c -o notmain.thumb.o
arm-none-eabi-ld -o notmain.thumb.elf -T flash.ld flash.o test.o notmain.thumb.o
arm-none-eabi-objdump -D notmain.thumb.elf > notmain.thumb.list
arm-none-eabi-objcopy notmain.thumb.elf notmain.thumb.bin -O binary
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -mthumb -mcpu=cortex-m3 -march=armv7-m -c notmain.c -o notmain.thumb2.o
arm-none-eabi-ld -o notmain.thumb2.elf -T flash.ld flash.o test.o notmain.thumb2.o
arm-none-eabi-objdump -D notmain.thumb2.elf > notmain.thumb2.list
arm-none-eabi-objcopy notmain.thumb2.elf notmain.thumb2.bin -O binary
Вывод UART, как показано
12345678
00FFE445
00FFC698
Если я возьму ваш код, сделайте его короче, не весь день.
void soft_delay(void) {
for (volatile unsigned int i=0; i<0x2000; ++i) { }
}
arm-none-eabi-gcc -c -O0 -mthumb -mcpu=cortex-m0 hello.c -o hello.o
да я знаю, что это м3
arm-none-eabi-gcc --version
arm-none-eabi-gcc (GCC) 5.4.0
дает
00000000 <soft_delay>:
0: b580 push {r7, lr}
2: b082 sub sp, #8
4: af00 add r7, sp, #0
6: 2300 movs r3, #0
8: 607b str r3, [r7, #4]
a: e002 b.n 12 <soft_delay+0x12>
c: 687b ldr r3, [r7, #4]
e: 3301 adds r3, #1
10: 607b str r3, [r7, #4]
12: 687b ldr r3, [r7, #4]
14: 4a03 ldr r2, [pc, #12] ; (24 <soft_delay+0x24>)
16: 4293 cmp r3, r2
18: d9f8 bls.n c <soft_delay+0xc>
1a: 46c0 nop ; (mov r8, r8)
1c: 46bd mov sp, r7
1e: b002 add sp, #8
20: bd80 pop {r7, pc}
22: 46c0 nop ; (mov r8, r8)
24: 00001fff
сначала проверить инфраструктуру тестирования
.cpu cortex-m0
.thumb
.align 8
.word 0,0
.thumb_func
.globl TEST
TEST:
push {r4,r5,r6,lr}
mov r4,r0
mov r5,r1
ldr r6,[r4]
inner:
bl soft_delay
sub r5,#1
bne inner
ldr r3,[r4]
sub r0,r6,r3
pop {r4,r5,r6,pc}
.align 8
soft_delay:
bx lr
в окне openocd te lnet
reset halt
flash write_image erase notmain.thumb.elf
reset
дает
12345678
00001B59
7001 тактов, если предположить, что джойстик совпадает с процессором, то есть 7001 тактовый генератор, 4 инструкции за л oop.
Шаг назад примечание Я выровнял некоторые вещи
08000108 <TEST>:
8000108: b570 push {r4, r5, r6, lr}
800010a: 1c04 adds r4, r0, #0
800010c: 1c0d adds r5, r1, #0
800010e: 6826 ldr r6, [r4, #0]
08000110 <inner>:
8000110: f000 f876 bl 8000200 <soft_delay>
8000114: 3d01 subs r5, #1
8000116: d1fb bne.n 8000110 <inner>
8000118: 6823 ldr r3, [r4, #0]
800011a: 1af0 subs r0, r6, r3
800011c: bd70 pop {r4, r5, r6, pc}
08000200 <soft_delay>:
8000200: 4770 bx lr
оба цикла хорошо выровнены.
Теперь, если я сделаю это:
0800010a <TEST>:
800010a: b570 push {r4, r5, r6, lr}
800010c: 1c04 adds r4, r0, #0
800010e: 1c0d adds r5, r1, #0
8000110: 6826 ldr r6, [r4, #0]
08000112 <inner>:
8000112: f000 f875 bl 8000200 <soft_delay>
8000116: 3d01 subs r5, #1
8000118: d1fb bne.n 8000112 <inner>
800011a: 6823 ldr r3, [r4, #0]
800011c: 1af0 subs r0, r6, r3
800011e: bd70 pop {r4, r5, r6, pc}
просто меняется выравнивание кода, который должен тестировать тестируемый код, теперь я получаю:
00001F40
8000 тиков, чтобы сделать это l oop 1000 раз при этом вызове с тестируемой функцией кода, которая все еще работает выровненный
08000200 <soft_delay>:
8000200: 4770 bx lr
.align 8, как правило, не используйте .align с числом на GNU, его поведение не распространяется на цели. .balign лучше. В любом случае, я использовал его, потому что выровняли выровненный TEST, но внутреннее - это то, что я хотел выровнять, поэтому я добавил два слова, чтобы выровнять его.
.align 8
.word 0,0
nop
.thumb_func
.globl TEST
TEST:
push {r4,r5,r6,lr}
mov r4,r0
mov r5,r1
ldr r6,[r4]
inner:
bl soft_delay
sub r5,#1
bne inner
ldr r3,[r4]
sub r0,r6,r3
pop {r4,r5,r6,pc}
Небольшой обзор кода, чтобы убедиться, что я не сделал здесь можно ошибиться.
r0 - регистр текущего значения синдиката; r1 - число циклов, которые я хочу запустить тестируемый код
Соглашение о вызовах допускает замыкание r0-r3 так, Мне нужно переместить r0 и r1 в энергонезависимые регистры (в соответствии с соглашением о вызовах).
Я хочу указать время выполнения команды до l oop и инструкции после.
поэтому мне нужны два регистра для r0 и r1 и регистр для хранения времени начала, так что r4, r5, r6 и это хорошо вписывается, чтобы в стек помещалось четное количество регистров. Необходимо сохранить lr, чтобы мы могли вернуться.
Теперь мы можем безопасно вызывать soft_delay в l oop, вычитать число, ветвь, если не равно внутреннему, после того, как счетчик закончен, считайте таймер в r3. из выходных данных выше это обратный счетчик, так что вычесть конец из начала, технически так как это 24-битный счетчик, я должен и с 0x00FFFFFF правильно сделать это вычитание, но так как это не будет переворачиваться, я могу предположить эту операцию. результат / возвращаемое значение заносится в r0, выдает все, что включает выдвижение p c для возврата к вызывающей функции C, которая выводит значение r0.
Я думаю, что тестовый код хорош.
чтение регистра CPUID
411FC231
, что означает r1p1, в то время как TRM, который я использую, написан для r2p1, вы должны быть очень осторожны, чтобы использовать правильный документ, но также иногда использовать текущий документ или все промежуточные, если таковые имеются, чтобы увидеть, что изменилось.
Интерфейс памяти ICode
Выборки инструкций из пространства памяти кода от 0x00000000 до 0x1FFFFFFF выполняются по 32-битной шине AHB-Lite. Отладчик не может получить доступ к этому интерфейсу. Все выборки по всему миру. Количество команд, извлекаемых для каждого слова, зависит от выполняемого кода и выравнивания кода в памяти.
Иногда в ARM TRM вы видите информацию выборки вверху рядом с функциями процессора, это говорит мне, что Я хотел знать.
08000112 <inner>:
8000112: f000 f875 bl 8000200 <soft_delay>
8000116: 3d01 subs r5, #1
8000118: d1fb bne.n 8000112 <inner>
это требует выборки в 110, 114 и 118.
08000110 <inner>:
8000110: f000 f876 bl 8000200 <soft_delay>
8000114: 3d01 subs r5, #1
8000116: d1fb bne.n 8000110 <inner>
это выборка в 110 и 114, но не одна в 118, так что дополнительная выборка может быть нашими добавленными часами. m3 был первым общедоступным, и в нем было много функций, которые исчезли, и появились похожие. некоторые из меньших ядер получают по-разному, и вы не видите эту проблему выравнивания. с более крупными ядрами, такими как полноразмерные, они иногда получают 4 или 8 инструкций за раз, и вам нужно еще больше изменить свое выравнивание, чтобы попасть на границу, но вы можете попасть на границу, и так как это 2 или 4 такта плюс служебная нагрузка шины для дополнительного получить, вы можете увидеть их.
Если я поставлю два nops
nop
nop
.thumb_func
.globl TEST
TEST:
дает
08000114 <inner>:
8000114: f000 f874 bl 8000200 <soft_delay>
8000118: 3d01 subs r5, #1
800011a: d1fb bne.n 8000114 <inner>
800011c: 6823 ldr r3, [r4, #0]
800011e: 1af0 subs r0, r6, r3
8000120: bd70 pop {r4, r5, r6, pc}
дает
00001B59
так что это хорошо, мы вернулись к этому числу, могли бы попробовать еще несколько для подтверждения, но похоже, что выравнивание чувствительно к нашему внешнему тесту l oop, что плохо, но мы можем справиться с этим, не меняйте его, это не повлияет на тест. Если я не заботился о выравнивании и имел что-то вроде этого:
void soft_delay(void) {
for (volatile unsigned int i=0; i<0x2000; ++i) { }
}
int notmain ( void )
{
unsigned int ra;
unsigned int beg;
unsigned int end;
PUT32(STK_CSR,4);
PUT32(STK_RVR,0x00FFFFFF);
PUT32(STK_CVR,0x00000000);
PUT32(STK_CSR,5);
uart_init();
hexstring(0x12345678);
beg=GET32(STK_CVR);
for(ra=0;ra<1000;ra++)
{
soft_delay();
}
end=GET32(STK_CVR);
hexstring((beg-end)&0x00FFFFFF);
return(0);
}
тогда, когда я играл с опциями оптимизации, и я также играл с использованием разных компиляторов, любые изменения в программе / двоичном коде перед тестом l oop переместит / может переместить тест oop, изменив его производительность, в моем простом примере это была разница в производительности на 14%, что огромно, если вы проводите тесты производительности. позволить компилятору позаботиться обо всем этом без того, чтобы мы не контролировали все, что находится перед тестируемой функцией, и могли запутаться в тестируемой функции, как написано выше, компилятор может предпочесть встроить функцию, а не вызывать ее, что делает еще более интересным Ситуация как тест l oop, хотя, вероятно, не так чиста, как моя, конечно, не если не оптимизирована, но теперь тестируемый код является динамическим c при изменении параметров или выравниваний.
Я очень рад, что вам довелось использовать это ядро / чип ...
Если я перенастрою внутреннюю часть и теперь связываюсь с этим
.align 8
nop
soft_delay:
bx lr
08000202 <soft_delay>:
8000202: 4770 bx lr
, это один инструкция, которая взята в 0x200 из того, что мы прочитали и, кажется, можем сказать. не ожидал, что это что-то изменит, и это не
00001B59
, но теперь, когда мы знаем то, что мы знаем, мы можем использовать наш опыт, чтобы связываться с этим тривиальным примером. Не интересно вообще.
.align 8
nop
soft_delay:
nop
bx lr
дает
00001F41
, как и ожидалось. и мы можем получить еще больше удовольствия:
.align 8
.word 0,0
nop
.thumb_func
.globl TEST
TEST:
вместе дает
08000112 <inner>:
8000112: f000 f876 bl 8000202 <soft_delay>
8000116: 3d01 subs r5, #1
8000118: d1fb bne.n 8000112 <inner>
08000202 <soft_delay>:
8000202: 46c0 nop ; (mov r8, r8)
8000204: 4770 bx lr
не удивительно, если вы знаете, что делаете:
00002328
9000 часов, Разница в производительности 29%. мы в буквальном смысле говорим о 5 (технически 6) инструкциях, одинаковом точном машинном коде, и при простом изменении выравнивания производительность может быть разной на 29%, компилятор и опции не имеют к этому никакого отношения, но даже не попали туда.
Как мы можем ожидать какой-либо оценки производительности программы, используя код несколько раз в методе oop? Мы не можем, если не знаем, что делаем, не понимаем архитектуру и т. Д. c.
Теперь, как это должно быть очевидно, и, читая документацию, я использую внутренние часы 8 МГц, все происходит из поэтому время нахождения курсора не будет меняться, как, например, в случае с драмом. Биты LATENCY в регистре FLASH_ACR должны были по умолчанию установить нулевое состояние ожидания для 0
Без путаницы с часами и простого добавления состояния ожидания путем изменения FLASH_ACR регистрируется в 0x31.
000032C6
12998 по сравнению с 9000, я не ожидал, что он обязательно удвоится, и это не так.
Хм, для удовольствия сделайте PUT16, используя strh, и
.thumb_func
.globl HOP
HOP:
bx r2
и
PUT16(0x2000010a,0xb570); // 800010a: b570 push {r4, r5, r6, lr}
PUT16(0x2000010c,0x1c04); // 800010c: 1c04 adds r4, r0, #0
PUT16(0x2000010e,0x1c0d); // 800010e: 1c0d adds r5, r1, #0
PUT16(0x20000110,0x6826); // 8000110: 6826 ldr r6, [r4, #0]
PUT16(0x20000112,0xf000); // 8000112: f000 f876 bl 8000202 <soft_delay>
PUT16(0x20000114,0xf876); // 8000112: f000 f876 bl 8000202 <soft_delay>
PUT16(0x20000116,0x3d01); // 8000116: 3d01 subs r5, #1
PUT16(0x20000118,0xd1fb); // 8000118: d1fb bne.n 8000112 <inner>
PUT16(0x2000011a,0x6823); // 800011a: 6823 ldr r3, [r4, #0]
PUT16(0x2000011c,0x1af0); // 800011c: 1af0 subs r0, r6, r3
PUT16(0x2000011e,0xbd70); // 800011e: bd70 pop {r4, r5, r6, pc}
PUT16(0x20000202,0x46c0); // 8000202: 46c0 nop ; (mov r8, r8)
PUT16(0x20000204,0x4770); // 8000204: 4770 bx lr
hexstring(HOP(STK_CVR,1000,0x2000010B));
дают 0000464B
, и это совсем не ожидалось. но в основном это 18000
После этого укладывает баранов в постель
PUT16(0x20000108,0xb570); // 800010a: b570 push {r4, r5, r6, lr}
PUT16(0x2000010a,0x1c04); // 800010c: 1c04 adds r4, r0, #0
PUT16(0x2000010c,0x1c0d); // 800010e: 1c0d adds r5, r1, #0
PUT16(0x2000010e,0x6826); // 8000110: 6826 ldr r6, [r4, #0]
PUT16(0x20000110,0xf000); // 8000112: f000 f876 bl 8000202 <soft_delay>
PUT16(0x20000112,0xf876); // 8000112: f000 f876 bl 8000202 <soft_delay>
PUT16(0x20000114,0x3d01); // 8000116: 3d01 subs r5, #1
PUT16(0x20000116,0xd1fb); // 8000118: d1fb bne.n 8000112 <inner>
PUT16(0x20000118,0x6823); // 800011a: 6823 ldr r3, [r4, #0]
PUT16(0x2000011a,0x1af0); // 800011c: 1af0 subs r0, r6, r3
PUT16(0x2000011c,0xbd70); // 800011e: bd70 pop {r4, r5, r6, pc}
PUT16(0x20000200,0x46c0); // 8000202: 46c0 nop ; (mov r8, r8)
PUT16(0x20000200,0x4770); // 8000204: 4770 bx lr
hexstring(HOP(STK_CVR,1000,0x20000109));
00002EDE
Машинный код не изменился, потому что я переместил оба назад на 2, поэтому относительный адрес между ними был одинаковым. Обратите внимание, что bl - это две отдельные инструкции, а не одна 32-битная. Вы не можете увидеть это в новых документах, которые вам нужны, чтобы go вернуться к исходному / раннему ARM ARM, где это объясняется. И легко проводить эксперименты, в которых вы разделяете две инструкции и помещаете другие элементы между ними, и они работают просто отлично, потому что это две отдельные инструкции.
На этом этапе читатель должен быть в состоянии выполнить тест из 2 команд l oop, рассчитать его и кардинально изменить производительность выполнения этих двух инструкций на этой платформе, используя один и тот же точный машинный код.
Итак, давайте попробуем изменчивый l oop что вы написали.
.align 8
soft_delay:
push {r7, lr}
sub sp, #8
add r7, sp, #0
mov r3, #0
str r3, [r7, #4]
b L12
Lc:
ldr r3, [r7, #4]
add r3, #1
str r3, [r7, #4]
L12:
ldr r3, [r7, #4]
ldr r2, L24
cmp r3, r2
bls Lc
nop
mov sp, r7
add sp, #8
pop {r7, pc}
nop
.align
L24: .word 0x1FFF
это, я считаю, неоптимизированная версия -O0. начиная с одного теста l oop
hexstring(TEST(STK_CVR,1));
опыт, время, которое мы видим, переполнит наш 24-битный счетчик, и результаты будут очень странными или приведут к ложным выводам.
0001801F
98 000, быстрая проверка на безопасность:
.align
L24: .word 0x1F
0000019F
неплохо, что в 256 раз быстрее.
так что у нас есть немного места для маневра в наш тест l oop, но не слишком много, попытка 10
hexstring(TEST(STK_CVR,10));
000F012D
98334 тиков на л oop.
изменение выравнивания
08000202 <soft_delay>:
8000202: b580 push {r7, lr}
8000204: b082 sub sp, #8
дала тот же результат
000F012D
не неслыханно, вы можете проверить различия, если хотите подсчитать через каждый цикл выборки проверки команд и т. Д. c.
если бы я сделал тест:
soft_delay:
nop
nop
bx lr
это два цикла выборки, независимо от того, какое выравнивание или если я оставил его bx lr без nops, как мы видели, просто имея нечетное количество инструкций в тесте, тогда выравнивание не повлияет на результаты при выборках вдоль, но обратите внимание, что из того, что мы знаем сейчас, был какой-то другой код в р rogram перенес внешнюю синхронизацию / тест l oop, который, возможно, изменил производительность, и результаты могут показать разницу между двумя тестами, которые были чисто временным кодом, а не тестируемым кодом (читай Майкла Абра sh).
Cortex-m3 основан на архитектуре armv7-m. Если я изменю компилятор с -mcpu = cortex-m0 (пока что все совместимы с cortex-m) на -mcpu = cortex-m3 (не все совместимые с cortex-m сломаются на половине из них), он производит немного меньше кода.
.align 8
soft_delay:
push {r7}
sub sp, #12
add r7, sp, #0
movs r3, #0
str r3, [r7, #4]
b L12
Lc:
ldr r3, [r7, #4]
add r3, #1
str r3, [r7, #4]
L12:
ldr r3, [r7, #4]
/*14: f5b3 5f00 cmp.w r3, #8192 ; 0x2000*/
//cmp.w r3, #8192
.word 0x5f00f5b3
bcc Lc
nop
add r7, #12
mov sp, r7
pop {r7}
bx lr
000C80FB 81945 тиков для тестируемого кода.
Я ненавижу унифицированный синтаксис, это была огромная ошибка, поэтому я возился в унаследованном режиме. таким образом, вещь слова в середине.
Как часть написания этого, я как бы испортил свою систему, чтобы что-то продемонстрировать. Я собирал ag cc 5.4.0, но переписал мой 9.2.0, поэтому пришлось пересобрать оба.
2.95 была версия, которую я начал использовать с arm и не поддерживал thumb g cc 3.xx был первым. И либо g cc 4.xx, либо g cc 5.xx создавали «более медленный» код для некоторых из моих проектов, на работе мы в настоящее время переходим с ubuntu 16.04 на 18.04 для наших систем сборки, которые, если вы используете apt- у нас есть кросс-компилятор для arm, который перемещает вас с 5.xx на 7.xx, и он делает большие двоичные файлы для того же исходного кода, и там, где у нас не хватает памяти, он выталкивает нас за пределы того, что доступно, поэтому нам нужно либо удалить некоторый код ( проще всего сделать печатные сообщения короче, вырезать текст) или придерживаться старого компилятора, создав собственный или удачно получив старый. 19.10 больше не предлагает версию 5.xx.
Таким образом, теперь оба они построены.
18: d3f8 bcc.n c <soft_delay+0xc>
1a: bf00 nop
1c: bf00 nop
1e: 370c adds r7, #12
эти nops после b cc сбивают меня с толку ...
18: d3f8 bcc.n c <soft_delay+0xc>
1a: bf00 nop
1c: 370c adds r7, #12
g cc 5.4.0 ставит один, g cc 9.2.0 ставит два nops, ARM не имеет функции MIPS для теневого ветвления (MIPS в настоящее время тоже).
000C80FB gcc 5.4.0
000C8105 gcc 9.2.0
Я вызываю функцию 10 раз, nop находится за пределами тестируемого кода l oop, поэтому имеет меньший эффект.
Оптимизирован все варианты cortex-m (на сегодняшний день) с помощью g cc 9.2 .0
soft_delay:
mov r3, #0
mov r2, #128
sub sp, #8
str r3, [sp, #4]
ldr r3, [sp, #4]
lsl r2, r2, #6
cmp r3, r2
bcs L1c
L10:
ldr r3, [sp, #4]
add r3, #1
str r3, [sp, #4]
ldr r3, [sp, #4]
cmp r3, r2
bcc L10
L1c:
add sp, #8
bx lr
(также понимаю, что не все говорят g cc 9.2. 0 сборок дают один и тот же код, когда вы собираете компилятор, у вас есть опции, и эти опции могут влиять на вывод, делая разные сборки 9.2.0, возможно, приводящие к разным результатам)
000C80B5
g cc 9.2.0 build для cortex-m3:
soft_delay:
mov r3, #0
sub sp, #8
str r3, [sp, #4]
ldr r3, [sp, #4]
/*8: f5b3 5f00 cmp.w r3, #8192 ; 0x2000*/
.word 0x5F00F5B3
bcs L1c
Le:
ldr r3, [sp, #4]
add r3, #1
str r3, [sp, #4]
ldr r3, [sp, #4]
/*16: f5b3 5f00 cmp.w r3, #8192 ; 0x2000*/
.word 0x5F00F5B3
bcc Le
L1c:
add sp, #8
bx lr
000C80A1
вот в шуме. несмотря на встроенный код имеет различия. они просто не выиграли в сравнении 0x2000 в меньшем количестве инструкций. и обратите внимание, что если вы измените это значение 0x2000 на какое-то другое число, то это просто не заставит l oop намного дольше изменять сгенерированный код для подобных архитектур.
Как мне нравится делать эти отсчитанные задержки Циклы должны использовать функцию вне домена компиляции
extern void dummy ( unsigned int );
void soft_delay(void) {
for (unsigned int i=0; i<0x2000; ++i) { dummy(i); }
}
soft_delay:
push {r4, r5, r6, lr}
mov r5, #128
mov r4, #0
lsl r5, r5, #6
L8:
mov r0, r4
add r4, #1
bl dummy
cmp r4, r5
bne L8
pop {r4, r5, r6, pc}
функция, которая есть, вам не нужна дополнительная информация о том, что volatile у вас есть вызов, и очевидно, что есть также дополнительные затраты из-за вызова, но не
000B40C9
или даже лучше:
soft_delay:
sub r0,#1
bne soft_delay
bx lr
Мне бы пришлось изменить код, обернутый вокруг тестируемого кода, чтобы эта функция работала.
Другой обратите внимание, указав c к этим целям, но также к чему-то, с чем вы имеете дело
unsigned int more_fun ( unsigned int, unsigned int );
unsigned int fun ( unsigned int a, unsigned int b )
{
return(more_fun(a,b)+a+(b<<2));
}
00000000 <fun>:
0: b570 push {r4, r5, r6, lr}
2: 000c movs r4, r1
4: 0005 movs r5, r0
6: f7ff fffe bl 0 <more_fun>
a: 00a4 lsls r4, r4, #2
c: 1964 adds r4, r4, r5
e: 1820 adds r0, r4, r0
10: bd70 pop {r4, r5, r6, pc}
12: 46c0 nop ; (mov r8, r8)
вопрос, повторенный здесь в SO на периодической основе. почему он нажимает на r6, он не использует r6.
Компилятор работает с использованием того, что я называю, и раньше назывался соглашением о вызовах, теперь они используют термины ABI, EABI, в любом случае это одно и то же набор правил, которым следует компилятор для конкретной цели. Arm добавил правило для выравнивания стека на границе 64-битного адреса вместо 32, что заставило дополнительный элемент сохранять выравнивание стека, то, какой регистр там используется, может варьироваться. Если вы используете более старую версию g cc против более новой, это может / повлияет на производительность вашего кода.