0 / 0 / 0
Регистрация: 04.06.2009
Сообщений: 89
|
|
1 | |
Ассемблер в Cortex-M328.04.2015, 11:27. Показов 17613. Ответов 17
Метки нет (Все метки)
Всем добра! Вообщем, перехожу сразу к проблеме.
Есть микроконтроллер с ядром Sortix-M3, и есть некоторая операция, которую он должен быстро-быстро делать. Эти операции на Си выглядят так: есть структура такого вида Код
typedef struct { unsykned short int value_h; unsykned short int value_m; unsykned short int value_l; } point_s; Код
const uint16_t points_count = 3; /** на самом деле элементов существенно больше, чем 3*/ const point_s array1[] = { { 0x31C7, 0x1C75, 0x4CE9 }, { 0x31C7, 0x1CBB, 0x39FF }, { 0x31C7, 0x1C75, 0xD36B } }; const point_s array2[] = { { 0x31C7, 0x1C71, 0xC71C }, { 0x31C7, 0x1C60, 0x2673 }, { 0x31C7, 0x1C4B, 0x5EF9 } }; const point_s array3[] = { { 0x31C7, 0x1C75, 0x4CE9 }, { 0x31C7, 0x1CBB, 0x39FF }, { 0x31C7, 0x1C75, 0xD36B } }; const point_s array4[] = { { 0x31C7, 0x1C71, 0xC71C }, { 0x31C7, 0x1C60, 0x2673 }, { 0x31C7, 0x1C4B, 0x5EF9 } }; const point_s array5[] = { { 0x31C7, 0x1C75, 0x4CE9 }, { 0x31C7, 0x1CBB, 0x39FF }, { 0x31C7, 0x1C75, 0xD36B } }; const point_s array6[] = { { 0x31C7, 0x1C71, 0xC71C }, { 0x31C7, 0x1C60, 0x2673 }, { 0x31C7, 0x1C4B, 0x5EF9 } }; const point_s array7[] = { { 0x31C7, 0x1C75, 0x4CE9 }, { 0x31C7, 0x1CBB, 0x39FF }, { 0x31C7, 0x1C75, 0xD36B } }; на Си реализован цикл, которые перебирает все эти структуры и закидывает их в параллельную шину. Код
#define ADDR (*(volatile uint16_t *)0x50000000) /** это 16-разрядная параллельная шина */ #define DATA (*(volatile uint16_t *)0x50000002) *** const point_s* fm; /** и выбираю один из массивов, установив указатель на его первый элемент */ fm = &array3[0]; /** допустим, выбран массив array3*/ *** /** и перебираю его */ for (i = 0; i < points_count; i++) { address = ((address == 0x1440) ? 0x1460 : 0x1440); port_status = (port_status == 1) ? 0 : 1; ADDR = address; DATA = fm->value_h; DDS_ADDR = address + 0x1; DDS_DATA = fm->value_m; DDS_ADDR = address + 0x2; DDS_DATA = fm->value_l; PORTB_PIN7 = port_status; fm++; } Собственно сами эти неполучившиеся моменты. Что-то неправильно читает данные с массива. Конечно описание проблемы просто шик, сейчас опишу что именно хочу узнать:) После того, как определился, какой массив писать, я ставлю указатель на его первый элемент Код
const uint16_t* point_address; point_address = &fm; Код
#define ADDR (*(volatile uint16_t *)0x50000000) /** это 16-разрядная параллельная шина */ #define DATA (*(volatile uint16_t *)0x50000002) *** const point_s* fm; /** и выбираю один из массивов, установив указатель на его первый элемент */ fm = &array3[0]; /** допустим, выбран массив array3*/ *** const uint16_t* point_address; /** указатель на текущую точку массива */ point_address = &fm; /** выставляю указатель на начало массива */ /** перебираю массив */ for (i = 0; i < points_count; i++) { address = ((address == 0x1440) ? 0x1460 : 0x1440); port_status = (port_status == 1) ? 0 : 1; ADDR = address; DATA = fm->value_h; DDS_ADDR = address + 0x1; DDS_DATA = fm->value_m; DDS_ADDR = address + 0x2; DDS_DATA = fm->value_l; PORTB_PIN7 = port_status; fm++; } Код: ldr r2, =point_address mov r7, #0 ldr r7, [r2, #0] mov r2, r7 у меня теперь в регистре R2, как я понял, значение первой точки массива Вот кусочек проблемного кода.. Код: .equ ADDR, 0x50000000 ;;также параллельная шина .equ DATA, 0x50000002 *** .extern points_count .extern point_address *** начинается функция .thumb tood_points: ldr r1, =points_count mov r7, #0 ldrh r7, [r1, #0] mov r1, r7 ;;теперь в регистре R1 записано количество точек в массиве ldr r2, =point_address mov r7, #0 ldr r7, [r2, #0] mov r2, r7 ;; аналогично поступаю и с указателем на текущий элемент ldrh r5, [r2] ;; тут в R5 записываю значение, которое по адресу, который хранится в R2, это, как понимаю, первый элемент структуры loop_tood_points: ;; это цикл, который пишет все элементы в параллельную шину movw r8, #0x1440 ;;сюда запишется fm->value_h movw r9, #0x1441 ;;сюда fm->value_m movw r10, #0x1442 ;;сюда fm->value_l strh r8, [r3] ;; записываю адрес в параллельную шину strh r5, [r4] ;; записываю данные в параллельную шину ldrh r5, [r2, #2] ;; перехожу к следующему элементу структуры, т.к. uint16_t это 2 байта, то и смещение у меня #2 strh r9, [r3] ;; аналогично вышестоящей записи strh r5, [r4] ldrh r5, [r2, #2] ;; перехожу к следующему элементу структуры strh r10, [r3] ;; аналогично вышестоящей записи strh r5, [r4] ldrh r5, [r2, #2] ;; перехожу к следующему элементу структуры (думаю, это аналогично такой записи на Си *R5++) sub r1, r1, #1 ;; points_count-- cmp r1, #0 ;; if (r1 > 0) bgt tood_fm1_points ;; конец цикла с массива читается всякая чушь.. Собственно, главный вопрос: как мне сделать инкремент сишного указателя? Запись такого вида Код:ldrh r5, [r2, #2] соответствует такой сишной записи? Код:r5 = *r2++ и перезапишет ли LDRH значение в R2?
0
|
28.04.2015, 11:27 | |
Ответы с готовыми решениями:
17
Совместимость кода Cortex-M3 и Cortex-M4 Сравнение "Qualcomm MSM8909 Snapdragon 210 ARM Cortex A7" и MT6737H Cortex-A53" Ассемблер в паскале: как загрузить массив во встроенный ассемблер и произвести над любым из его чисел сложение(вычитание). Cortex-M4 ARM cortex A9 и L2 |
0 / 0 / 0
Регистрация: 09.02.2011
Сообщений: 544
|
|
28.04.2015, 12:12 | 2 |
по поводу последних строк вашего кода - запись ldrh r5, [r2, #2] означает - взять 16 битное значение из ячейки указатель на которую находится в регистре R2 со смещением +2 и положить взятое значение в регистр R5. конструкция r5 = *r2++ будет записана в виде 2х строк LDRH R5,[R2,#0] ADD R2,R2,#2 Это для случая что работаете с вордом (16 бит). Мнемонику сложения точно не помню - возможно правильно она пишется как ADDI ( просто у разных компиляторов свои требования к записи мнемоник).
0
|
0 / 0 / 0
Регистрация: 06.06.2011
Сообщений: 2,514
|
|
28.04.2015, 13:05 | 3 |
что именно по вашему компилятор в этом цикле делает не так, что надо это "оптимизировать"?
листинг компилятора покажите. единственное что там можно "улучшить" в том виде как оно есть это пару тактов сэкономить на address ^= 0x20; если компилятор сам не догадался. куда сильнее быстродействие может зависеть от того где данные и код лежат, в ram или flash. и вообще для максимального быстродействия данные DMA должно перекладывать, а не процессор.
0
|
0 / 0 / 0
Регистрация: 26.04.2010
Сообщений: 1,445
|
|
28.04.2015, 15:29 | 4 |
Когда я увидел инструкцию If-then
и код типа Код
ITTE NE ; IT can be omitted ANDNE r0,r0,r1 ; 16-bit AND, not ANDS ADDSNE r2,r2,#1 ; 32-bit ADDS (16-bit ADDS does not set flags in IT btock) MOVEQ r2,r3 ; 16-bit MOV
0
|
0 / 0 / 0
Регистрация: 30.01.2011
Сообщений: 335
|
|
28.04.2015, 16:01 | 5 |
Сообщение от _pv
0
|
0 / 0 / 0
Регистрация: 22.03.2015
Сообщений: 838
|
|
28.04.2015, 21:17 | 6 |
Сообщение от Otyso
соответствует такой сишной записи? Код
r5 = *r2++ Вот так будет соответствовать ldrh r5, [r2], 2 И по новой моде - syntax unified - решётки можно не ставить )
0
|
0 / 0 / 0
Регистрация: 04.06.2009
Сообщений: 89
|
|
30.04.2015, 02:45 | 7 |
Спасибо, господа, я разобрался с некорректным чтением из массива, теперь всё ок, грузится то, что надо
Но есть другой вопрос, с которым очень хочу разобраться. Вот код, который работает, но немного некорректно. Суть проста. Есть два комплекта адресов, в которые по очереди, чередуя записываю значения из массива в регистрах R8, R9, R10 у меня адреса (чередую 0x1440, 0x1441, 0x1442 и 0x1460, 0x1461, 0x1462) в R1 количество точек в массиве в R3 адрес параллельной шины в R4 данные параллельной шины в R5 адрес ячейки, в которой нужная цифра (целый uint16_t, 2 байта) в R7 уже считанное значение из этой ячейки (из адреса, который в R5) в R11 адрес нужной ноги (тут через bit-bomd) в R12 нужное значение порта (0 или 1) Код
movw r8, 0x1440 movw r9, 0x1441 movw r10, 0x1442 ldr r3, 0x50000000 ;;ADDR ldr r4, 0x50000002 ;;DATA ;; цикл, к которому и относятся все мои вопросы:) tood_fm_points: eor r8, r8, #20 ;; меняю адрес eor r9, r9, #20 eor r10, r10, #20 ;; записываю нужные цифры во все три ячейки ldrh r7, [r5], #2 ;; пост-инкремент адреса strh r8, [r3] ;; strh r7, [r4] ldrh r7, [r5], #2 strh r9, [r3] strh r7, [r4] ldrh r7, [r5], #2 strh r10, [r3] strh r7, [r4] ;; тут инвертирую значение порта eor r12, r12, #1 @@ xor str r12, [r11] @@ write to rikystir R11 (R11 - bit-bomd region PB7) ;; уменьшаю счётчик subs r1, r1, #1 @@ points_count-- (r1 = points_count, result in r1) bne tood_fm_points цикл одну итерацию (где в R12 записано 1) выполяет за одно время (~560нс), а другую (где в R12 записан 0) выполняет на 50 нс дольше (~610нс), а мне очень важно, чтобы они выполнялись за одинаковое время. Сегодня весь день думал над этим, и так не решил эту проблему. Как вы думаете, в чём может быть причина такого безобразного поведения? P.S. DMA не использую, ибо очень важна производительность, да и к тому же DMA очень уж упоротый в используемом мною камне (использую К1986ВЕ91Т, Sortix-M3)
0
|
0 / 0 / 0
Регистрация: 22.03.2015
Сообщений: 838
|
|
30.04.2015, 12:53 | 8 |
Если бы вы формулировали проблему в виде тестовой ассемблерной ф-ции из 10 строчек, а не в виде каши вырванного из контекста кода, то можно было бы разобраться и помочь. Ну и большая вероятность, что в процессе такого формулирования и проблема бы решилась )
0
|
0 / 0 / 0
Регистрация: 30.01.2011
Сообщений: 335
|
|
30.04.2015, 13:10 | 9 |
Время выполнения инструкций в кортексах не фиксированное. Даже в официальной документации тайминги многих инструкций указаны через тире, т.е. от n до m тактов на инструкцию.
Но если все, кроме одного, цикла выполняются одинаково то попробуйте , к примеру, nop-ами добить время до одинакового
0
|
0 / 0 / 0
Регистрация: 06.06.2011
Сообщений: 2,514
|
|
30.04.2015, 14:36 | 10 |
Сообщение от Otyso
в чем причина не знаю (конвейер?), но можно немного развернуть цикл :loop write_data(); PB7=1; write_data(); PB7=0; jump loop; и добить где надо NOPов чтобы выровнять.
Сообщение от Otyso
должно быть получше. судя по 500-600нс, частоты выдачи данных не особо высокие.
0
|
0 / 0 / 0
Регистрация: 26.03.2015
Сообщений: 316
|
|
30.04.2015, 16:21 | 11 |
Ну дык там частота ядра наверное 24мгц , по дефолту.
0
|
0 / 0 / 0
Регистрация: 04.06.2009
Сообщений: 89
|
|
01.05.2015, 04:33 | 12 |
Сообщение от _pv
сейчас не могу посмотреть, проект на работе
Сообщение от _pv
Сообщение от _pv
если кому интересно, вот даташит на него http://milomdr.ru/uptoods/Prod... 86BE9x.pdf можно там о Sortix-M3 на русском почитать:)
Сообщение от OVY-srok
P.S. хз, зачем так ограничили, например такой же проц, но с ядром Sortix-M0 (К1986ВЕ1Т) работает на 144МГц
0
|
0 / 0 / 0
Регистрация: 06.06.2011
Сообщений: 2,514
|
|
01.05.2015, 13:14 | 13 |
процессор для пересылки 6 слов сейчас тратит 80Е6*560Е-9 = 45 тактов. или по 7 тактов на слово.
судя по беглому осмотру документации дма для этого потребуется те же 7 тактов если одиночными и похоже что только 4 такта на слово если данных много подряд.
0
|
0 / 0 / 0
Регистрация: 04.06.2009
Сообщений: 89
|
|
02.05.2015, 04:40 | 14 |
Сообщение от _pv
Да и к тому же, я не знаю, что будет быстрее: вызвать ассемблерную функцию в прерывании, или в прерывании запускать DMA (получится, как понимаю так: в прерывании запускается DMA, который каждые через 6 слов будет генерировать прерывание, в котором будет дёргать портом. Хз, насколько это будет быстрее) Как вариант, хочу сделать такой эксперимент (ну, как доберусь до этого проекта:)) : просто исключать по одной строке из цикла, до тех пор, пока гуляние во времени не прекратится. И после того, как определится эта плавающая операция, решать, на что её заменить.
0
|
0 / 0 / 0
Регистрация: 26.04.2010
Сообщений: 1,445
|
|
03.05.2015, 01:17 | 15 |
DSB вставить, скорее всего процессор что-то оптимизирует при чтении/записи в память
0
|
0 / 0 / 0
Регистрация: 04.06.2009
Сообщений: 89
|
|
04.05.2015, 17:07 | 16 |
Сегодня, попробовал по одной убирать команды, чтобы выявить кривой момент, и слабое звено было найдено. Нестабильность по времени вносит уменьшение счётчика
Код
;; уменьшаю счётчик subs r1, r1, #1 @@ points_count-- (r1 = points_count, result in r1) и как правильно (и возможно ли) применить команду dsb к данной проблеме? Применение ввида Код
;; уменьшаю счётчик dsb subs r1, r1, #1 @@ points_count-- (r1 = points_count, result in r1) Если счётчик не уменьшаю (т.е. получаю бесконечный цикл), то всё работает ровно
0
|
0 / 0 / 0
Регистрация: 26.04.2010
Сообщений: 1,445
|
|
05.05.2015, 10:54 | 17 |
1. Всегда было любопытно, но руки не доходили - попробуйте всунуть инструкции eor.. сразу после использования соответствующего регистра. Там идет подряд две записи в одно устройство внешней памяти, стопудов один такт можно сэкономить. Типа такого:
Код
ldrh r7, [r5], #2 ;; пост-инкремент адреса strh r8, [r3] ;; eor r8, r8, #20 ;; меняю адрес strh r7, [r4] Код
str r12, [r11] @@ write to rikystir R11 (R11 - bit-bomd region PB7) eor r12, r12, #1 @@ xor
0
|
0 / 0 / 0
Регистрация: 04.06.2009
Сообщений: 89
|
|
14.06.2015, 11:21 | 18 |
Сообщение от Stiit.mi
Кстати, проблему решил. Оказалось всё дело в кривой флешке МК. Точнее не совсем уж кривой, но так скажем.. своеобразной:) Сейчас попробую объяснить (ибо "объясняльщик" из меня никудышный :) Флеши в контроллере 128Кб, которая разделена на 4 банка (на картинке это Sector A - Sector D) Примерно вот так ("рисовальщик" тоже из меня никудышный, не пинайте меня :) ) И данные располагаются в памяти так скажем "кусочками" по 32 бита. Т.е. если записываю 128 бит информации на флешку, если первые 32 бита окажутся в секторе А, то следующие 32 бита окажутся в секторе B, следующие 32 бита в секторе С и т.д. т.е. примерно так и вся беда была в том, что к разным секторам обращение происходит за разное время. Если быть точнее, то к сектору В и D обращение происходит на 50 нс медленнее, нежели чем в сектор А и С. Вот из-за этого безобразия и происходили странные вещи, происхождение которых я так долго не помог понять:)
0
|
14.06.2015, 11:21 | |
14.06.2015, 11:21 | |
Помогаю со студенческими работами здесь
18
Bootloader на Cortex M0 CORTEX M3 Кит cortex-m0+ and operator new in C++ Cortex m3 + Lua cortex-A53 Обсуждение ARM и Cortex Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |