Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: лог некой шины, Как это реализовать более оптимально?
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Sirko
Необходимо снять лог некой шины, у которой частота в районе мегагерца.
Код простой
Код
u08 array[1000];
u08* pArr = &array[999];
while(pArr >= array)
    *pArr-- = PINA;
У меня получается приблизительно 18 тактов на цикл. Можно ли это улучшить?
galjoen
Цитата(Sirko @ Jun 29 2011, 18:26) *
У меня получается приблизительно 18 тактов на цикл

Странно. У меня, насколько я помню т.к. было очень давно, менее чем за 20 тактов не только байт в лог писался, но и CRC32 от него считалось. Тоже поток мегабайт в секунду шёл и 20 мГц процессор успевал.
kovigor
Цитата(Sirko @ Jun 29 2011, 17:26) *
У меня получается приблизительно 18 тактов на цикл. Можно ли это улучшить?


А есть у этой шины строб ? И какая у шины разрядность, и куда лог пишете ? Во внешнее ОЗУ ? Я бы попробовал все это на асме переписать. По идее, все должно получиться ...
Sirko
Цитата
менее чем за 20 тактов не только...
Возможно, у Вас размерность массива укладывалась в восемь бит. В любом случае, я далеко не виртуоз в в кодении, посему и прошу совета.

На счет стробов и прочего сказать ничего не могу, - для этого и нужно отсканить шину.
Под рукой есть только M162 в DIPе (что-б не паяться особо) и макетка с XMEGA128. Но для XMEGи придется мудрить с преобразованием напряжений на входах. Кстати вопрос - а не обидится ли XMEGA, если подать на ее входы пятивольтовые уровни через резисторы с пол килоома?

Цитата
Я бы попробовал все это на асме переписать

На асме я, к сожалению, не потяну, вернее, этот кусок вопросов особых не вызывает, а вот с остальной частью софта застряну на долго. Скомбинировать асм и си не умею. Да и правильно написанный сишный код (имеется ввиду конкретно цикл опроса входов) вряд ли получится оптимизировать на асме.
=GM=
Цитата(Sirko @ Jun 29 2011, 13:26) *
Можно ли это улучшить?

На асме можно улучшить до 3 тактов.
demiurg_spb
У xMega есть DMA...
Правда xMega живьём не видел и не знаю можно-ли настроить DMA на чтение из порта.
Sirko
Цитата
и не знаю
Аналогично.

Сам сегодня щупаю впервые. Все, что смог - это запустить от внешнего кварца и "хелло ворлд" на usart отправить.
kovigor
Цитата(Sirko @ Jun 29 2011, 20:49) *
Сам сегодня щупаю впервые. Все, что смог - это запустить от внешнего кварца и "хелло ворлд" на usart отправить.


Слишком мало данных о шине. Как минимум о разрядности и наличии строба вы знать обязаны.

По поводу асма. Скачайте компилятор CodeVision, он генерит очень понятный асм - файл. Найдите там нужную процедурку и вручную ее оптимизируйте, после чего этот асм-файл компильните. Только не забудьте в опциях включить оптимизацию по времени выполнения. И все ...
Sirko
Цитата
На асме можно улучшить до 3 тактов.
В три такта можно только опросить порт с инкрементом, а в случае с массивом необходимо контролировать его диапазон.

Цитата
Скачайте компилятор CodeVision
Скачал, поглядел. Среда кажется довольно навороченной. С ней самой еще нужно освоиться, откровенно говоря, лениво как-то ради одного, двух сэкономленных тактов да и то может быть.
Почему-то я склонен верить, что -
Цитата
Да и правильно написанный сишный код (имеется ввиду конкретно цикл опроса входов) вряд ли получится оптимизировать на асме.


Цитата
Как минимум о разрядности и наличии строба вы знать обязаны.
К сожалению.
На вскидку (сужу по топологии разводки): 8 бит данных, 4 бита адрес, 2строба, 1бит - что-то.
Возможно "что-то" и есть строб, а "строба" - выбор R/W.
Шина объединяет между собой три микрушки на трех разных платах, одна плата управляет двумя другими. Микрухи - специфические, либо плиски (что мало вероятно), либо заказные. Во всяком случае, даташитов на них нет.
Адрес, данные и "что-то" - на всех трех платах.
А стробы идут индивидуально, по одному на плату.

