Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: ARM Ассемблер
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Tarkus
Читал heyrick.co.uk, автор - апологет принципа "код - произведение искусства" smile.gif
Например, в одну инструкцию:

BIC R0, R0, R0, ASR#31 ; IF R0 < 0 THEN R0 = 0

Или еще лучше, таблица из "N" переходов с помощью всего двух инструкций:

.swihandler
CMP R11, #((endofjumptable - startofjumptable) / 4)
ADDCC PC, PC, R11, LSL #2
B error
.startofjumptable
B swi0
B swi1
; etc...
.endofjumptable

Честно говоря, не понял принципа работы второго примера. Кто-нибудь может прояснить?
_Pasha
Код
.swihandler

; На вход R11 - индекс в таблице переходов 0..макс. допустимый индекс


  CMP   R11, #((endofjumptable - startofjumptable) / 4);  проверили чтоб R11 < (макс.доп индекс + 1)
  ADDCC PC, PC, R11, LSL #2; и выполнили переход
  B     error; иначе перешли на обработку ошибки "недопустимый входной параметр"
.startofjumptable
B      swi0
B    swi1
; etc...
.endofjumptable

Где-то так. Сорри, форматтер кода - полный боян, такшта несколько нечитабельно
scifi
Цитата(Tarkus @ Mar 5 2009, 13:21) *
CMP R11, #((endofjumptable - startofjumptable) / 4)
ADDCC PC, PC, R11, LSL #2
B error
.startofjumptable
B swi0
B swi1
; etc...
.endofjumptable

Честно говоря, не понял принципа работы второго примера. Кто-нибудь может прояснить?

Неужели лень заглянуть в список инструкций ARM? Сравнение для того, чтобы R11 было меньше или равно размера таблицы. Если действительно меньше или равно, то прыгнуть на адрес PC+(4*R11), где PC указывает на "B swi0". В противном случае выполняется "B error".

Цитата(Tarkus @ Mar 5 2009, 13:21) *
Читал heyrick.co.uk, автор - апологет принципа "код - произведение искусства" smile.gif

Этот принцип для досужей потехи хорош, а для профессиональной деятельности крайне вреден. Если так ковыряться в инструкциях, то времени на работу не останется :-)
KRS
да в этих примерах ничего удивительного нет!
а таблицу переходов таким образом любой нромальный компилятор строит!
Так что если хотите посмотреть на особености ARM ассемблера, возьмите разные компиляторы:
IAR, GCC, RVCT (только свежие) поставьте максимальную оптимизацию по времени, потом по скорости и смотрите листинг увидите много хитрых приемов.
Современный ARM компилер трудно обогнать используя ассемблер, особенно при работе с константами - сидеть и вычислять чего куда сдвинуть и счем проксорить... (особенно если вы используете текстовое имя константы, а компилер то знает ее значение...)
В общем, очень мало место осталось где стоит применять ASM ( но знать его все равно полезно)
_Pasha
Цитата(KRS @ Mar 5 2009, 13:59) *
Современный ARM компилер трудно обогнать используя ассемблер,


Это уж точно плюс пицот! Дык не все верят в очевидное sad.gif

Цитата
особенно при работе с константами - сидеть и вычислять чего куда сдвинуть и счем проксорить... (особенно если вы используете текстовое имя константы, а компилер то знает ее значение...)



А вот это, простите, не понял. Писанины больше получается - это факт, и выражений всяких поболее надо.
KRS
Цитата(_Pasha @ Mar 5 2009, 14:04) *
А вот это, простите, не понял. Писанины больше получается - это факт, и выражений всяких поболее надо.

Например идет простая инициализация переферии.
т.е. надо по куче константных адресов записать константы.
Причем обычно константы представлены не числами, а символически (например не 0x12300 a ADC_REG_CTRL).
Так вот в силу специфики команд ARM не все контсатны можно оптимально загрузить в регистр, но зная их числовые представления и то что до этого было в регистрах можно получать их разными арифметическими и логическими действиями и компилер с этим прекрасно справляется, а вот руками упаришься считать. (типа что бы получиь константу DAC_INIT_1 = 0x48C04 можно (0x1233>>2)+4 а это одна команда для ARM)
Tarkus
Цитата(_Pasha @ Mar 5 2009, 12:49) *
.swihandler
ADDCC PC, PC, R11, LSL #2; и выполнили переход
Где-то так. Сорри, форматтер кода - полный боян, такшта несколько нечитабельно

Спасибо, действительно все прозрачно smile.gif
_Pasha
Цитата(KRS @ Mar 5 2009, 14:19) *
Так вот в силу специфики команд ARM не все контсатны можно оптимально загрузить в регистр

