Невыровненный доступ

sharikov

Active member
Поддерживается ли в ESP32 работа с невыровенными адресами или будет исключение ?
Например если 32-х битную переменную расположить по адресу не кратному 4.
Такой же вопрос про ESP8266.
Про замедление и плохой стиль я знаю. Вопрос именно про исключения.
 

enjoynering

Active member
я экспериментировал с ESP8266 и Arduino framework. разницы размера скеча от в unt8_t, uint16_t и uint32_t не заметил. похоже, какой тип не выбрать, всегда тупо занимается 4 бита

вот тут чел спрашивает почему sizeof возвращает 1+1+4=6 байт
Код:
typedef struct packet_t{
  uint8_t a;     // sizeof(uint8_t) returns 1
  uint8_t b;     // sizeof(uint8_t) returns 1
  float c;         // sizeof(float) returns 4
};
а размер пакета получается 8
Код:
sizeof(packet_t) //returns 8
чтоб этого не происходило надо так
Код:
typedef struct __attribute((__packed__)) packet_t{
  uint8_t a;
  uint8_t b;
  float c;
};
 

pvvx

Активный участник сообщества
Поддерживается ли в ESP32 работа с невыровенными адресами или будет исключение ?
Будет исключение.

В IRAM, (кеш) Flash, registers - всегда должно быть выровнено и желательно чтение/запись по 32 бита.
 

pvvx

Активный участник сообщества
Про замедление и плохой стиль я знаю.
Думаю, что нет.
На версиях NodeMCU LUA ESP8266 по exception читаются байтовые (string/char) переменные из Flash.
Один exception = один char :) Метода такая чтобы не исправлять printf() и прочие процедуры библиотек создана процедура на векторе exception выуживающая байтики из IRAM (кеш) Flash...
За последние годы в некоторых вариантах Aduino такое тоже наблюдалось - вставляли обработку exception по нарушению align(4)... - сотни тактов на байт для Arduino ерунда. Всё равно игрушка...
 

sharikov

Active member
Будет исключение.
В IRAM, (кеш) Flash, registers - всегда должно быть выровнено и желательно чтение/запись по 32 бита.
На ESP32 исключения не возникает.
Тест:
Код:
void unaligned_access_test(void) {
   static char buffer[61];
   unsigned int index, data;
   volatile char *ptr1;

   // fill buffer
   for (index=0; index < sizeof(buffer); index++)
       buffer[index]=index;
   // check buffer alignment
   ptr1=&buffer[0];
   printf("&buffer[0]=0x%x\n", (unsigned int)ptr1);
   if (((unsigned int)ptr1 & 3) !=0) {
       printf("unaligned!\n");
       data = (unsigned int)ptr1;
       data|=3;
       data+=1;  // 4-byte aligned address
       ptr1=(volatile char *)data;
       printf("new: 0x%x\n", (unsigned int)ptr1);
   }
   // unaligned access test
   for (index=0; index<4; index++) {
       printf("[0x%x] : 0x%04x\n", (unsigned int)ptr1, *(unsigned int*)ptr1);
       ptr1++;
   }
}
Результат:
Код:
&buffer[0]=0x3ffb2eb4
[0x3ffb2eb4] : 0x3020100
[0x3ffb2eb5] : 0x4030201
[0x3ffb2eb6] : 0x5040302
[0x3ffb2eb7] : 0x6050403
 

sharikov

Active member
Добавил измерение времени:
Код:
extern unsigned int g_ticks_per_us_pro;
void unaligned_access_test(void) {
    static char buffer[61];
    unsigned int index, data, ccount_now, ccount_prv;
    volatile char *ptr1;

    printf("ticks per us = %u\n", g_ticks_per_us_pro);
    // fill buffer
    for (index=0; index < sizeof(buffer); index++)
        buffer[index]=index;
    // check buffer alignment
    ptr1=&buffer[0];
    printf("&buffer[0]=0x%x\n", (unsigned int)ptr1);
    if (((unsigned int)ptr1 & 3) !=0) {
        printf("unaligned!\n");
        data = (unsigned int)ptr1;
        data|=3;
        data+=1;  // 4-byte aligned address
        ptr1=(volatile char *)data;
        printf("new: 0x%x\n", (unsigned int)ptr1);
    }

    // unaligned access test
    for (index=0; index<4; index++) {
        //ccount_prv = xthal_get_ccount();
        __asm__ __volatile__("rsr %0,ccount":"=a" (ccount_prv));
        data=*(volatile unsigned int*)ptr1;
        //ccount_now = xthal_get_ccount();
        __asm__ __volatile__("rsr %0,ccount":"=a" (ccount_now));

        printf("[0x%x] : 0x%04x  cycles : %d\n",
                (unsigned int)ptr1, data,
                (ccount_now < ccount_prv)? ccount_prv-ccount_now : ccount_now- ccount_prv);
        ptr1++;
    }
}
Результаты:
Код:
ticks per us = 240
&buffer[0]=0x3ffb2eb4
[0x3ffb2eb4] : 0x3020100  cycles : 3
[0x3ffb2eb5] : 0x4030201  cycles : 10
[0x3ffb2eb6] : 0x5040302  cycles : 10
[0x3ffb2eb7] : 0x6050403  cycles : 10
По моему 10 тактов для обработчика исключения недостаточно. А вот на два обращения по шине данных и простой конвейера похоже. Результаты на 240MHz и на 160MHz не отличаются.
 

