• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

SPI

Andrey

New member
Кто нибудь пробовал его реально использовать в режиме мастера? Чтобы подключить какое нибудь ацп или цап или ещё что то?
С какой частотой удалось его запустить?
Микросхема имеет несколько CS сигналов. CS совпадет с MTDO, который используется для бута из флэща, И должен быть в момент старта притянут к земле. Это не есть хорошо, дергать CS почём зря. По этому я думаю лучше использовать другой CS. Например CS2. Но в SDK я не нашёл управления CS. Как можно его выбирать? Дёргать им в ручную, как бы тоже не очень красиво.
 

Perfer

New member
Сегодня целый день колупаю тему аппаратного spi на esp8266. Вот самая адекватная инофрмация:
Где-то по ссылкам есть интересный архив SPI_CODE_141204.rar стоит посмотреть
PS да и что зазорного в дерганье CS руками? режим мастера ведь ))
 

Andrey

New member
Спасибо за информацию. Я хотел бы попробовать повесить на SPI 1мгц АЦП, и не хотелось бы терять лишние такты на управление ногой. К тому же по CS у ацп начинается преобразование. и он вроде должен быть синхронизирован с тактами.
 

Perfer

New member
Поигрался с дисплеем (2.2") на чипе ili9341 по SPI. Обмен между esp8266 и дисплеем успешно проходит на частоте 10Mhz - это теоретический максимум на котором может работать ili9341.
CS дергаю сам, так как HSPI ChipSelect сидит на GND жестко. Вашу проблему с CS до конца не понял - сложно представить как CS может быт синхронизирован с тактами ))
 
Последнее редактирование:

Andrey

New member
Прошу прощения не так выразился. Не синхронизирован. В моём случае такт должен быть минимум через 6 нс после выставления CS. Я предполагал что на выставление в ручную потеряется время. А сьём с АЦП предполагался 1 мгц при тактовой SPI от 16 мгц. И не хотелось терять лишние такты. Сейчас похоже проект с SPI для меня стал не актуален, так как не могу обеспечить 1мгц такт с помощью ESP8266. Буду пытаться делать устройство с внешним контроллером и передачей по UART
На счёт CS. Вроде как специально заявлено что CS2 для всяких там АЦП и периферии. Или у вас на модуле он замкнут физически?
Ещё вопрос. Вы используете прерывание по SPI?
 

pvvx

Активный участник сообщества
В моём случае такт должен быть минимум через 6 нс после выставления CS.
От куда такие скорости у ножек порта со внутренним стробированием в 80MHz (SPI ESP8266)? Больше 20Mbits у UART биты уже сливаются на выходе ног (что говорит о стробировании выходов/входов)...
У регистров портов I/O внутренний CLK похоже ещё ниже (или строб внутренней шины) (мне пока неизвестен регистр переключения клока внутренних шин и периферии портов).
 

Perfer

New member
В моём случае такт должен быть минимум через 6 нс после выставления CS
Это не реально как не крути :)
А сьём с АЦП предполагался 1 мгц
Да и это не реально :)
Ставьте FPGA/DSP и в путь! И про SPI в этом случая я бы забыл и поскорее, в сторону чего-нибудь параллельного или LVDS. Да тут еще вопрос, допустим сняли поток данных на 1Mhz с АЦП, так их ведь еще обработать и передать дальше обычно надо, а тут эта бздюлька ESP8266 может и подвести:)
У регистров портов I/O внутренний CLK похоже ещё ниже (или строб внутренней шины) (мне пока неизвестен регистр переключения клока внутренних шин и периферии портов).
Думаю что нет такого регистра, и очень странно со стороны разработчиков ядра его иметь. Вот регистр настройки делителя частоты I/O скорее всего есть
PS если я Вас правильно понял
 

pvvx

Активный участник сообщества
Думаю что нет такого регистра, и очень странно со стороны разработчиков ядра его иметь. Вот регистр настройки делителя частоты I/O скорее всего есть
PS если я Вас правильно понял
что-то именно то. Т.к. UART идет дискретно и более 20Mbit/s сливаются биты (пропадают если 010101 на осциллографе), (у UART в SDK CLK 80MHz). При этом входы/выходы QSPI работают на 80MHz...
Чип то xtensa-lx106-elf - у него шинами, вроде...
Тут я снял работу bit-а порта http://esp8266.ru/forum/threads/tajmer-i-gpio.75/#post-1266
 