Но суть топика - это вопрос: как "Правильно" сделать еще правильнее?
Палыч
Цитата(Sirko @ Jun 29 2011, 18:26) *
У меня получается приблизительно 18 тактов на цикл. Можно ли это улучшить?
Цифра в 18 тактов показалась мне неоправданно большой. Решил перепроверить Ваши данные. Оттранслировал Вашу программу в IAR 5.51 и получил 9 тактов на цикл - очень даже приемлемый результат, который вряд ли удастся улучшить.
V_G
Цитата(demiurg_spb @ Jun 30 2011, 04:37) *
У xMega есть DMA...
Правда xMega живьём не видел и не знаю можно-ли настроить DMA на чтение из порта.

Можно.
DMA читает с заданного адреса источника (с включенным или выключенным автоинкрементом) и пишет в заданный адрес назначения (с включенным или выключенным автоинкрементом). Т.к. все порты имеют адрес в памяти, то чтение из порта через DMA идет без проблем.
alexeyv
Цитата
У меня получается приблизительно 18 тактов на цикл. Можно ли это улучшить?


Откомпилировал в AVRStudio5. Посмотрел листинг:

Код
in    r18, 0x19
st    -Z, r18
cp    r30, r24
cpc    r31, r25
brne    .-10


Цикл занимает 5 команд, да еще инициализация регистров - 4 команды. Вполне нормально. На асме можно убрать одну команду если только использовать размер массива не более 256
Sirko
Странновато как-то:
Попробовал на ноуте скомпилить.
Получилось
Код
IN    R24, 0x19
STD    Z+0, R24
SBIW    R30, 0x01
CP    R30, R28
CPC    R31, R29
BRNE    PC-0x05


Выполняется за девять тактов проца. Результат очень таки разнится с прошлым вариантом.
И студия и компилятор на обоих компах одинаковые, оптимизация -0S, MakeФайлы в обоих случаях студийные.

Ну да ладно, источник проблемы пойман, дальше разберусь. Кардинально. smile3009.gif

P.S. А в пятой студии свой компилятор?
Мой листинг немного другой.
Компилятор WinAVR-20100110.

=GM=
Цитата(Sirko @ Jun 29 2011, 19:34) *
В три такта можно только опросить порт с инкрементом, а в случае с массивом необходимо контролировать его диапазон

Ну, тут вы немного погорячились. Для 1000 отсчётов нужно развернуть цикл и повторить 1000 раз фрагмент "прочитать порт в регистр + сохранить регистр с автоинкрементом". Вот вам три такта и будет.
rx3apf
Цитата(alexeyv @ Jun 30 2011, 10:05) *
Откомпилировал в AVRStudio5. Посмотрел листинг:

Код
in    r18, 0x19
st    -Z, r18
cp    r30, r24
cpc    r31, r25
brne    .-10


Цикл занимает 5 команд, да еще инициализация регистров - 4 команды. Вполне нормально. На асме можно убрать одну команду если только использовать размер массива не более 256

Или кратно 256 и буфер на границе. 5 команд - но 7 тактов (или 6, если контроль на 1 байт). Однако вполне можно сократить до 5 тактов, причем с контролем на 16-битный адрес.
alexeyv
Цитата
А в пятой студии свой компилятор?

Так же как и в WInAVR, в AStudio использует GNU-toolchain. Про версии не помню, но файлы в каталогах \avr\bin\ совпадают

Цитата
Или кратно 256 и буфер на границе. 5 команд - но 7 тактов (или 6, если контроль на 1 байт). Однако вполне можно сократить до 5 тактов, причем с контролем на 16-битный адрес


Согласен:
Код
ldi    r31,0x04
ldi    r30,0x00
ldi    r25,0x02
ldi    r24,0x00
;cycle:
in    r18, 0x19
st    -Z, r18
cpc    r31, r25
brne    .-8

где ((r31-r25) * 256) - размер массива
Sirko
Цитата
Необходимо снять лог некой шины, у которой частота в районе мегагерца.

Наивный, однако wink.gif

После некоторых телодвижений - Mega162 была задвинута в сторонку.
Еще после некоторых телодвижениий эйфории от юзания XMega тоже поубавилось.

