Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Как оценить время в Keil
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > ARM, 32bit
Nikitoc
Всем здравствуйте. Я недавно перепрыгнул (вернее нахожусь в прыжке) с PIC'ов на ARM'ы. После привычного Mplab'a Keil кажется враждебным biggrin.gif А если конкретней мне никак не удается оценить количество тактов за которое выполняется команда. В Mplab'e есть очень удобный инструмент Stopwatch - который, собственно, за все эти ф-ции отвечает. А в Keil'e такого нет. Посоветуйте, как мерить время (в тактах, или наносекундах) выполнения команд. Пишу на Си. Камень at91sam7s256.
Да, и еще, если разогнать проц до 80Мгц и выполнять программу из оперативной памяти, будут ли быстрее выполняться команды обращения к портам? Мне нужно максимально быстро их сканировать для считывания видео с простенькой CMOS-камеры.
aaarrr
Цитата(Nikitoc @ Mar 16 2010, 17:30) *
Посоветуйте, как мерить время (в тактах, или наносекундах) выполнения команд.

Debug->Execution Profiling->Time
Только вейтстейты при работе с флеш и обращению к периферии он не учитывает. Я уж не говорю про простои из-за работы DMA.
Поэтому считать лучше или в уме или на железке.

Цитата(Nikitoc @ Mar 16 2010, 17:30) *
Да, и еще, если разогнать проц до 80Мгц и выполнять программу из оперативной памяти...

Ну, если он разгонится еще.

Цитата(Nikitoc @ Mar 16 2010, 17:30) *
...будут ли быстрее выполняться команды обращения к портам? Мне нужно максимально быстро их сканировать для считывания видео с простенькой CMOS-камеры.

Будут, конечно. Но чтение порта в любом случае достаточно медленное - 4 такта.
Nikitoc
Цитата(aaarrr @ Mar 16 2010, 19:09) *
Debug->Execution Profiling->Time
Только вейтстейты при работе с флеш и обращению к периферии он не учитывает. Я уж не говорю про простои из-за работы DMA.
Поэтому считать лучше или в уме или на железке.


А если по дизассемблеру считать? В смысле по количеству команд? Насколько это будет точно?
aaarrr
Цитата(Nikitoc @ Mar 16 2010, 21:48) *
А если по дизассемблеру считать? В смысле по количеству команд? Насколько это будет точно?

И близко не будет. Но запомнить, сколько занимает тактов та или иная команда, совсем нетрудно. Ну, плюс еще досчитывать вейтстейты на обращение к флеш и периферии через APB.
Nikitoc
Цитата(aaarrr @ Mar 16 2010, 22:31) *
И близко не будет. Но запомнить, сколько занимает тактов та или иная команда, совсем нетрудно. Ну, плюс еще досчитывать вейтстейты на обращение к флеш и периферии через APB.

О. А можно поподробней про эти самые вэйтстейты? В стартапе есть закладочка Configuration Wizard и там настройки Embedded Flash Controller (EFC1 и EFC0). И две опции: FMCN: Flash Microsecond Cycle Number (по умолчанию 50) и FWS: Flash Wait State (по умолчанию Read: 2 cycle/ Write: 3 cycles). Что это за параметры? Как их можно изменять? На что влияют?
aaarrr
Поподробнее можно посмотреть в даташите. Если кратко, то при работе на частоте больше 30МГц, доступ к флеш должен осуществляться минимум с 1 WS, т.е. за 2 такта.
Nikitoc
Угу, ясненько. И если можно последний моментик: Вы написали, что при обращении к периферии (я так понимаю порты туда тоже входят) нужно досчитывать эти вэйтстейты. Но разве эти регистры расположены во флеше?
aaarrr
Периферия находится на "медленной" шине APB, цикл чтения/записи на ней занимает два такта MCK.
Nikitoc
Цитата(aaarrr @ Mar 16 2010, 23:46) *
Периферия находится на "медленной" шине APB, цикл чтения/записи на ней занимает два такта MCK.