Да это понятно, обычно Peripheral-Specific макросы в помощь. Правда, я не особо широкого кругозора - дальше GNU AS не интересовался макросредствами для армов, но не сомневаюсь, что у всяких там кейлов с этим все в порядке

Цитата(Tarkus @ Mar 5 2009, 14:23) *
Спасибо

Пожалуйста
kons
Цитата
Этот принцип для досужей потехи хорош, а для профессиональной деятельности крайне вреден. Если так ковыряться в инструкциях, то времени на работу не останется :-)

Немного не так. Если неохота ковыряться в инструкциях, надо пользоваться C. Например, обсуждаемая здесь инициализация периферии - уж точно не задача для asm. На таких задачах (как и на большинстве прочих), как верно отметил KRS,
Цитата
Современный ARM компилер трудно обогнать используя ассемблер

А вот написать какой-нибудь внутренний короткий цикл на asm (фильтр какой-нибудь или демодулятор) - это часто очень даже имеет смысл. И тут уж все средства хороши. Но (по опыту) максимальный эффект в asm-модуле достигается не жонглированияем битами, а оптимизацией доступа к памяти - использованием вместо кучи LDR/STR одной LDMIA/STMIA. Компилятор так не умеет (ну, кроме входа/выхода в процедуру есс-но). Для ядра ARM7TDMI на задачах типа свертки результат подобного подхода очень даже радует.
ar__systems
Цитата(kons @ Mar 8 2009, 02:00) *
Немного не так. Если неохота ковыряться в инструкциях, надо пользоваться C. Например, обсуждаемая здесь инициализация периферии - уж точно не задача для asm. На таких задачах (как и на большинстве прочих), как верно отметил KRS,
А вот написать какой-нибудь внутренний короткий цикл на asm (фильтр какой-нибудь или демодулятор) - это часто очень даже имеет смысл. И тут уж все средства хороши. Но (по опыту) максимальный эффект в asm-модуле достигается не жонглированияем битами, а оптимизацией доступа к памяти - использованием вместо кучи LDR/STR одной LDMIA/STMIA. Компилятор так не умеет (ну, кроме входа/выхода в процедуру есс-но). Для ядра ARM7TDMI на задачах типа свертки результат подобного подхода очень даже радует.

Грамотный ответ. Оптимизировать инициализацию переферии - оч глупо. Она выполняется один раз.
Оптимизировать цикл (на асм, по сравнению с С) думаю можно практически всегда, особенно если он написан на С человеком, который ассемблера в жизни не видел.
zltigo
Цитата(ar__systems @ Mar 8 2009, 16:53) *
....если он написан на С человеком, который ассемблера C в жизни не видел.

smile.gif так правильно.
scifi
Цитата(ar__systems @ Mar 8 2009, 16:53) *
Оптимизировать цикл (на асм, по сравнению с С) думаю можно практически всегда

Зачем оптимизировать всегда? Я хотел донести такую мысль: не нужно оптимизировать, если и без оптимизации хорошо работает. Я думаю, бессмысленная оптимизация уносит слишком большое количество человеко-лет, которые могли бы быть потрачены более эффективно.
sergeeff
Почти в каждой современной книге по С/С++ написано о вреде преждевременной оптимизации и выстраивается последовательность:
1. Программирование алгоритма.
2. Отладка алгоритма и его комплексное тестирование.
3. Изучение быстродействия отдельных функций и модулей.
4. Оптимизация (при необходимости) узких мест.
_Pasha
Цитата(sergeeff @ Mar 9 2009, 15:15) *
1. Программирование алгоритма.
2. Отладка алгоритма и его комплексное тестирование.
3. Изучение быстродействия отдельных функций и модулей.
4. Оптимизация (при необходимости) узких мест.

Это квадратные советы. Они не учитывают задачи портирования, коих большинство, когда об алгоритме уже известно почти все, надо чтобы оно красиво влезло и не мешало жить наращиваемой функциональности. В таких случаях я лично начинаю с проверки и оптимизации именно узких мест.
KRS
Цитата(kons @ Mar 8 2009, 10:00) *
Но (по опыту) максимальный эффект в asm-модуле достигается не жонглированияем битами, а оптимизацией доступа к памяти - использованием вместо кучи LDR/STR одной LDMIA/STMIA. Компилятор так не умеет (ну, кроме входа/выхода в процедуру есс-но).