В конечном итоге, в ходе издевательств над собой и процом, оказалось, что Мега, погнанная до 48Мгц с четырьмя тактами на одно измерение, судя по всему, не отражает достоверно состояние шины т.к. есть одиночные показания некоторых бит. Стало быть, есть вероятность, что какие-нибудь данные могут проскочить мимо.
Что-ж, видать, не судьба.

Есть предположение, что DMA не сумеет опросить порты быстрее, чем есть на данный момент.
В любом случае, на данный момент я не смог найти, как прикрутить DMA к портам.

Исходник приложу - вдруг какие-нибудь мысли появятся.
CODE
typedef unsigned char u08;
typedef unsigned int u16;
typedef unsigned long u32;

#define F_CPU 48000000UL

#include <avr/io.h>
#include <util/delay.h>


#define USART USARTC0
#define OnLight PORTQ.OUTCLR = PIN2_bm
#define OffLight PORTQ.OUTSET = PIN2_bm
#define ToggleLight PORTQ.OUTTGL = PIN2_bm
#define IsPressedButton (PORTQ.IN & PIN3_bm)

#define GetPortOne PORTK.IN
#define GetPortSecond PORTK.IN

#define ScanPorts10(p) do{*p-- = GetPortOne; *p-- = GetPortSecond; /* 0 */\
*p-- = GetPortOne; *p-- = GetPortSecond; /* 1 */\
*p-- = GetPortOne; *p-- = GetPortSecond; /* 2 */\
*p-- = GetPortOne; *p-- = GetPortSecond; /* 3 */\
*p-- = GetPortOne; *p-- = GetPortSecond; /* 4 */\
*p-- = GetPortOne; *p-- = GetPortSecond; /* 5 */\
*p-- = GetPortOne; *p-- = GetPortSecond; /* 6 */\
*p-- = GetPortOne; *p-- = GetPortSecond; /* 7 */\
*p-- = GetPortOne; *p-- = GetPortSecond; /* 8 */\
*p-- = GetPortOne; *p-- = GetPortSecond; /* 9 */}while(0)

#define ScanPorts100(p) do{ScanPorts10(p); /* 0 */\
ScanPorts10(p); /* 1 */\
ScanPorts10(p); /* 2 */\
ScanPorts10(p); /* 3 */\
ScanPorts10(p); /* 4 */\
ScanPorts10(p); /* 5 */\
ScanPorts10(p); /* 6 */\
ScanPorts10(p); /* 7 */\
ScanPorts10(p); /* 8 */\
ScanPorts10(p); /* 9 */}while(0)

#define ScanPorts1000(p) do{ScanPorts100(p); /* 0 */\
ScanPorts100(p); /* 1 */\
ScanPorts100(p); /* 2 */\
ScanPorts100(p); /* 3 */\
ScanPorts100(p); /* 4 */\
ScanPorts100(p); /* 5 */\
ScanPorts100(p); /* 6 */\
ScanPorts100(p); /* 7 */\
ScanPorts100(p); /* 8 */\
ScanPorts100(p); /* 9 */}while(0)


#define PutChar(ch) do{}while(!(USART.STATUS & USART_DREIF_bm));\
USART.DATA = ch;


#define SIZE_ARRAY 8000
unsigned char array[SIZE_ARRAY];

void sendDigit(u16 digit){
PutChar(digit/1000 + '0'); digit %= 1000;
PutChar(digit/100 + '0'); digit %= 100;
PutChar(digit/10 + '0'); digit %= 10;
PutChar(digit + '0');
PutChar(' ');
PutChar(' ');
}

void sendBusState(u08 state){
for(u08 i=0; i<8; ++i){
PutChar(state & _BV(7) ? '1' : '0');
PutChar('\t');
state <<= 1;
}
}