Последнее редактирование:

sharikov

Active member
На основе кода от Perfer запустил HSPI в режиме мастер, научился использовать аппаратную выдачу команд и аппаратный CS. Теперь надо подключить прерывания. Кто нибудь прерывания от HSPI использовал ?
Насколько я понял вектор у SPI и HSPI один. В примере обработчика прерываний SPI проверяют биты 4 и 7 по адресу 0x3ff00020 и если прерывание не от HSPI просто сбрасывают флаги и выходят. Странно. На основном канале висит флэш с прошивкой, к ней идут обращения. SDK что не использует прерывания ?
 

sharikov

Active member
Обнаружил что время работы обработчика прерывания hspi зависит от частоты шины spi задаваемой через регистр SPI_CLOCK.
Код по прерываниям посылает байты по spi. Время между передачами (CS="1") показывает время обработки прерываний.
Код:
/* In the master mode, it is the divider of spi_clk. So spi_clk frequency is 80MHz/(spi_clkdiv_pre+1)/(spi_clkcnt_N+1)
* In the master mode, SPI_CLKCNT_H must be floor((spi_clkcnt_N+1)/2-1)
* In the master mode, SPI_CLKCNT_L must be eqaul to spi_clkcnt_N
*/
#define SPI_CLOCK_10MHZ (((0 & SPI_CLKDIV_PRE) << SPI_CLKDIV_PRE_S) |\
        ((7 & SPI_CLKCNT_N) << SPI_CLKCNT_N_S) |\
        ((3 & SPI_CLKCNT_H) << SPI_CLKCNT_H_S) |\
        ((7 & SPI_CLKCNT_L) << SPI_CLKCNT_L_S))
#define SPI_CLOCK_500KHZ (((2 & SPI_CLKDIV_PRE) << SPI_CLKDIV_PRE_S) |\
        ((63 & SPI_CLKCNT_N) << SPI_CLKCNT_N_S) |\
        ((31 & SPI_CLKCNT_H) << SPI_CLKCNT_H_S) |\
        ((63 & SPI_CLKCNT_L) << SPI_CLKCNT_L_S))
#define SET_SPI_CLOCK_FAST() WRITE_PERI_REG(SPI_CLOCK(HSPI),SPI_CLOCK_10MHZ)
#define SET_SPI_CLOCK_SLOW() WRITE_PERI_REG(SPI_CLOCK(HSPI),SPI_CLOCK_500KHZ)



/*
* передает команду, потом читает read_length
* только запуск операции, прием данных в обработчике прерываний
*/
LOCAL volatile uint8_t mem1=0; // проверка влияния обращений к регистрам на скорость
void hspi_cr_start(const uint8_t cmd, const uint8_t read_length)
{
    uint32_t regvalue;
    uint16_t numberBit;

    if (mem1 ==0)
    {
        regvalue =  SPI_USR_COMMAND  /*| SPI_CS_HOLD */ ;
        if (0 != read_length)
        {
            regvalue |= SPI_USR_MISO;
            numberBit = read_length * 8 -1;
        }
        else
            numberBit = 7;

        // отключаем фазы обмена address, dummy, data_out
        //regvalue &= ~(BIT2 | SPI_USR_ADDR | SPI_USR_DUMMY | SPI_USR_MOSI /*| SPI_USR_MISO | SPI_USR_COMMAND*/); //clear bit 2 see example IoT_Demo
        WRITE_PERI_REG(SPI_USER(HSPI), regvalue);

        //set output buffer length, the buffer is the register"SPI_FLASH_C0"
        WRITE_PERI_REG(SPI_USER1(HSPI),
                ((7 & SPI_USR_MOSI_BITLEN)<<SPI_USR_MOSI_BITLEN_S)|
                ((numberBit & SPI_USR_MISO_BITLEN)<<SPI_USR_MISO_BITLEN_S));

        //SPI_FLASH_USER2 bit28-31 is cmd length,cmd bit length is value(0-15)+1,
        // bit15-0 is cmd value.
        //0x70000000 is for 8bits cmd
        WRITE_PERI_REG(SPI_USER2(HSPI),
                ((7 & SPI_USR_COMMAND_BITLEN) << SPI_USR_COMMAND_BITLEN_S) |
                ((cmd & SPI_USR_COMMAND_VALUE) << SPI_USR_COMMAND_VALUE_S) );
        mem1=1;
    }
    SET_PERI_REG_MASK(SPI_CMD(HSPI), SPI_USR);   // send
}