Иногда очень даже умеет! К тому же использование команд LDM STM не уменьшает количество досупов к памяти данных, а только за счет уменьшения инструкций уменьшается количество досупов к программе, а это не всегда приводит к увеличению быстродействия. И команды LDM STM увеличивают время реакции на прерывание, в RVCT есть даже опция на ограничение числа регистров в этой команде.
А вы можете привести конкретный пример цикла где действительно стоит использовать асм?
MALLOY2
расчет CRC32, ЦФ, свертка, FFT, практически все матиметические циклы будут на асме работать быстрее! Вопрос вы будите писать их на асм или возьмете мощнее процессор ?
kons
to KRS.
Вы правы, кол-во обращений к данным не уменьшает, но (для ARM7TDMI, у Cortex и особенно ARM9 разница не столь существенна) LDR - 3 такта/слово, LDMIA - 2+1*кол-во слов. Т.е. при загрузке, скажем, 8 регистров LDRx8 = 24 такта, LDMIA - 10 тактов. Да, забыл сказать - это при выполнении из RAM. При выполнении из флэша на AT91SAM7 добавьте к каждому LDR еще по такту - флеш медленнее, однако.

Простенький пример - цикл вычисления FIR. Данные берутся из циркулярного буфера (DL_PTR, DL_BASE, DL_SIZE). Обрабатываются 4 отвода за раз. Напишите на c, скомпилируйте, посчитайте такты, сравните. А стоит или не стоит это делать на ASM - зависит от потребной частоты вычисления и длины фильтра. В моем случае (Fд=48 кГц, L=20 отводов, и кроме этого еще куча дел) - стоило однозначно. Была бы Fд 8 кГц - не стал бы заморачиваться. Что же касается реакции на прерывания - на то DMA есть.

FIR_DELAY_LOOP MACRO
LOCAL FDL_LP
FDL_LP: LDMDB DL_PTR!,{R_SMP3-R_SMP0} ;8
CMP DL_PTR,DL_BASE
ADDLS DL_PTR,DL_PTR,DL_SIZE
LDMIA SUFF_PTR!,{R_SUFF0-R_SUFF3} ;6
MLA R_ACC,R_SUFF0,R_SMP0,R_ACC ;16
MLA R_ACC,R_SUFF1,R_SMP1,R_ACC
MLA R_ACC,R_SUFF2,R_SMP2,R_ACC
MLA R_ACC,R_SUFF3,R_SMP3,R_ACC
SUBS SUFF_CNT,SUFF_CNT,#4 ;4
BNE FDL_LP
ENDM

P.S. А как тут код нормальный вставлять? Что с цитатой, что без - все одно каша...

to Pasha:
Цитата
Это квадратные советы. Они не учитывают задачи портирования, коих большинство, когда об алгоритме уже известно почти все, надо чтобы оно красиво влезло и не мешало жить наращиваемой функциональности. В таких случаях я лично начинаю с проверки и оптимизации именно узких мест.

+500. Особенно для DSP, и особенно на этапе выбора процессора.
ar__systems
Цитата(MALLOY2 @ Mar 9 2009, 07:40) *
расчет CRC32, ЦФ, свертка, FFT, практически все матиметические циклы будут на асме работать быстрее! Вопрос вы будите писать их на асм или возьмете мощнее процессор ?


Мощнее процессор решение тупое но часто приемлимое. Иногда, к счастью нет -- иначе бы Visual Basic и в embedded programming пробрался бы smile.gif Когда стоит задача добиться минимального потребления тока оптимизация на асм. очень помогает, ибо каждая лишняя инструкця цикла это сколько-то лишних nA. (При условии что программа в основном спит, и изредка включается чтобы обработать данные).
aaarrr
Цитата(kons @ Mar 9 2009, 15:55) *
P.S. А как тут код нормальный вставлять? Что с цитатой, что без - все одно каша...

Теги [сode] и [сodebox].
KRS
Цитата(kons @ Mar 9 2009, 15:55) *
Простенький пример - цикл вычисления FIR. Данные берутся из циркулярного буфера (DL_PTR, DL_BASE, DL_SIZE)...

конечно компилер так не сможет скомпилировать, потому что у вас куча допущений: например длина циклического буфера кратна 4 словам! и соотвественно STUFF_CNT тоже кратно 4. Если вы сумеете объяснить это компилятору...
По поводу раскрутки циклов IAR это делать умеет простой пример:
Код
unsigned test(unsigned *dl, unsigned *suff)
{
    unsigned r;
    unsigned cnt;
    r = 0;
    cnt = 16;
    do {
        r += (*(dl++)) * (*(suff++));
    }while(--cnt);
    return r;
}

получаем листинг
Код
      1          unsigned test(unsigned *dl, unsigned *suff)
      2          {
   \                     test:
   \   00000000   00402DE9           PUSH     {LR}
   \   00000004   04D04DE2           SUB      SP,SP,#+4
      3              unsigned r;
      4              unsigned cnt;
      5              r = 0;
   \   00000008   0020A0E3           MOV      R2,#+0
      6              cnt = 16;
   \   0000000C   0430A0E3           MOV      R3,#+4
      7              do {
      8                  r += (*(dl++)) * (*(suff++));
   \                     ??test_0:
   \   00000010   04C090E4           LDR      R12,[R0], #+4
   \   00000014   04E091E4           LDR      LR,[R1], #+4
   \   00000018   9E2C22E0           MLA      R2,LR,R12,R2
      9              }while(--cnt);
   \   0000001C   04C090E4           LDR      R12,[R0], #+4
   \   00000020   04E091E4           LDR      LR,[R1], #+4
   \   00000024   9E2C22E0           MLA      R2,LR,R12,R2
   \   00000028   04C090E4           LDR      R12,[R0], #+4
   \   0000002C   04E091E4           LDR      LR,[R1], #+4
   \   00000030   9E2C22E0           MLA      R2,LR,R12,R2
   \   00000034   04C090E4           LDR      R12,[R0], #+4
   \   00000038   04E091E4           LDR      LR,[R1], #+4
   \   0000003C   9E2C22E0           MLA      R2,LR,R12,R2
   \   00000040   013053E2           SUBS     R3,R3,#+1
   \   00000044   F1FFFF1A           BNE      ??test_0
     10              return r;
   \   00000048   0200A0E1           MOV      R0,R2
   \   0000004C   04D08DE2           ADD      SP,SP,#+4       ;; stack cleaning
   \   00000050   0080BDE8           POP      {PC}            ;; return
     11          }

да LDMIA конечно не задействован, но раскрутка на лицо.

А если взять такую функцию (раскрутить цикл вручную)
Код
unsigned test(unsigned *dl, unsigned *suff, unsigned cnt)
{
    unsigned r;
    r = 0;
    do {
        unsigned d1,d2,d3,d4;
        unsigned s1,s2,s3,s4;

        d1 =  *(dl++);
        d2 =  *(dl++);
        d3 =  *(dl++);
        d4 =  *(dl++);

        s1 = *(suff++);
        s2 = *(suff++);
        s3 = *(suff++);
        s4 = *(suff++);

        r += s1*d1 + s2*d2 + s3*d3 + s4*d4;
    }while(cnt-=4);
    return r;
}

и взять компилер помощнее ( RVCT 4.0)
то получим такой asm
Код
; generated by ARM C/C++ Compiler, RVCT4.0 [Build 471]
; commandline armcc [-S -O3 -Otime test.c]
        ARM
        REQUIRE8
        PRESERVE8

        AREA ||.text||, CODE, READONLY, ALIGN=2

test PROC
        PUSH     {r4-r10}
        MOV      r3,#0
|L1.8|
        LDR      r12,[r0],#4
        LDM      r1!,{r7,r8,r10}
        LDM      r0!,{r4-r6}
        MUL      r12,r7,r12
        MUL      r5,r10,r5
        MLA      r12,r8,r4,r12
        LDR      r9,[r1],#4
        SUBS     r2,r2,#4
        MLA      r4,r9,r6,r5
        ADD      r12,r12,r4
        ADD      r3,r3,r12
        BNE      |L1.8|
        POP      {r4-r10}
        MOV      r0,r3
        BX       lr
        ENDP

так что умеют компиляторы и LDMIA использовать smile.gif

я конечно не утверждаю что надо без asm обходится! действительно многие вещи приходится писать на нем, но часто в силу того что на С не объснить компилеру некоторые ньюансы. Но не стоит недооценивать современные компиляторы.
kons
Да, хорошая штука RVCT. Мой IAR так не умеет - проверял. Однако такой код на c выходит длиннее ассемблерного. А для того, чтобы написать его и заценить понятливость компилятора, все равно надо знать asm...
KRS
Цитата(kons @ Mar 9 2009, 18:36) *
А для того, чтобы написать его и заценить понятливость компилятора, все равно надо знать asm...

C этим не поспоришь!
Еще при этом можно узнать некоторые особенности компилятора, например если заменить строчку
Код
r += s1*d1 + s2*d2 + s3*d3 + s4*d4;

на
Код
        r += s1*d1;
        r += s2*d2;
        r += s3*d3;
        r += s4*d4;

то получим в RVCT слегка другой листинг
Код
        LDR      r12,[r0],#4
        LDM      r1!,{r7,r8,r10}
        MLA      r3,r7,r12,r3
        LDM      r0!,{r4-r6}
        MLA      r3,r8,r4,r3
        MLA      r3,r10,r5,r3
        LDR      r9,[r1],#4
        SUBS     r2,r2,#4
        MLA      r3,r9,r6,r3
        BNE      |L1.8|

тут видно что для RVCT подобные циклы лучше разворачивать на 3 операции ( 6 регистров он может выделить), а не на 4
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.