int main(void){

OSC.XOSCCTRL=0x4b; // диапазон: 2-9 МГц, внешний резонатор, с временем запуска 16 000 циклов
OSC.CTRL=0x08; // включаем генератор на внешнем кварцевом резонаторе
while(!(OSC.STATUS&0x08)); // ждём готовности генератора на внешнем кварцевом резонаторе

OSC.PLLCTRL=0xc6; // внешний источник синхро, множитель=6
OSC.CTRL=0x18; // включаем синтезатор частоты
while(!(OSC.STATUS&0x10)); // ждём готовности синтезатора частоты

CPU_CCP=0xd8; // разрешить (сигнатурой) изменение важного регистра (следущая строка)
CLK.CTRL=0x04; // источник системной синхронизации - синтезатор частоты
CLK.PSCTRL=0x00; // Коэф. деления предделителей A, B и C = 1.

PORTC.DIRSET = PIN3_bm;
PORTC.DIRCLR = PIN2_bm;

USART.CTRLC = (uint8_t) USART_CHSIZE_8BIT_gc | USART_PMODE_DISABLED_gc;
USART.BAUDCTRLA = (u08)289; // Битрейт 921600 при 48МГц clock
USART.BAUDCTRLB = (289 >> 8) | ((-7) << USART_BSCALE0_bp);
USART.CTRLB |= USART_TXEN_bm;

PORTQ.DIRSET = PIN2_bm; // PQ2 Лампа
PORTQ.DIRCLR = PIN3_bm; // PQ3 Кнопка
PORTQ.PIN3CTRL = PORT_OPC_WIREDORPULL_gc; // Подтяжка вниз


PORTK.DIR = PORTJ.DIR = 0;
PORTJ.PIN0CTRL =
PORTJ.PIN1CTRL =
PORTJ.PIN2CTRL =
PORTJ.PIN3CTRL =
PORTJ.PIN4CTRL =
PORTJ.PIN5CTRL =
PORTJ.PIN6CTRL =
PORTJ.PIN7CTRL =
PORTK.PIN0CTRL =
PORTK.PIN1CTRL =
PORTK.PIN2CTRL =
PORTK.PIN3CTRL =
PORTK.PIN4CTRL =
PORTK.PIN5CTRL =
PORTK.PIN6CTRL =
PORTK.PIN7CTRL = PORT_OPC_WIREDANDPULL_gc; // Подтяжка вверх


while(1){

while(!IsPressedButton){
_delay_ms(250);
ToggleLight;
}

u08 *p = &array[SIZE_ARRAY-1];

while(!(PORTJ.IN & PIN7_bm)); // Ждать появления питания на девайсе

ScanPorts1000(p);
ScanPorts1000(p);
ScanPorts1000(p);
ScanPorts1000(p);

p = &array[SIZE_ARRAY-1];
u16 index = SIZE_ARRAY-1;

PutChar('-'); PutChar('-'); PutChar('-'); PutChar('-');
static u16 currentSample = 0;
sendDigit(currentSample++); PutChar('\n'); PutChar('\r');

do{
sendDigit(index--);
PutChar('\t');

//sendBusState(*p--);
//PutChar('\t');

sendBusState(*p);
PutChar('\n');
PutChar('\r');
static u08 pause = 0;
if(!pause--)
_delay_ms(10); // Для компенсации "несинхронности UART"
}while(p-- != array);

PutChar('\n');
PutChar('\r');
PutChar('\n');
PutChar('\r');
PutChar('\n');
PutChar('\r');

_delay_ms(500);
}
}
=GM=
Цитата(Sirko @ Jul 2 2011, 17:33) *
Наивный, однако wink.gif

Да, наивный и упрямый, поскольку, чтобы считать сигнал около мегагерца, нужна частота выборок около двух мегагерц. Следовательно, при тактовой частоте проца 20 МГц, необходимое число тактов на цикл 20/2=10 машинных циклов. Десяти циклов должно хватить за глаза. Скажу так, за 5 циклов можно считать из порта, записать в память, передвинуть указатель и проверить его на конец массива. Ну и ещё чего-нибудь, скажем помигать светодиодом и опросить кнопки. Ну или непрерывно писать в буфер циклически и по запросу выдавать весь массив, который записан на текущее время.
Sirko
Цитата
Скажу так, за 5 циклов можно считать, записать, передвинуть, проверить и ещё чего-нибудь

Это че, - шутка юмора такая?
Записать-передвинуть с проверкой менее шести тактов, - не представляю как. Или Вы имеете ввиду 5 команд процессора?


Цитата
чтобы считать сигнал около мегагерца, нужна частота выборок около двух

Это никто не оспаривает. 1Мгц - было предположение, поскольку визуально дорожки длинные, несуразные, тянутся от основого чипа к другим - звездой, через разъемы на разные платы, плюс еще в настоящий момент соплей навешано (шлейф от флопика с макетками) и это все вроде как продолжает функционировать.
На текущий момент у меня скан получается порядка 12МГц, но есть вероятность, что и этого недостаточно.