void  __attribute__((optimize("O2"))) hspi_int_handler(void* *para)
{
    enum spi_rx_state temp_rx_state;  // локальная копия volatile переменной
    uint32_t regvalue;

    if(READ_PERI_REG(0x3ff00020) & BIT4)
    {
        //following 3 lines is to clear isr signal
        CLEAR_PERI_REG_MASK(SPI_SLAVE(SPI), 0x3ff);
        ++spicount;   // проверка работы прерываний
    }
    else
        if (READ_PERI_REG(0x3ff00020) & BIT7)
        { //bit7 is for hspi isr,

            // обнуляем все запросы прерываний spi (хотя использется только SPI_TRANS_DONE)
            CLEAR_PERI_REG_MASK(SPI_SLAVE(HSPI),
                    SPI_TRANS_DONE | SPI_SLV_WR_STA_DONE |
                    SPI_SLV_RD_STA_DONE | SPI_SLV_WR_BUF_DONE |
                    SPI_SLV_RD_BUF_DONE);

            hspi_cr_start(INSTRUCTION_READ_STATUS, 62);
            mcp251x.mcp_rx_state = RXSTATE_FAST_POLLING ;
        }
}

void ICACHE_FLASH_ATTR hspi_init()
{
    uint32 regvalue;

    // gpio mux
    WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105);
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2); // HSPIQ MISO
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2); // HSPID MOSI
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2); // CLK
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2); // CS

#if 0
    WRITE_PERI_REG(SPI_CLOCK(HSPI),
            ((1 & SPI_CLKDIV_PRE) << SPI_CLKDIV_PRE_S) |
            ((3 & SPI_CLKCNT_N) << SPI_CLKCNT_N_S) |  /*In the master mode, it is the divider of spi_clk. So spi_clk frequency is 80MHz/(spi_clkdiv_pre+1)/(spi_clkcnt_N+1)*/
            ((1 & SPI_CLKCNT_H) << SPI_CLKCNT_H_S) |  /*In the master mode, it must be floor((spi_clkcnt_N+1)/2-1)*/
            ((3 & SPI_CLKCNT_L) << SPI_CLKCNT_L_S));  /*In the master mode, it must be eqaul to spi_clkcnt_N*/
#else
    SET_SPI_CLOCK_FAST();
#endif

    // Disable the interrupt
    CLEAR_PERI_REG_MASK(SPI_SLAVE(HSPI),
            SPI_TRANS_DONE_EN | SPI_SLV_WR_STA_DONE_EN |
            SPI_SLV_RD_STA_DONE_EN | SPI_SLV_WR_BUF_DONE_EN |
            SPI_SLV_RD_BUF_DONE_EN     );
    //register level2 isr function, which contains spi, hspi and i2s events
    ETS_SPI_INTR_ATTACH(hspi_int_handler, NULL);
    //enable level2 isr, which contains spi, hspi and i2s events
    ETS_SPI_INTR_ENABLE();
}
Результаты:
SPI_CLOCK_500KHZ (((2 & SPI_CLKDIV_PRE) << SPI_CLKDIV_PRE_S) |\
((63 & SPI_CLKCNT_N) << SPI_CLKCNT_N_S) |\
((31 & SPI_CLKCNT_H) << SPI_CLKCNT_H_S) |\
((63 & SPI_CLKCNT_L) << SPI_CLKCNT_L_S))
Такт spi =2 .4us
Длительность обработки прерывания = 19us, период посылок =60us

SPI_CLOCK_10MHZ (((0 & SPI_CLKDIV_PRE) << SPI_CLKDIV_PRE_S) |\
((7 & SPI_CLKCNT_N) << SPI_CLKCNT_N_S) |\
((3 & SPI_CLKCNT_H) << SPI_CLKCNT_H_S) |\
((7 & SPI_CLKCNT_L) << SPI_CLKCNT_L_S))
Такт spi = 100ns
Длительность обработки прерывания = 3us (похоже это минимум), период посылок = 55 us


Вопрос: КАК такое возможно ?
И что сделать чтобы при низкой частоте spi получить время обработки прерывания такое же как при высокой ?
 

