|
С и asm-вставки, Локальные переменные |
|
|
|
Apr 2 2012, 04:56
|
Местный
  
Группа: Участник
Сообщений: 298
Регистрация: 26-01-09
Из: Пермь
Пользователь №: 43 940

|
Перед тем как оптимизировать на асме, разберитесь сначала с оптимизацией ни СИ. Во-первых, при оптимизации на асме можно сделать очепятку, которую потом будете долго и упорно искать. Во-вторых, оптимизация на Си выполняется гораздо быстрее. В-третьих, при хорошо изученном поведении своего компилятора, отимизированный код на Си будет всего на 5...15% больше своего собрата на асме, зато скока сэкономленого времени?! И если только оптимизация на Си не помогает, то необходимо залезать в ассемблер. Но и при ассемблерной оптимизации есть куча подводных камней. Примеры: 1. Использование указателей вместо индексации массивов Код int8_t * val = &Values[0]; // в не тела цикла .................................. // в цикле: FsummRe[FLookignForFreq] += (int32_t) (*val) * cosVal; FsummIm[FLookignForFreq] += (int32_t) (*val) * sinVal; .................................. val++; Это сокращает код на одно вычисление адреса Values[CurVal]. FsummIm и FsummRe, FsinTable и FcosTable также можно привести к использованию указателей 2. Оптимизация циклов. Цикл i=N;do{.....}while(i--); в большинстве случаев более оптимизировано компилируется, чем цикл for(i = 0; i < N; i++){........}; 3. При целочисленной арифметике, деление заменяется на умножение и сдвиг. 4. Объявление наиболее используемых локальных переменных как register (не вовсех компиляторах) 5. и т.д. и т.п..........................
|
|
|
|
|
Apr 2 2012, 07:46
|
Частый гость
 
Группа: Участник
Сообщений: 140
Регистрация: 21-04-11
Пользователь №: 64 524

|
Цитата(alexeyv @ Apr 2 2012, 07:56)  Спасибо! Весьма полезные советы!!!
|
|
|
|
|
Apr 2 2012, 09:04
|
Местный
  
Группа: Участник
Сообщений: 298
Регистрация: 26-01-09
Из: Пермь
Пользователь №: 43 940

|
Цитата(XVR @ Apr 2 2012, 14:30)  На достаточном уровне оптимизации (-О3 достаточно) gcc это все сделает сам. (Хотя в конверсии массивов в указатели может и запутаться - у вас там весьма накрученно массивы индексируются) 1. Поверьте мне - GCC НЕ ВСЕ ЭТО сделает сам, в нем заложены только типичные алгоритмы, которые впрочем постоянно расширяются и периодически обновляются. 2. Мои высказывания относятся ко всем компиляторам, а не только к GCC. И если писать сразу отпимизировано, то не надо будет думать, что "я это делать не буду - это компилятор САМ прооптимизирует", а потом локти себе кусать. Это путь к деградации, если думать что за тебя все сделает компилятор/оптимизатор. 3. -О3 конечно хорошо, но когда идет отладка, то -О3 отключаешь, и как раз в этот момент не хватает - то оперативки, то флеша, то быстродействия......... (если конечно при проектировании не заложили запас, а все сделали в притык, как часто бывает)
|
|
|
|
|
Apr 2 2012, 09:19
|

Ambidexter
    
Группа: Свой
Сообщений: 1 589
Регистрация: 22-06-06
Из: Oxford, UK
Пользователь №: 18 282

|
Цитата(XVR @ Apr 2 2012, 07:30)  ... у вас там весьма накрученно массивы индексируются Да там индексы не нужны вообще. Например, здесь FsummRe[FreqNum] += Values[i]*cosVal; дважды по 256 раз вычисляется адрес по индексу FreqNum, но этот индекс - константа в подпрограмме. Надо бы поставить A=A+Values[i]*cosVal; в теле цикла, а после цикла сделать обратное присвоение FsummRe[FreqNum]=А
--------------------
Делай сразу хорошо, плохо само получится
|
|
|
|
|
Apr 6 2012, 08:46
|
Частый гость
 