Цитата
нужно развернуть цикл и повторить 1000 раз фрагмент "прочитать порт в регистр + сохранить регистр с автоинкрементом". Вот вам три такта и будет

У меня получается четыре
.
.
.
lds r24, 0x0728
sts 0x3F42, r24
lds r24, 0x0728
sts 0x3F41, r24
lds r24, 0x0728
sts 0x3F40, r24
.
.
.
rx3apf
Цитата(Sirko @ Jul 3 2011, 00:50) *
Это че, - шутка юмора такая?
Записать-передвинуть с проверкой менее шести тактов, - не представляю как. Или Вы имеете ввиду 5 команд процессора?

Loop:
in temp,<порт>
st Z+,temp
cp ZL,...
cpc ZH,...
in temp,<порт>
st Z+,temp
brne Loop
Sirko
Попробую прокоментировать, если че поправьте.

Loop:
in temp,<порт> - получить состояние шины (1 такт)
st Z+,temp - засунуть это значение в оперативку (2 такта)
cp ZL,... - если младший байт указателя не переходит границу, то перейти к опросу порта (1 такт)
cpc ZH,... - иначе проверить старший байт, если ... - неважно
in temp,<порт> - получить состояние шины (1 такт)
st Z+,temp - засунуть это значение в оперативку (2 такта)
brne Loop - продолжить (если нужно) (1 такт)

Т.е. если не придираться к иногда возникающему CP-CPC, то в среднем по четыре такта на один опрос.
Ну в принципе в настоящий момент у меня тоже их четыре
А вот как-бы засунуть в сишный файл вот это -
in temp,<порт>
st Z+,temp
in temp,<порт>
st Z+,temp
in temp,<порт>
st Z+,temp
...
???
rx3apf
Цитата(Sirko @ Jul 3 2011, 02:33) *
Попробую прокоментировать, если че поправьте.

cp ZL,... - если младший байт указателя не переходит границу, то перейти к опросу порта (1 такт)

Вот тут неправильно. Последовательность cp ZL,... cpc ZH,... - это сравнение с 16-битным значением (заданным в регистрах), определяющим границу массива.
Цитата
Т.е. если не придираться к иногда возникающему CP-CPC, то в среднем по четыре такта на один опрос.

Не четыре, а пять, и не в среднем, а всегда. Но граница должна быть установлена на один байт раньше.

Sirko
Цитата
Вот тут неправильно.
Понял. Спасибо.

Но тогда выходит, что
Цитата
и не в среднем, а всегда.
- всегда, но чередуясь 5, 4, 5, 4, 5...

Loop:
in temp,<порт> - получить состояние шины (1 такт)
st Z+,temp - засунуть это значение в оперативку (2 такта)
cp ZL,... - если младший и
cpc ZH,... - старший байт указателя переходят границу - выставить флаг (2 такта)
in temp,<порт> - получить состояние шины (1 такт)
st Z+,temp - засунуть это значение в оперативку (2 такта)
brne Loop - продолжить (если нужно) (1 такт)
rx3apf
Цитата(Sirko @ Jul 3 2011, 11:58) *
Понял. Спасибо.

Но тогда выходит, что - всегда, но чередуясь 5, 4, 5, 4, 5...
.....
brne Loop - продолжить (если нужно) (1 такт)

При переходе - 2 такта. Поэтому каждый период выборки строго 5 тактов.
Sirko
Спасибо, буду знать.
defunct
Цитата(Sirko @ Jul 3 2011, 12:51) *
Спасибо, буду знать.

Эта задача не для AVR. Возьмите простенький CPLD + static RAM + mega162.
подключите CPLD и мегу(по ее шине) к МС памяти.
Внутри CPLD организуется счетчик по отрицательному фронту строба шины которую хотите мониторить.
По стробу внешней шины читайте с нее данные и кладите в SRAM.
при переполнении счетчика, отключите CPLD от памяти и дайте меге строб что та может читать.

CPLD на 150-180Mhz с требуемым количеством ног (вам надо 18+8+2) стоит в районе 5$-8$.
либо готовая макетка за 13$ здесь
МС SRAM брать такую чтобы успевала за шиной которую собрались мониторить. В старых 386-х под кеш использовались 10нс (100Mhz) память 62256 (32KB чип) в DIP копусах вот ее можно взять, но возможно хватит и более медленной памяти.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.