sharikov

Active member
Добавил строб на входе и выходе обработчика прерывания. Время одинаковое при быстрой и медленной скорости spi. Задержка возникает где-то в другом месте. Надо искать дальше.
 

sharikov

Active member
Подробнее:
Код:
скорость spi SLOW (T=2.4us)
                   : ___             _________________
CS                 :    \___________/                 \_______________________
                   :                        ________
строб в прерывании : ______________________/        \____________________
                                    |      |           |
                                    | 16us | 2.6us     |
                                           |   2us  |
от перехода CS=0->1 (окончание передачи spi) до входа в обработчик прерывания spi прошло 16us,
от входа в обработчик прерывания spi до перехода CS=1->0 (начало передачи spi) прошло 2.6us



скорость spi FAST (T=0.1us)
                   : ___             ____________
CS                 :    \___________/            \_______________________
                   :                        ________
строб в прерывании : ______________________/        \____________________
                                    |      |     |
                                    |  2us |1.5us|
                                           |   2us  |
                                   
от перехода CS=0->1 (окончание передачи spi) до входа в обработчик прерывания spi прошло 2us,
от входа в обработчик прерывания spi до перехода CS=1->0 (начало передачи spi) прошло 1.5us
Видно что тормоза при низкой скорости spi возникают из-за непонятной задержки после окончания обмена spi.
 

pvvx

Активный участник сообщества
Видно что тормоза при низкой скорости spi возникают из-за непонятной задержки после окончания обмена spi.
Всегда после передачи ждет N тактов SPI-CLK и выставляет готовность (которая уже вызывает прерывание)?
Интересно сколько этот N и есть ли возможность его изменения в регистрах управления?

Оно(?):
REG_USR_DUMMY_CYCLELEN The length in spi_clk cycles of "dummy" phase. The register value shall be (cycle_num-1)
 
Последнее редактирование:

sharikov

Active member
Всегда после передачи ждет N тактов SPI-CLK и выставляет готовность (которая уже вызывает прерывание)?
Интересно сколько этот N и есть ли возможность его изменения в регистрах управления?
Оно(?):
REG_USR_DUMMY_CYCLELEN The length in spi_clk cycles of "dummy" phase. The register value shall be (cycle_num-1)
При начале передачи ждет 1 такт spi перед выставлением cs в "0".
Что происходит после передачи сказать трудно потому что сигнал готовности spi недоступен для наблюдения снаружи.
Однако время пропорционально тактовой частоте spi
Измерения длительности высокого уровня cs:
20us при периоде клока spi 2,4us
8.4us при периоде клока spi 0,8us
Время работы обработчика прерывания 1,5us
"лишняя" задержка получается
20-1,5-2,4 = 16,1us (6,7 бита)
8,4-1,5-0,8 = 6,1us (7,6 бита)

Фазу DUMMY я не включаю, попробовал изменить REG_USR_DUMMY_CYCLELEN - никаких изменений. Работают COMMAND и MISO. Разрывов в обмене или лишних импульсов нет - сколько заказал в настройках контроллера столько и выдается.
Проверил влияние количества байт в фазе MISO на задержку - не влияет (была мысль что внутри контроллер всегда работает с 32 битами данных)
 

pvvx

Активный участник сообщества
sharikov - с вашими настройками
#define SPI_CLOCK_500KHZ (((2 & SPI_CLKDIV_PRE) << SPI_CLKDIV_PRE_S) |\
((63 & SPI_CLKCNT_N) << SPI_CLKCNT_N_S) |\
((31 & SPI_CLKCNT_H) << SPI_CLKCNT_H_S) |\
((63 & SPI_CLKCNT_L) << SPI_CLKCNT_L_S))

У меня так:
3.708us.gif
при запуске в iram (показывает счетчик прерываний в 10 секунд):

Start RAM porg
CLK = 80 MHz, Set CLK 160 MHz
HSPI ints 122511
HSPI ints 122511
HSPI ints 122512
user code done


Проверил неописанные биты. Нашлось только это:

// SPI_CTRL2:
#define SPI_CTRL2_CLK_HDIV2 (BIT(12)) // Уменьшает длительность "1" на CLK в 2 раза
#define SPI_CTRL2_CLK_HDIV4 (BIT(15)) // Уменьшает длительность "1" на CLK в 4 раза

