Пробовал так [R0] и так @R0, не катит. На сайте кейла по запросам addressing modes и indirect addressing ничего толкового.
Косвенная адресация в STM32F4
- Сообщения: 671
- Зарегистрирован: Вт июн 29, 2010 12:31:49
Подскажите, пожалуйста, как включить сабж в кейле?
Пробовал так [R0] и так @R0, не катит. На сайте кейла по запросам addressing modes и indirect addressing ничего толкового.
Пробовал так [R0] и так @R0, не катит. На сайте кейла по запросам addressing modes и indirect addressing ничего толкового.
- Реклама
- Сообщения: 1163
- Зарегистрирован: Ср янв 05, 2011 16:25:15
наберите в поисковике arm assembler instruction и произойдет чудо.....
- Сообщения: 671
- Зарегистрирован: Вт июн 29, 2010 12:31:49
Спасибо.
Не сразу, но нашёл такое: http://www.keil.com/support/man/docs/ar ... HGCADB.htm.
Попробую, учитывая особенности архитектуры (F4 умеет только thumb2, как я понял).
Не сразу, но нашёл такое: http://www.keil.com/support/man/docs/ar ... HGCADB.htm.
Попробую, учитывая особенности архитектуры (F4 умеет только thumb2, как я понял).
- Сообщения: 671
- Зарегистрирован: Вт июн 29, 2010 12:31:49
Похоже, не все команды умеют работать с косвенной адресацией.
Но LDR вполне работает. Пока на этом остановлюсь.
Но LDR вполне работает. Пока на этом остановлюсь.
- Сообщения: 671
- Зарегистрирован: Вт июн 29, 2010 12:31:49
Чтоб не создавать ещё тему, спрошу здесь.
Нижеприведённый код описывает перемножение с накоплением массива с замерами сигнала по массивам синусов и косинусов, затем вычисляется значение амплитуды.
Получается что-то вроде БПФ с фильтрацией. Впрочем, теория не важна. Запинка случилась там, где в коде вопросительные знаки. Дело в том, что по инструкции SMLAL заполняются два регистра ядра - старшее и младшее слово.
По VMOV окончательные суммы передаются в FPU. Далее нужно взять квадраты этих чисел, сложить и взять из них корень.
Однако команды FPU работают только с одиночными регистрами, а суммы, с которыми требуется проводить операции, лежат в двух каждая. Есть какой-нибудь способ обойти это в асме, или придётся вылезать в С? Судя по осциллографу, получается приличная экономия: 40 мкс в чистом асме против 100 для того же алгоритма в С (хотя кто знает, может с костылями то же получится?).
Нижеприведённый код описывает перемножение с накоплением массива с замерами сигнала по массивам синусов и косинусов, затем вычисляется значение амплитуды.
Код: Выделить всё
Sqrt PROC
label0
LDR R4, [R2,R3]
LDR R6, [R0,R3]
LDR R7, [R1,R3]
SMLAL R8, R9, R4, R6
SMLAL R10, R11, R4, R7
CBZ R3, label1
SUB R3, #4
B label0
label1
VMOV S0, S1, R8, R9
VMOV S2, S3, R10, R11
???
VMUL.F32 S0,S0,S0
VMUL.F32 S1,S1,S1
VADD.F32 S0,S0,S1
VSQRT.F32 S0,S0
BX LR
ENDPПо VMOV окончательные суммы передаются в FPU. Далее нужно взять квадраты этих чисел, сложить и взять из них корень.
Однако команды FPU работают только с одиночными регистрами, а суммы, с которыми требуется проводить операции, лежат в двух каждая. Есть какой-нибудь способ обойти это в асме, или придётся вылезать в С? Судя по осциллографу, получается приличная экономия: 40 мкс в чистом асме против 100 для того же алгоритма в С (хотя кто знает, может с костылями то же получится?).
- Реклама
- Сообщения: 535
- Зарегистрирован: Вт авг 28, 2012 22:21:33
Мне не совсем понятен код. Сначала ты выполняешь целочисленное умножение 32х32=64. А потом кладешь целочисленный же результат в регистр FPU. Если я не ошибаюсь, то VMOV просто копирует данные, она не переведет целочисленное значение в формат IEEE754. Это твоего вопроса не касается, просто непонятка.
Ну а что касается вопроса - я вижу несколько вариантов.
Первое - оставить все как есть и использовать программную конвертацию int64 во float. Не знаю, есть ли такая функция в компиляторе или нет. Естесственно о скорости можно забыть. Хотя... Надо эксперементировать.
Второе - если твои операнды в сумме (я про длину) меньше 64 бит, то можно использовать не 32х32=64, а 32х32=32 умножение. Результат, опять же, придется переводить во float. Но здесь уже легче, ибо компилятор стопроцентно поддерживает такое преобразование, а самое главное, что есть аппаратная инструкция конвертации VCVT (почитай про нее в даташите).
Ну а что касается вопроса - я вижу несколько вариантов.
Первое - оставить все как есть и использовать программную конвертацию int64 во float. Не знаю, есть ли такая функция в компиляторе или нет. Естесственно о скорости можно забыть. Хотя... Надо эксперементировать.
Второе - если твои операнды в сумме (я про длину) меньше 64 бит, то можно использовать не 32х32=64, а 32х32=32 умножение. Результат, опять же, придется переводить во float. Но здесь уже легче, ибо компилятор стопроцентно поддерживает такое преобразование, а самое главное, что есть аппаратная инструкция конвертации VCVT (почитай про нее в даташите).
- Сообщения: 1163
- Зарегистрирован: Ср янв 05, 2011 16:25:15
... сам то понял, что написал? FPU работает через стек. Чтобы избежать ненужных вопросов посоветую старую книжку - "Архитектура и программирование арифметического сопроцессора" Григорьев В.Л. Хотя там описан i8087, но принцип тот же.Однако команды FPU работают только с одиночными регистрами, а суммы, с которыми требуется проводить операции, лежат в двух каждая.
- Сообщения: 535
- Зарегистрирован: Вт авг 28, 2012 22:21:33
Ну так-то он все правильно сказал. В Cortex-M4 есть регистры R0...Rn, которые могут быть операндами целочисленных инструкций, и регистры S0...Sn, которые могут быть операндами FPU инструкций. Причем здесь стек мне не совсем понятно.ChipKiller писал(а):сам то понял, что написал? FPU работает через стек.
- Сообщения: 1163
- Зарегистрирован: Ср янв 05, 2011 16:25:15
menzoda писал(а):Причем здесь стек мне не совсем понятно.
... речь шла о VPUSH,VPOP ....может не совсем понятно выразился - пардон.TripleKill писал(а):.....Однако команды FPU работают только с одиночными регистрами, а суммы, с которыми требуется проводить операции, лежат в двух каждая. Есть какой-нибудь способ обойти это в асме, или придётся вылезать в С?
- Сообщения: 671
- Зарегистрирован: Вт июн 29, 2010 12:31:49
Всем спасибо.
Действительно, VMOV просто копирует данные, в итоге получается фигня.
Процедуру разделил вылазкой в С, т.е. из кода вызывается сначала MAC, потом операции с корнем. Зато передаётся всё как нужно.
Позже оказалось, что суперская точность тут не нужна (код по работе, проталкиваю STM32 как более современный), и результат MAC можно сдвинуть вправо, оставив только старшие разряды в одном 32-битном регистре. SMLAL остаётся на месте, просто потом производятся манипуляции с двумя регистрами результата.
Итого: +4 мкс из-за лишних телодвижений, и всё равно STM уделывает XC166) У обоих время преобразования в районе 80-90 мкс (на два канала), при том, что стм обрабатывает массив из 100 измерений по каналу, а с166 - из 39.
Есть вопрос по косметике. Вот код в С:
Как видно, сначала данные из регистров вытаскиваются в массив int, потом преобразуются и попадают в массив float. Казалось, почему бы передачу типа не сделать в вызове процедуры Ampl, да и вообще, обойтись без массивов? Однако компилятор ни то, ни другое не проглатывает (w1 не изменяется вообще), а вот как приведено - работает. В чём тут может быть дело? К слову, если сделать не массив f1, а две переменные int, то даже из регистров данные не вытаскиваются.
Действительно, VMOV просто копирует данные, в итоге получается фигня.
Процедуру разделил вылазкой в С, т.е. из кода вызывается сначала MAC, потом операции с корнем. Зато передаётся всё как нужно.
Позже оказалось, что суперская точность тут не нужна (код по работе, проталкиваю STM32 как более современный), и результат MAC можно сдвинуть вправо, оставив только старшие разряды в одном 32-битном регистре. SMLAL остаётся на месте, просто потом производятся манипуляции с двумя регистрами результата.
Итого: +4 мкс из-за лишних телодвижений, и всё равно STM уделывает XC166) У обоих время преобразования в районе 80-90 мкс (на два канала), при том, что стм обрабатывает массив из 100 измерений по каналу, а с166 - из 39.
Есть вопрос по косметике. Вот код в С:
Код: Выделить всё
STM_EVAL_LEDOn(LED3);
MacOp(POS1_isintable, POS1_icostable, samples, (POS1_FFTLENGTH-1)*4);
f1[0] = R8;
f1[1] = R10;
f2[0]=(float)f1[0];
f2[1]=(float)f1[1];
w1=(float)Ampl(f2[0], f2[1]);
STM_EVAL_LEDOff(LED3);- Сообщения: 535
- Зарегистрирован: Вт авг 28, 2012 22:21:33
К сожалению, у меня нет под рукой компилятора, чтобы посмотреть в чем проблема. Но какая-то она странная, ведь так не должно быть. Посмотри что за ассемблер он генерирует из твоей Си-вставки. И может быть вообще ее убрать? Я же нашел ассемблерную инструкцию конвертации во float, чего ее не использовать?
Если код действительно только такой
Необходимо сделать h файл в который поместить декларацию.
float Ampl(float, float);
Включить этот h айл в c файл.
После этого преобразования типов должны выполняться компилятором неявно.
И вообще выносить декларации в h файлы - это правильный стиль. Когда нибудь это спасет Вас от поиска непонятного поведения в течение нескольких дней.
То компилятор должен давать предупреждение - отсутствие предварительной декларации функции Ampl. Без этого компилятор не знает, какие типы аргументов у функции и предполагает, что Вы подсовываете правильные типы.ChipKiller писал(а): w1=(float)Ampl(f2[0], f2[1]);
Необходимо сделать h файл в который поместить декларацию.
float Ampl(float, float);
Включить этот h айл в c файл.
После этого преобразования типов должны выполняться компилятором неявно.
И вообще выносить декларации в h файлы - это правильный стиль. Когда нибудь это спасет Вас от поиска непонятного поведения в течение нескольких дней.
- Сообщения: 671
- Зарегистрирован: Вт июн 29, 2010 12:31:49
- Сообщения: 535
- Зарегистрирован: Вт авг 28, 2012 22:21:33
Это точно. Я как-то раз два дня подряд мучался. Оказывается не включил H-файл. Главное компилятор, зараза, не сказал ни слова, обычно то они предупреждают о таком, дескать "Function declared implicitly".Galizin писал(а):И вообще выносить декларации в h файлы - это правильный стиль. Когда нибудь это спасет Вас от поиска непонятного поведения в течение нескольких дней.
- Сообщения: 671
- Зарегистрирован: Вт июн 29, 2010 12:31:49
Всем спасибо за помощь, всё разместилось в одной функции и выглядит так:
Однако компилятор как-то странно себя ведёт)
Вот код, которым я замеряю время выполнения преобразования.
Переменная w1 в дебаге не меняет значения, с которым была инициализирована. Я было подумал, что компилятор понимает, что раз я кручусь в бесконечном цикле, и далее нигде переменную не вызываю, то и обрабатывать её не стоит. Видоизменил код, чтоб цикл выполнялся конечное число раз, а w1 дальше вызывалась. Но ничего не изменилось.
Если же я вызываю функцию до цикла, в самом main, то w1 изменяется. Что тут не так? Кроме того, изменяя код функции, при работе цикла я вижу изменение по времени, т.е. код-то вроде как работает. Почему тогда w1 не меняется? Или это дебагер гадит и не показывает?
P.S. Проверил диодом:
Похоже, всё-таки дебагер. Досадно. Ну хоть переменная меняется на самом деле.
Код: Выделить всё
ExtractAmp PROC
MOV R8, #0
MOV R9, #0
MOV R10, #0
MOV R11, #0
label0
LDR R4, [R2,R3]
LDR R6, [R0,R3]
LDR R7, [R1,R3]
SMLAL R8, R9, R4, R6
SMLAL R10, R11, R4, R7
CBZ R3, label1
SUB R3, #4
B label0
label1
LSR R8, #4
LSL R9, #28
ADD R8, R8, R9
LSR R10, #4
LSL R11, #28
ADD R10, R10, R11
VMOV S0, R8
VCVT.F32.S32 S0, S0
VMOV S1, R10
VCVT.F32.S32 S1, S1
VMUL.F32 S0,S0,S0
VMUL.F32 S1,S1,S1
VADD.F32 S0,S0,S1
VSQRT.F32 S0,S0
BX LR
ENDPВот код, которым я замеряю время выполнения преобразования.
Код: Выделить всё
while(1) {
STM_EVAL_LEDOn(LED3);
w1=ExtractAmp(POS1_isintable, POS1_icostable, samples, (POS1_FFTLENGTH-1)*4);
STM_EVAL_LEDOff(LED3);
}Если же я вызываю функцию до цикла, в самом main, то w1 изменяется. Что тут не так? Кроме того, изменяя код функции, при работе цикла я вижу изменение по времени, т.е. код-то вроде как работает. Почему тогда w1 не меняется? Или это дебагер гадит и не показывает?
P.S. Проверил диодом:
Код: Выделить всё
if (w1>(float)100500) STM_EVAL_LEDOn(LED5);