Группа: Участник
Сообщений: 140
Регистрация: 21-04-11
Пользователь №: 64 524

|
Возник следующий вопрос - как в на асме прочитать байтик из памяти программ? Покопавшись в просторах интернета нашёл следующее: CODE ldi ZH, hi8(pm(F64_cos)) ldi ZL, lo8(pm(F64_cos)) lpm r18, Z
F64_cos - таблица значений косинуса. Но чёт это не работает. Код в AVR stidio 4 помнется выглядит примерно так: CODE ldi ZH, high(F64_cos*2) ldi ZL, low(F64_cos*2) lpm r18, Z
Т.е. в AVR stidio 4 нужно делать сдвиг в лево, что равно умножению на 2. Почитав доки на avr-gcc нашёл что макрос pm надо использовать если нужен доступ к памяти программ, но он равносилен деления на 2 или, что тоже самое, сдвигу на 1 в право. На этом моменте у меня сразу возникло не понимание принципа доступа к памяти программ на ассемблере в avr-gcc. Может кто в курсе как делается правильно? Вот весь код на асме, который написал: CODE #include "../Headers/asm_ftransform.h" #include <avr/io.h>
.extern FsummRe // == r9, r8, r7, r6 .extern FsummIM // == r13, r12, r11, r10 .extern CurVal // == r5 .extern Flags .extern FsinTable .extern FcosTable .extern F64_cos
.global DMA_CH0_vect DMA_CH0_vect: push r0 push r1 push ZL push ZH push r17 push r18 push r19
/* load Sin and Cos */ // calc offset ldi r17, 0x35 //mul r5, r17 // r1:r0 - offset
// sin addres ldi ZH, hi8(pm(F64_cos)) ldi ZL, lo8(pm(F64_cos)) //add r16, r0 //adc r17, r1 lpm r18, Z sts _SFR_IO_ADDR(USARTE0_DATA), r18
// DMA.CH0.CTRLB |= 48; // clear interrupt flags ldi r26, 0x30 sts _SFR_IO_ADDR(DMA_CH0_CTRLB), r26 // stop ADC clr r17 sts _SFR_IO_ADDR(ADCA_CTRLA), r17
ldi r17, 2 sts Flags, r17
pop r19 pop r18 pop r17 pop ZH pop ZL pop r1 pop r0
reti
Нашел в чём проблема, мой косяк, F64_cos была в ОЗУ. И собственно чтоб прочитать из памяти программ нужно: ldi ZH, hi8(F64_cos) ldi ZL, lo8(F64_cos) lpm r18, Z без всяких макросов pm(). И новый вопрос: я правильно понимаю, макрос pm() используется для вызова подпрограмм, например: ldi ZH, hi8(pm(s_func)) ldi ZL, lo8(pm(s_func)) call Z так?
|
|
|
|
|
Apr 9 2012, 03:56
|
Местный
  
Группа: Участник
Сообщений: 298
Регистрация: 26-01-09
Из: Пермь
Пользователь №: 43 940

|
1. pm не используется для вызова подпрограм. Для вызова подпрограмм используются команды CALL/ICALL/EICALL/RCALL. pm() - program memory, макрос просто умножает параметр на 2. Дело в том, что ОЗУ у AVR адресуется байтово, а flash-память словарно, и адреса всех объектов, лежащих во flash-памяти являются словарными. Поэтому что бы из словарного адреса получить байтовый (для доступа к любому байту flash-памяти), необходимо умножить на 2, или сдвинуть на 1 влево. Доступ к flash-памяти: Код ldi ZH, high(Table_1<<1) ; Initialize Z pointer ldi ZL, low(Table_1<<1) lpm r16, Z+ ; Load constant from program Low-adress + increment Z lpm r16, Z ; Load constant from program High-adress 2. call Z - нет такой команды, есть icall. И насколько я помню при загрузке ZH:ZL для команды icall, сдвигать адрес не надо. Косвенный вызов функции: Код ldi ZH, high(func) ; Initialize Z pointer ldi ZL, low(func) icall ; Load constant from program
Сообщение отредактировал alexeyv - Apr 9 2012, 04:11
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|