// SPI_PIN:
//#define SPI_PIN_CLK0 (BIT(5)) // Выход CLK = "0"
#define SPI_PIN_CS_INV (BIT(6)) // инверсия сигнала CS
#define SPI_PIN_CS_CLK (BIT(11)) // сигнал на CS = CLK
#define SPI_PIN_MOSI_MISO (BIT(16)) // сигнал MISO выводится на MOSI
//#define SPI_PIN_CLK1_CS0 (BIT(19)) // CS = "0", CLK = "1"
#define SPI_PIN_CLK_INV (BIT(29)) // инверсия сигнала CLK
#define SPI_PIN_CS0 (BIT(30)) // CS = "0"
//#define SPI_PIN_CS_0 (BIT(31)) // CS = "0"

Но главное это (допишите в "spi_register.h" от esp_iot_rtos_sdk) :
Код:
#define SPI_CTRL1(i)                (REG_SPI_BASE(i) + 0xC)   // default  HSPI = 0x5fff0120 / QSPI = 0x5fff0120
#define SPI_CS_DELAY_INT                   0x0000000F // кол-во тактов SPI-CLK задержки до выставления прерывания и готовности
#define SPI_CS_DELAY_INT_S               28
У flash в SDK тоже стоит задержка в +5 тактов SPI :(

Проект в UDK с загрузкой в IRAM, чтобы не точить flash (но BIOS ESP8266 дает неверную настройку PLL, от этого и 78440 Baud и т.д. надо перепрограммировать PLL) :
 

Вложения

Последнее редактирование:

sharikov

Active member
sharikov - с вашими настройками
#define SPI_CLOCK_500KHZ (((2 & SPI_CLKDIV_PRE) << SPI_CLKDIV_PRE_S) |\
((63 & SPI_CLKCNT_N) << SPI_CLKCNT_N_S) |\
((31 & SPI_CLKCNT_H) << SPI_CLKCNT_H_S) |\
((63 & SPI_CLKCNT_L) << SPI_CLKCNT_L_S))

У меня так:
Почем у вас с указанными настройками клок 3,7us ? должно быть 2,4 как по расчету так и фактически.

Проверил неописанные биты. Нашлось только это:
// SPI_CTRL2:
#define SPI_CTRL2_CLK_HDIV2 (BIT(12)) // Уменьшает длительность "1" на CLK в 2 раза
#define SPI_CTRL2_CLK_HDIV4 (BIT(15)) // Уменьшает длительность "1" на CLK в 4 раза
А если установить оба бита ?


// SPI_PIN:
//#define SPI_PIN_CLK0 (BIT(5)) // Выход CLK = "0"
#define SPI_PIN_CS_INV (BIT(6)) // инверсия сигнала CS
#define SPI_PIN_CS_CLK (BIT(11)) // сигнал на CS = CLK
#define SPI_PIN_MOSI_MISO (BIT(16)) // сигнал MISO выводится на MOSI
//#define SPI_PIN_CLK1_CS0 (BIT(19)) // CS = "0", CLK = "1"
#define SPI_PIN_CLK_INV (BIT(29)) // инверсия сигнала CLK
#define SPI_PIN_CS0 (BIT(30)) // CS = "0"
//#define SPI_PIN_CS_0 (BIT(31)) // CS = "0"

SPI_PIN_MOSI_MISO - куда при этом идет MOSI ? они меняются местами что ли?

Но главное это (допишите в "spi_register.h" от esp_iot_rtos_sdk) :
Код:
#define SPI_CTRL1(i)                (REG_SPI_BASE(i) + 0xC)   // default  HSPI = 0x5fff0120 / QSPI = 0x5fff0120
#define SPI_CS_DELAY_INT                   0x0000000F // кол-во тактов SPI-CLK задержки до выставления прерывания и готовности
#define SPI_CS_DELAY_INT_S               28
У flash в SDK тоже стоит задержка в +5 тактов SPI
%$##$%*#@!!!!!!
Гребаный Espressif !
Там нет случаем бита разрешения задержки до выставления прерывания ?

Добавил в инициализацию
CLEAR_PERI_REG_MASK(SPI_CTRL1(HSPI), SPI_CS_DELAY_INT << SPI_CS_DELAY_INT_S);
CS стал 6,6us. Минимальное время это 3us. Теперь лишняя задержка 3,6us. Отнимаем 1 такт при старте остается 1,2us. Те. 0,5 такта неустранимая задержка до готовности в конце обмена.