Спасибо большое. Вроде все ясно. Дальше, думаю, с даташитом разберусь.
Nikitoc
Типа отчет, о том что получилось. Тестовая программка:
Код
while (1) {
    AT91C_BASE_PIOA -> PIO_SODR = AT91C_BASE_PIOA -> PIO_SODR | 0x00000800;           //  PA11    = 1
    AT91C_BASE_PIOA -> PIO_CODR = AT91C_BASE_PIOA -> PIO_CODR | 0x00000800;           //  PA11    = 0
}

Мне удалось разогнать процессор (at91sam7s256) до 100 Мгц.
При выполнении программы из ОЗУ период меандра на ножке PA11 был примерно 200 нс.
И это при тактовом цикле 10нс!
Дизассемблер показал, что изменение состояния ножки занимает 4-5 команд:
Код
   319:         while (1) {
0x00102CE0  EA000008  B         0x00102D08
   320:         AT91C_BASE_PIOA -> PIO_SODR = AT91C_BASE_PIOA -> PIO_SODR | 0x00000800;     //  PA11      = 1
0x00102CE4  E3A00000  MOV       R0,#0x00000000
0x00102CE8  E5100BD0  LDR       R0,[R0,#-0x0BD0]
0x00102CEC  E3800B02  ORR       R0,R0,#0x00000800
0x00102CF0  E3A01000  MOV       R1,#0x00000000
0x00102CF4  E5010BD0  STR       R0,[R1,#-0x0BD0]
   321:         AT91C_BASE_PIOA -> PIO_CODR = AT91C_BASE_PIOA -> PIO_CODR | 0x00000800;                   //  PA11     = 0
0x00102CF8  E3A00000  MOV       R0,#0x00000000
0x00102CFC  E5100BCC  LDR       R0,[R0,#-0x0BCC]
0x00102D00  E3800B02  ORR       R0,R0,#0x00000800
0x00102D04  E5010BCC  STR       R0,[R1,#-0x0BCC]
   319:         while (1) {
0x00102D08  EAFFFFF5  B         0x00102CE4

Если каждая команда будет выполняться ровно 2 такта, то все складывается. Но, если я не ошибаюсь, у армов большинство команд выполняется за один такт. Может кто-нибудь подскажет, в чем дело?
aaarrr
Цитата(Nikitoc @ Mar 19 2010, 15:23) *
Мне удалось разогнать процессор (at91sam7s256) до 100 Мгц.

Что за стремление разогнать процессор, даже не попытавшись сначала освоить работу с ним в штатном режиме?

Цитата(Nikitoc @ Mar 19 2010, 15:23) *
Но, если я не ошибаюсь, у армов большинство команд выполняется за один такт.

Ошибаетесь. Только MOV и ORR в приведенном листинге выполняются за один такт.

Цитата(Nikitoc @ Mar 19 2010, 15:23) *
Может кто-нибудь подскажет, в чем дело?

Оптимизатор надо было включить. Ну и на чтениях порта теряете время.
Nikitoc
Цитата(aaarrr @ Mar 19 2010, 17:24) *
Ошибаетесь. Только MOV и ORR в приведенном листинге выполняются за один такт.


А не подскажете, случайно, где можно подробно посмотреть время исполнения (в тактах) ассемблерных команд?

Цитата
Оптимизатор надо было включить. Ну и на чтениях порта теряете время.


Ну да. При включенной оптимизации по времени 3-го уровня компилятор сократил код до 3-х команд:

Код
   319:            AT91C_BASE_PIOA -> PIO_CODR = AT91C_BASE_PIOA -> PIO_CODR | 0x00000800;
0x00102BC4  E5140BCC  LDR       R0,[R4,#-0x0BCC]
0x00102BC8  E3800B02  ORR       R0,R0,#0x00000800
0x00102BCC  E5040BCC  STR       R0,[R4,#-0x0BCC]
   320:                 AT91C_BASE_PIOA -> PIO_SODR = AT91C_BASE_PIOA -> PIO_SODR | 0x00000800;
0x00102BD0  E5140BD0  LDR       R0,[R4,#-0x0BD0]
0x00102BD4  E3800B02  ORR       R0,R0,#0x00000800
0x00102BD8  E5040BD0  STR       R0,[R4,#-0x0BD0]
   318:          while (1) {
0x00102BDC  EAFFFFF8  B         0x00102BC4

Но измерить разницу (увидеть на осциллографе) в периоде мне не удалось. Я имею в виду, что период так и остался равным примерно 200 нс...

А по поводу разгона... так я ж говорил это мне видео хочется поснимать biggrin.gif При таких условиях мне удается захватить примерно 3,5 fps (QCIF 176x144). Но меня ограничивает скорость COM-порта PC - 115200. Поэтому следующий этап - освоение SPI и DMA. Хочется сделать потоковую запись на SD-карточку. Если это конечно будет иметь смысл (по скорости).
aaarrr
Цитата(Nikitoc @ Mar 19 2010, 16:47) *
А не подскажете, случайно, где можно подробно посмотреть время исполнения (в тактах) ассемблерных команд?

В даташите на ARM7TDMI.

Цитата(Nikitoc @ Mar 19 2010, 16:47) *
Но измерить разницу (увидеть на осциллографе) в периоде мне не удалось. Я имею в виду, что период так и остался равным примерно 200 нс...

Было 22 такта, стало 19. Уберите чтение PIO_SODR и PIO_CODR - это регистры только для записи. И почитайте даташит на процессор.
Nikitoc
Цитата(aaarrr @ Mar 19 2010, 18:14) *
В даташите на ARM7TDMI.
Было 22 такта, стало 19. Уберите чтение PIO_SODR и PIO_CODR - это регистры только для записи. И почитайте даташит на процессор.

:-) Все, я ушел просвещаться. Спасибо за помощь.
P.S. Хотя по поводу PIO_SODR и PIO_CODR я не понял.
Код
AT91C_BASE_PIOA -> PIO_CODR = AT91C_BASE_PIOA -> PIO_CODR | 0x00000800

Я здесь вроде не читаю, а записываю...
Как это сделать - на асме или на Си тоже можно?
toweroff
Цитата
AT91C_BASE_PIOA -> PIO_SODR = AT91C_BASE_PIOA -> PIO_SODR | 0x00000800;


Цитата
Я здесь вроде не читаю, а записываю...


0x00102BC4 E5140BCC LDR R0,[R4,#-0x0BCC]
0x00102BC8 E3800B02 ORR R0,R0,#0x00000800
0x00102BCC E5040BCC STR R0,[R4,#-0x0BCC]

и где же _только пишем_?
RabidRabbit
Цитата(Nikitoc @ Mar 19 2010, 17:27) *
Я здесь вроде не читаю, а записываю...


Ну как же:
AT91C_BASE_PIOA -> PIO_CODR = AT91C_BASE_PIOA -> PIO_CODR | 0x00000800;
Если подумать, откуда берётся значение выражения, выделенного жирным шрифтом? smile.gif
Поэтому вполне достаточно будет просто:
AT91C_BASE_PIOA -> PIO_CODR = 0x00000800;
То же самое для PIO_SODR.
Если записать в CODR 0x800 то установится в 0 только 11-й бит порта, на остальные никакого влияния не будет smile.gif
aaarrr
Цитата(Nikitoc @ Mar 19 2010, 17:27) *
Код
AT91C_BASE_PIOA -> PIO_CODR = AT91C_BASE_PIOA -> PIO_CODR | 0x00000800

Я здесь вроде не читаю, а записываю...
Как это сделать - на асме или на Си тоже можно?

Читаете, естественно. Откуда иначе возьмется содержимое PIO_CODR, над которым производится операция bitwise OR?

Если принять во внимание совершенную бесполезность чтения PIO_CODR (вредность, точнее, ибо никто даже не гарантирует,
что из этого регистра будет считан 0, хотя на практике это и так), то останется:
Код
AT91C_BASE_PIOA->PIO_CODR = 0x00000800;

И этот код даст точно такой же результат.

P.S. Запись вида a |= 1 вместо a = a | 1 делает код гораздо более удобочитаемым.
Nikitoc
Огромное спасибо за пояснения уважаемым гуру. Я, видите ли, не силен в arm-ассемблере (в СИ тоже не профессор biggrin.gif ), знаю более-менее только микрочиповский, для 8-битников, но это совсем другое дело . С этим чтением запутался вообще. В общем сделал как вы рекомендовали и скомпилилось компактней:
Код
0x00103C14  E3A01B02  MOV       R1,#0x00000800
0x00103C18  E3A00000  MOV       R0,#pI2C(0x00000000)
0x00103C1C  E5001BD0  STR       R1,[R0,#-0x0BD0]
  3169:       AT91C_BASE_PIOA -> PIO_CODR =  0x00000800;
0x00103C20  E5001BCC  STR       R1,[R0,#-0x0BCC]
0x00103C24  EAFFFFFC  B         0x00103C1C

здесь:
Код
AT91S_TWI  * pI2C = AT91C_BASE_TWI;
#define AT91C_BASE_TWI       (AT91_CAST(AT91PS_TWI)     0xFFFB8000) // (TWI) Base Address


Вроде бы (особенно с вашими разъяснениями) код стал понятен, кроме строки:
Код
0x00103C18  E3A00000  MOV       R0,#pI2C(0x00000000)

Как я не отнимал от 0xFFFB8000 0x0BD0 и 0x0BCC, базовых адресов регистров PIO_CODR (0xFFFFF434) PIO_SODR (0xFFFFF430) я не получил wacko.gif . Что я неправильно понимаю? Почему нельзя занести в регистр R0 значение не pI2C, а pPIO? Относительно него смещения были бы меньше.
aaarrr
Цитата(Nikitoc @ Mar 20 2010, 01:45) *
Вроде бы (особенно с вашими разъяснениями) код стал понятен, кроме строки:
Код
0x00103C18  E3A00000  MOV       R0,#pI2C(0x00000000)

Как я не отнимал от 0xFFFB8000 0x0BD0 и 0x0BCC, базовых адресов регистров PIO_CODR (0xFFFFF434) PIO_SODR (0xFFFFF430) я не получил. Что я неправильно понимаю? Почему нельзя занести в регистр R0 значение не pI2C, а pPIO? Относительно него смещения были бы меньше.

А почему от 0xFFFB8000, а не от 0, как, собственно, и написано в команде? 0 - 0x0BD0 = 0xFFFFF430; 0 - 0x0BCC = 0xFFFFF434
Откуда в листинге взялся pI2C - остается только гадать. Вполне допускаю, что это просто своеобразный глюк дизассемблера.
Nikitoc
Хм. Ну ладно, но нафига тогда эта петрушка со смещениями? Почему в качестве операнда надо указывать [R0,#-0x0BD0] или [R0,#-0x0BCC] а не, непосредственно адрес, допустим, [0xFFFFF434] ?
aaarrr
Потому что в 32 бита команды не получится упихать 32 бита адреса. Изучите систему команд, многое прояснится.
Nikitoc
Да я вообще и пытаюсь это делать. Но блин, тяжело дается. С помощью сообщества гораздо проще. Да и в любом случае, на примерах удобней учится, чем тупо читать англоязычный даташит и пытаться уместить его весь в голове, а потом по кусочкам доставать нужное.
aaarrr
Цитата(Nikitoc @ Mar 20 2010, 02:27) *
Да я вообще и пытаюсь это делать.

Это хорошо, это правильно smile.gif
Посмотрите вот этот старый кошерный даташит с подробным разбором системы команд и примерами. Современные, увы, уже не так хороши.

Нажмите для просмотра прикрепленного файла
Нажмите для просмотра прикрепленного файла
Нажмите для просмотра прикрепленного файла
Nikitoc
Пасибки. Попробую дочитать хотя бы до половины smile.gif Лично у меня в библиотеке имеются два "учебника" по армовскому асму 1999 и 2004 годов. Увлекательно, конечно, читать. Особенно когда доходит смысл написанного. Но иногда нужно прочесть одно и то же раз 5-10, чтобы въехать. Но если не применять на практике, то оно моментально выветривается. А вот за вчерашний день я аж 4 команды выучил (LDR, MOV, ORR и STR)! И теперь не скоро их забуду :-) В общем, большое спасибо за помощь. Будем двигаться дальше помаленьку.
defunct
Цитата(Nikitoc @ Mar 19 2010, 15:47) *
А не подскажете, случайно, где можно подробно посмотреть время исполнения (в тактах) ассемблерных команд?

Да собсно можно просто подумать логически, например команда LDR загружает слово из памяти в регистр.
Проц делает выборку слова команды из ОЗУ, а затем выполняя эту команду читает еще одно слово из того же ОЗУ в регистр. Не может же он за один такт оба слова прочитать.
Аналогично и с STR.
aaarrr
По такой логике LDR и STR должны занимать одинаковое число тактов, что не соответствует действительности.
defunct
Цитата(aaarrr @ Mar 21 2010, 03:11) *
По такой логике LDR и STR должны занимать одинаковое число тактов, что не соответствует действительности.

По такой логике ясно, что и LDR, и STR - выполняются не за один такт.
То, что запись и чтение это разные операции это уже другая логика. А конкретное количество тактов смотрится в Instruction Set Reference и зависит от конкретной архитекуры.
aaarrr
Цитата(defunct @ Mar 21 2010, 04:34) *
...А конкретное количество тактов смотрится в Instruction Set Reference и зависит от конкретной архитекуры.

И может быть на некоторых архитектурах равно одному. Так что все равно начинать надо с чтения и изучения.
defunct
Цитата(aaarrr @ Mar 21 2010, 03:47) *
И может быть на некоторых архитектурах равно одному. Так что все равно начинать надо с чтения и изучения.

Но только не на ARM7 ;> Со вторым выводом солидарен.
Nikitoc
Продолжаю терзать at91sam7s256.

Привожу код который я написал для работы с SPI. Самое удивительное (для меня) и чему я не могу найти объяснения - это то, что код этот нормально работал (писал на SD-карточку), а теперь перестал работать.

Код
/////////////////////////////////////////////////////////////////////////////
void CONFIG_SPI () {
    pSPI -> SPI_MR = AT91C_SPI_MSTR | AT91C_SPI_MODFDIS | (AT91C_SPI_PCS & (0x0D << 16));    //Master mode,  use freq divider, select 1 chanel CS.
    pSPI -> SPI_CSR[1] = AT91C_SPI_NCPHA | AT91C_SPI_BITS_8 | AT91C_SPI_CSAAT | (AT91C_SPI_SCBR & (SCBR << 8));    // clock idle low, data valid on rising edge, 8-bit data
    pSPI -> SPI_CR = AT91C_SPI_SPIEN | AT91C_SPI_LASTXFER;
}
/////////////////////////////////////////////////////////////////////////////////////
char SPI (char DATA) {
    pSPI -> SPI_TDR = DATA;    
    while (!(pSPI -> SPI_SR & AT91C_SPI_TDRE));   /* Wait for Empty Tx Buffer */    
    while (!(pSPI -> SPI_SR & AT91C_SPI_RDRF));    
    return (pSPI -> SPI_RDR & 0xFF);  
}
////////////////////////////////////////////////////////////////////////////////////


Просьба помочь найти ошибку.
aaarrr
Цитата(Nikitoc @ Apr 6 2010, 19:59) *
...теперь перестал работать.

Предлагаете угадать, как именно он "перестал работать"?
Nikitoc
Цитата(aaarrr @ Apr 6 2010, 19:08) *
Предлагаете угадать, как именно он "перестал работать"?

Да нет же. Может в коде какая-то ошибка... Отладчиком дохожу до условия -
Код
    while (!(pSPI -> SPI_SR & AT91C_SPI_TDRE));   /* Wait for Empty Tx Buffer */

- и зависаю здесь, не могу дождаться этого флага. Линия CS в низкий уровень после загрузки SPI_TDR тоже не переводится.
Такое впечатление, что SPI вообще не включен.
aaarrr
Цитата(Nikitoc @ Apr 6 2010, 20:26) *
Такое впечатление, что SPI вообще не включен.

А он включен? Выложите весь код, относящийся к SPI.
Nikitoc
Цитата(aaarrr @ Apr 6 2010, 19:39) *
А он включен? Выложите весь код, относящийся к SPI.

Код
void PIO_INIT () {
    pP_M_C -> PMC_PCER = pP_M_C -> PMC_PCER | 0x00000024;     // Enable clocking of SPI and PIO (PIO clk enable for reading input state)                    
    pPIO -> PIO_PDR = 0x80027000;         // disable PA17 I/O for PCK1 out  and PA12,13,14,31 for SPI
    pPIO -> PIO_PER = 0x00240800;         // enable PA21, PA18 and PA11 I/O
    pPIO -> PIO_PPUDR = 0xFFFFFCFF;         // Turn off pull-up resistor  except 8 and 9
    pPIO -> PIO_ODR = 0x200014FF;         // Enable input for PIO PA29, PA12, PA10, PA0-PA7
    pPIO -> PIO_OER = pPIO -> PIO_OER | 0x00240800;        // Enable out for PA11(CAMERA ON/OFF) and PA21 (ON/OFF SDcard) and PA18 (RED_LED)
}

Использую такие дефайны:
Код
#define SCBR  ((MCK/SPCK))                      /*SPI Baud Rate Divisor */

#define SPCK    18000000                      /* SPI Baud Rate */

#define QUARTZ    20000000

#define FREQ_DIV   10

#define FREQ_MUL   89

#define MCK   (((QUARTZ*(FREQ_MUL+1)/FREQ_DIV))/2)


Код
AT91S_SPI  * pSPI = AT91C_BASE_SPI;
aaarrr
pP_M_C чему равен? И зачем вообще использовать эти указатели?
Запись
Код
*AT91C_PMC_PCER = 1UL << AT91C_ID_SPI;

куда более читабельна и не вызывает лишних вопросов. Кроме того, не нужно читать write only регистры - PMC_PCER, PIO_OER.

Начните с приведения частоты MCK к разрешенным значениям.
Nikitoc
Цитата
pP_M_C чему равен? И зачем вообще использовать эти указатели?

Код
AT91S_PMC  *pP_M_C = AT91C_BASE_PMC;

Цитата
Запись
Код
*AT91C_PMC_PCER = 1UL << AT91C_ID_SPI;

куда более читабельна и не вызывает лишних вопросов. Кроме того, не нужно читать write only регистры - PMC_PCER, PIO_OER.

Угу. Спасибо. Понял.
Цитата
Начните с приведения частоты MCK к разрешенным значениям.

К сожалению не помогло (уменьшил до 50МГц) симптомы те же :-(
Значения прочитанных отладчиком регистров соответствуют тому, что я в них записываю (кроме WRITE ONLY, конечно). Я в тупике.
aaarrr
Для write only есть соответствующие status-регистры. Проверьте, включен ли клок для SPI в PMC_PCSR.
Nikitoc
Цитата(aaarrr @ Apr 6 2010, 20:29) *
Проверьте, включен ли клок для SPI в PMC_PCSR.

Да включен, там записано значение 0xA4

P.S. А может SPI выйти из строя? И так чтобы регистры при этом остались доступными для чтения и записи?
Nikitoc
Вот здесь http://electronix.ru/forum/index.php?showtopic=73308 у человека были похожие проблемы. Но я так и не понял поборол он их или нет.
aaarrr
Цитата(Nikitoc @ Apr 6 2010, 23:02) *
...я так и не понял поборол он их или нет.

Поборол, да. Просто отладчиком перестал пользоваться. Попробуйте, кстати.
Nikitoc
Проблема решилась. Оказалось что SD-шка мертвая. Причем наглухо. Питание у нее звонится с землей. Что могло привести к таким печальным последствиям я даже не знаю. После замены карточки все заработало. Но все равно мне не понятно почему не работал SPI. Буфер ведь должен был заполниться по-любому какими-то значениями (0x00 или 0xFF) после определенного количества тактов. Да и линия CS перед началом передачи должна была опуститься, а этого не происходило. Такое впечатление, что передача вообще не начиналась. Короче неясно. Туманно.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.