sharikov

Active member
То в простой SRAM. А в других областях "В IRAM, (кеш) Flash, registers"?
При невыровненном чтении данных из области Instruction External Flash (0x400C2000-0x40BFFFFF) возникает исключение LoadStoreAlignment и далее ребут. Выровненное чтение из этой области работает.

Обращение к области Data External Flash (0x3F400000-0x3F7FFFFF) исключение не выдается. Результаты двух тестов:
Код:
ticks per us = 240
&buffer[0]=0x3f402e9e
unaligned!
new: 0x3f402ea0
[0x3f402ea0] : 0x5040302  cycles : 287
[0x3f402ea1] : 0x6050403  cycles : 12
[0x3f402ea2] : 0x7060504  cycles : 12
[0x3f402ea3] : 0x8070605  cycles : 12
ticks per us = 240
&buffer[0]=0x3f402e9e
unaligned!
new: 0x3f402ea0
[0x3f402ea0] : 0x5040302  cycles : 4
[0x3f402ea1] : 0x6050403  cycles : 12
[0x3f402ea2] : 0x7060504  cycles : 12
[0x3f402ea3] : 0x8070605  cycles : 12
287 циклов - подгрузка кэш. Из кэша данные читаются на 1 или 2 такта медленнее чем из SRAM.
 

pvvx

Активный участник сообщества
287 циклов - подгрузка кэш.
Это на одно считывание?
Какая длина блока чтения у "кеш" из SPI-Flash? 16-ть байт или ?
240MHz/287 = 830 кГц 2хCPU :) Супер производительность ESP32 :) :)
У меня, по расчетам, получилось среднее к 10 миллионам операций CPU в секунду (на оба ядра) в процессах типа кодеков и прочего кода (типа рассчет ключей,SSL и подобное), когда он не вмещается в убогий "кеш" ESP32 (а он и не вместится никогда).
 

sharikov

Active member
Это на одно считывание?
Какая длина блока чтения у "кеш" из SPI-Flash? 16-ть байт или ?
240MHz/287 = 830 кГц 2хCPU
В мануале не нашел. На форуме пишут "Cache lines are 32 bytes".
В теории должно быть
40MHz / (32+5) = 1.08 МHz или 0.925us на промах кэш.

Жаль что кэш на 1 такт медленнее SRAM
 

pvvx

Активный участник сообщества
В мануале не нашел. На форуме пишут "Cache lines are 32 bytes".
В теории должно быть
40MHz / (32+5) = 1.08 МHz или 0.925us на промах кэш.
32 байта QSPI -> 64 такта SPI шины + заголовок с адресом (5 байт = 10 тактов + до пары тактов на старте).
Получаем 80MHz/80 = 1 блок в 32 байта -> 1MHz * 32, т.е. QSPI читается в "кеш" со скоростью 32 мегабайта в сек.
А средняя длина команды у ESP - 2 с чем-то байта, к трем ... и учтем работу "кеш" блоками на чтение указателей (лишние чтение вместо 4-х байт 32-х)...
32/3 = 10M команд CPU в сек, если код длиннее "кеш" и идет в линейку или рандом.
STM32 или какой современный PIC, не говоря уже о mips и прочих с большой SRAM, уделывает ESP32 в 10..100 раз.
 

pvvx

Активный участник сообщества
И при такой производительности (в 10МГц) ESP32 жрет так-же, даже немного более из-за постоянной SPI-Flash работы всех контроллеров с тактированием на 240x2МГц ядер. Т.е. и энерго-эффективность так-же падает по сравнению с другими на порядки !
На ESP8266 уже эта бяда, а тут ещё 2 ядра на ту-же шину QSPI повесили... :eek:
 

sharikov

Active member
Интересно что происходит с работой второго ядра во время загрузки кэша ?
Скажем второе ядро исполняет критичный по времени код только из IRAM и в это время первое ядро подгружает кэш.
Шина как я понимаю одна и как пишут на форуме блоков предвыборки в процессорах нет
 

pvvx

Активный участник сообщества
Интересно что происходит с работой второго ядра во время загрузки кэша ?
Скажем второе ядро исполняет критичный по времени код только из IRAM и в это время первое ядро подгружает кэш.
Если оно работает из SRAM (IRAM) - то работает как и работало.
Шина как я понимаю одна и как пишут на форуме блоков предвыборки в процессорах нет
"предвыборки" нет и док-во в лишней команде "memw" и "extw".
 
Сверху Снизу