Интересно зачем они добавили задержку на flash.

Откуда вы берете недокументированные биты ?
 

pvvx

Активный участник сообщества
А если установить оба бита ?
Будут узкие палки в единичку на осциле :)
SPI_PIN_MOSI_MISO - куда при этом идет MOSI ? они меняются местами что ли?
Похоже - есть и документированный аналогичный бит.
CS стал 6,6us. Минимальное время это 3us. Теперь лишняя задержка 3,6us. Отнимаем 1 такт при старте остается 1,2us. Те. 0,5 такта неустранимая задержка до готовности в конце обмена.
Там всегда CS подымается на 2 такта SPI + та задержка в указанных битах.
Интересно зачем они добавили задержку на flash.
Чтобы работали отстойные китайские Flash.
Откуда вы берете недокументированные биты ?
Эти биты оказались документированы. В параллельной теме описал.
 

pvvx

Активный участник сообщества
Почем у вас с указанными настройками клок 3,7us ? должно быть 2,4 как по расчету так и фактически.
По тому, что запускаю из IRAM, без SDK. BIOS ROM в чипе ESP8266, как и всё от Espressif, имеет тучу ошибок и настраивает внутренние CLK неверно. SDK, при загрузке, исправляет это и многие другие функции...
 

sharikov

Active member
Решил проверить сообщение http://www.esp8266.com/viewtopic.php?f=13&t=2427&start=10#p15607
Компилятор udk, в опции компилятора добавлено -mno-serialize-volatile
Код
Код:
#define GET_CCOUNT(x) __asm__ __volatile__("rsr %0, ccount" : "=r"(x))

uint32_t calcdelta(uint32_t v1, uint32_t v2)
{
    return (v2 >= v1)? v2-v1 : v1-v2;
}

void __attribute__((optimize("O2"))) write_test(uint32_t i)
{
    uint32_t j;
    uint32_t t1, t2;
    volatile uint32_t* ptr;


    ets_uart_printf("\n--------\n");
    os_delay_us(100000);

    do
    {
        ets_uart_printf("\twithout MEMW\n");
        os_delay_us(100000);
        ptr=(volatile uint32_t *)SPI_W0(HSPI);
        __asm__ __volatile__("memw" : : : "memory");
        ets_intr_lock();
        GET_CCOUNT(t1);
        //t1=get_ccount();
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        //__asm__ __volatile__("memw" : : : "memory");
        GET_CCOUNT(t2);
        //t2=get_ccount();
        ets_intr_unlock();
        ets_uart_printf(" 8 writes: time= %d\n", calcdelta(t1, t2));
        os_delay_us(100000);

        ptr=(volatile uint32_t *)SPI_W0(HSPI);
        __asm__ __volatile__("memw" : : : "memory");
        ets_intr_lock();
        GET_CCOUNT(t1);
        //t1=get_ccount();
        /*for (j=0; j<16; j++)
            ptr[j]= i;*/
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        //__asm__ __volatile__("memw" : : : "memory");
        GET_CCOUNT(t2);
        //t2=get_ccount();
        ets_intr_unlock();
        ets_uart_printf("16 writes: time= %d\n", calcdelta(t1, t2));
        os_delay_us(100000);


        ets_uart_printf("\twith MEMW\n");
        os_delay_us(100000);
        ptr=(volatile uint32_t *)SPI_W0(HSPI);
        __asm__ __volatile__("memw" : : : "memory");
        ets_intr_lock();
        GET_CCOUNT(t1);
        //t1=get_ccount();
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        __asm__ __volatile__("memw" : : : "memory");
        GET_CCOUNT(t2);
        //t2=get_ccount();
        ets_intr_unlock();
        ets_uart_printf(" 8 writes: time= %d\n", calcdelta(t1, t2));
        os_delay_us(100000);

        ptr=(volatile uint32_t *)SPI_W0(HSPI);
        __asm__ __volatile__("memw" : : : "memory");
        ets_intr_lock();
        GET_CCOUNT(t1);
        //t1=get_ccount();
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        __asm__ __volatile__("memw" : : : "memory");
        GET_CCOUNT(t2);
        //t2=get_ccount();
        ets_intr_unlock();
        ets_uart_printf("16 writes: time= %d\n\n", calcdelta(t1, t2));
        os_delay_us(100000);
    }
    while (--i);
    ets_uart_printf("\n--------\n");
}
Результаты:
Код:
--------
   without MEMW
 8 writes: time= 22
16 writes: time= 93
   with MEMW
 8 writes: time= 75
16 writes: time= 172

   without MEMW
 8 writes: time= 22
16 writes: time= 93
   with MEMW
 8 writes: time= 75
16 writes: time= 172

   without MEMW
 8 writes: time= 22
16 writes: time= 93
   with MEMW
 8 writes: time= 75
16 writes: time= 172

   without MEMW
 8 writes: time= 22
16 writes: time= 93
   with MEMW
 8 writes: time= 75
16 writes: time= 172


--------
Какие будут мысли ?
 

pvvx

Активный участник сообщества
Компилятор udk, в опции компилятора добавлено -mno-serialize-volatile
Какие будут мысли ?
Выкинуть компилятор UDK на помойку. Он ваши *ptr++= i раскладывает на load в регистр 32-х битный адрес по смещению, потом запись по этому регистру со смещением ноль. По другому он не умеет. И вообще не умеет работать со смещениями - адрес в регистре + смещение. Только нулевое смещение. Кто-то задал такие параметры транслятору, чтобы вместо 3 байт *ptr++ выходило 10 байт с загрузкой адреса каждый раз. :mad:
Код:
void write_test(int i)
{
        volatile unsigned int * ptr=(volatile unsigned int *)0x60000240;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        *ptr++= i;
        __asm__ __volatile__("memw" : : : "memory");
}
xtensa компилятор:
Код:
60000874 <write_test>:
60000874:    ff4d31           l32r    a3, 600005a8 <_stext+0x44>
60000877:    0020c0           memw
6000087a:    106322           s32i    a2, a3, 64
6000087d:    0020c0           memw
60000880:    116322           s32i    a2, a3, 68
60000883:    0020c0           memw
60000886:    126322           s32i    a2, a3, 72
60000889:    0020c0           memw
6000088c:    136322           s32i    a2, a3, 76
6000088f:    0020c0           memw
60000892:    146322           s32i    a2, a3, 80
60000895:    0020c0           memw
60000898:    156322           s32i    a2, a3, 84
6000089b:    0020c0           memw
6000089e:    166322           s32i    a2, a3, 88
600008a1:    0020c0           memw
600008a4:    176322           s32i    a2, a3, 92
600008a7:    0020c0           memw
600008aa:    186322           s32i    a2, a3, 96
600008ad:    0020c0           memw
600008b0:    196322           s32i    a2, a3, 100
600008b3:    0020c0           memw
600008b6:    1a6322           s32i    a2, a3, 104
600008b9:    0020c0           memw
600008bc:    1b6322           s32i    a2, a3, 108
600008bf:    0020c0           memw
600008c2:    1c6322           s32i    a2, a3, 112
600008c5:    0020c0           memw
600008c8:    1d6322           s32i    a2, a3, 116
600008cb:    0020c0           memw
600008ce:    1e6322           s32i    a2, a3, 120
600008d1:    0020c0           memw
600008d4:    1f6322           s32i    a2, a3, 124
600008d7:    0020c0           memw
600008da:    f00d         ret.n
С отключенным memw:
Код:
60000874 <write_test>:
60000874:    ff4d31           l32r    a3, 600005a8 <_stext+0x44>
60000877:    106322           s32i    a2, a3, 64
6000087a:    116322           s32i    a2, a3, 68
6000087d:    126322           s32i    a2, a3, 72
60000880:    136322           s32i    a2, a3, 76
60000883:    146322           s32i    a2, a3, 80
60000886:    156322           s32i    a2, a3, 84
60000889:    166322           s32i    a2, a3, 88
6000088c:    176322           s32i    a2, a3, 92
6000088f:    186322           s32i    a2, a3, 96
60000892:    196322           s32i    a2, a3, 100
60000895:    1a6322           s32i    a2, a3, 104
60000898:    1b6322           s32i    a2, a3, 108
6000089b:    1c6322           s32i    a2, a3, 112
6000089e:    1d6322           s32i    a2, a3, 116
600008a1:    1e6322           s32i    a2, a3, 120
600008a4:    1f6322           s32i    a2, a3, 124
600008a7:    0020c0           memw
600008aa:    f00d         ret.n
UDK (c отключенным memw):
Код:
40100000 <_stext>:
40100000:    000240           excw
40100003:    024460           excw
40100006:    486000           excw
40100009:    600002           l8ui    a0, a0, 96
4010000c:    024c         movi.n    a2, 64
4010000e:    506000           witlb    a0, a0
40100011:    600002           l8ui    a0, a0, 96
40100014:    000254           excw
40100017:    025860           excw
4010001a:    5c6000           excw
4010001d:    600002           l8ui    a0, a0, 96
40100020:    000260           excw
40100023:    026460           excw
40100026:    686000           excw
40100029:    600002           l8ui    a0, a0, 96
4010002c:    026c         movi.n    a2, -32
4010002e:    706000           excw
40100031:    600002           l8ui    a0, a0, 96
40100034:    000274           excw
40100037:    027860           excw
4010003a:    7c6000           excw
4010003d:    600002           l8ui    a0, a0, 96

40100040 <write_test>:
40100040:    fff031           l32r    a3, 40100000 <_stext>
40100043:    0329         s32i.n    a2, a3, 0
40100045:    ffef31           l32r    a3, 40100004 <_stext+0x4>
40100048:    0329         s32i.n    a2, a3, 0
4010004a:    ffef31           l32r    a3, 40100008 <_stext+0x8>
4010004d:    0329         s32i.n    a2, a3, 0
4010004f:    ffef31           l32r    a3, 4010000c <_stext+0xc>
40100052:    0329         s32i.n    a2, a3, 0
40100054:    ffef31           l32r    a3, 40100010 <_stext+0x10>
40100057:    0329         s32i.n    a2, a3, 0
40100059:    ffee31           l32r    a3, 40100014 <_stext+0x14>
4010005c:    0329         s32i.n    a2, a3, 0
4010005e:    ffee31           l32r    a3, 40100018 <_stext+0x18>
40100061:    0329         s32i.n    a2, a3, 0
40100063:    ffee31           l32r    a3, 4010001c <_stext+0x1c>
40100066:    0329         s32i.n    a2, a3, 0
40100068:    ffee31           l32r    a3, 40100020 <_stext+0x20>
4010006b:    0329         s32i.n    a2, a3, 0
4010006d:    ffed31           l32r    a3, 40100024 <_stext+0x24>
40100070:    0329         s32i.n    a2, a3, 0
40100072:    ffed31           l32r    a3, 40100028 <_stext+0x28>
40100075:    0329         s32i.n    a2, a3, 0
40100077:    ffed31           l32r    a3, 4010002c <_stext+0x2c>
4010007a:    0329         s32i.n    a2, a3, 0
4010007c:    ffed31           l32r    a3, 40100030 <_stext+0x30>
4010007f:    0329         s32i.n    a2, a3, 0
40100081:    ffec31           l32r    a3, 40100034 <_stext+0x34>
40100084:    0329         s32i.n    a2, a3, 0
40100086:    ffec31           l32r    a3, 40100038 <_stext+0x38>
40100089:    0329         s32i.n    a2, a3, 0
4010008b:    ffec31           l32r    a3, 4010003c <_stext+0x3c>
4010008e:    0329         s32i.n    a2, a3, 0
40100090:    0020c0           memw
40100093:    f00d         ret.n
И то что он наплодил в _stext констант - они не объединяются, т.к. каждая эксклюзивная. А у xtensa компилятора, при использовании базовых адресов со смещением константы объединяются для разных процедур. Даже если найдет константу, которая лежит в диапазоне использования регистра со смещением, то и её возьмет, а поправит смещение при обращении. Как итог - в UDK потеря не 9 байт, а более, на каждое обращение к регистру (к примеру в сравнении с xtensa).
Аналогично и при обращении к полям структур. Он будет выеживаться - прибавлять, вычитать из адресного регистра, но потом только по нулевому смещению возьмет данные. Ужас просто - плодит кучу команд вместо 3-х байтной обращения по регистру со смешением.... Кто-то спецом навредничал, чтобы не использовали бесплатный GCC и распространяет такой кривой компиллер... Внешними опциями это не удалось исправить. В xtensa можно и так и сяк крутить директивами в опциях компилятору...
Вполне серьезная причина выкинуть кривой UDK компилятор.
 
Последнее редактирование:
Сверху Снизу