Перейти к содержанию
    

Чудеса оптимизации кода GCC-ARM

Всем привет!!

1. Из инструментов при себе имею STM32F407 - Discovery, CooCox и осцилл (2ch x 100MHz).

2. Задача: захватить 16 разрядные данные с ADC (4 канала) по протоколу SPI при тактовой частоте 5MHz (CLK).

3. Решение: программная реализация протокола ввиду наличия лишь 3_ёх SPI в STM32F407 и некоторых отклонений входного потока от стандарта SPI (исходные условия), при тактовой в 168 MHz хватает впритык и только с оптимизатором -O3.

Программка выглядит так:

// Соответствие функций входных линий и ножек порта:

#define        SCK 1<<4
#define        NSS 1<<5
#define        MOSI_SPI0 1<<0
#define        MOSI_SPI1 1<<1
#define        MOSI_SPI2 1<<2
#define        MOSI_SPI3 1<<3

#define        N_DATA  10        // количество данных (выборок)

uint16_t     DATA_0[N_DATA];        // Массив отсчетов для 0_го канала 
uint16_t     DATA_1[N_DATA];        // Массив отсчетов для 1_го канала 
uint16_t     DATA_2[N_DATA];        // Массив отсчетов для 2_го канала 
uint16_t     DATA_3[N_DATA];        // Массив отсчетов для 3_го канала 

uint16_t       DATA_temp[16];        // Буфера для сохранения состояний порта, (для повышения быстродействия)

uint16_t     N_Data;            // Счетчик/номер текущего отсчета в периоде захвата или выдачи данных
uint8_t        i;            // Само собой разумеется

void SSS(void)    // Ф-ия преобразования сохраненных состояний порта DATA_temp[i] в отчеты DATA_0-3[N_Data]
    {

            i=0;
            DATA_0[N_Data] = 0;
            while(i<16)
            for(i=0;i<16;i++)
            {
           if((DATA_temp[i] & MOSI_SPI0 == MOSI_SPI0) )
                DATA_0[N_Data] |= (0x8000>>i);
            i++;
            }
          i=0;
            DATA_1[N_Data] = 0;
            while(i<16)
            {
            if((DATA_temp[i] & MOSI_SPI1) == MOSI_SPI1)
                DATA_1[N_Data] |= (0x8000>>i);
            i++;
            }
            i=0;
            DATA_2[N_Data] = 0;
            while(i<16)
            {
            if((DATA_temp[i] & MOSI_SPI2) == MOSI_SPI2)
                DATA_2[N_Data] |= (0x8000>>i);
            i++;
            }
            i=0;
            DATA_3[N_Data] = 0;
            while(i<16)
            {
            if((DATA_temp[i] & MOSI_SPI3) == MOSI_SPI3)
                DATA_3[N_Data] |= (0x8000>>i);
            i++;
            }

    }

void Flag(void)    // Ф-ия "Флаг" - "контрольная точка"
    {
    GPIOD->BSRRL =(1<<14);        // set
    GPIOD->BSRRH =(1<<14);        // clr
    }

void ProgramSPI(void)    // Программная реализация SPI где D0-MOSI(SPI0)|D1-MOSI(SPI1)|D2-MOSI(SPI2)|D3-MOSI(SPI3)   D4-SCK    D5-NSS
{
    N_Data=0;
    {
    while ((GPIOD->IDR & NSS) == 0)// NSS==0 (ждем начала выполнения преобразования ADC)
        {}

    while ((GPIOD->IDR & NSS) == NSS)// NSS==1 (ждем окончания выполнения преобразования ADC)
        {}
//    Flag();
    
// Процесс захвата данных (SPI0-3):
    i=0;
    while (i<16)
        {
        while ((GPIOD->IDR & (NSS|SCK)) != SCK)    // SCK!=1|NSS==0
        {}
        Flag();
        DATA_temp[i]=GPIOD->IDR;
        Flag();
        while ((GPIOD->IDR & (NSS|SCK)) != 0)    // SCK!=0|NSS==0
        {}
        //    Flag();
        i++;
        }
SSS();

// Проверка принятых байтов:

    if((DATA_0[N_Data]!= 0xA38C) | (DATA_1[N_Data]!= 0xA38C) | (DATA_2[N_Data]!= 0xA38C) | (DATA_3[N_Data] != 0xA38C))
        {
        Flag();
        }
    N_Data++;            // Инкремент счетчика отсчетов - подготовка к следующему полуслову
    if(N_Data==N_DATA)
        return;            // Переполнение буфера данных, возврат в main()
    }
}

int main(void)                // Очень странная функция !!!;)
{
    SystemInit();
    PortD_Init();
    while(1)
    {
        ProgramSPI();
    }
}

 

В общем все работает, ф-ия Flag() - контрольная точка (выводит строб на порт который контролируется осциллографом), ProgramSPI() сохраняет состояние ножек порта в нужное время 16 раз, ф-ия SSS() преобразует данные, но есть одно НО, при подключении вызова ф-ии SSS() компилятор отказывается оптимизировать код в сторону быстродействия, т.е. другими словами - в случае применения где либо этих массивов, скорости не хватает и появляются ошибки чтения.

Пытался применять квалификаторы типа volatile и restrict, не помогло.

Вопрос: как заставить компилятор оптимизировать или не оптимизировать нужные мне куски кода??

Спасибо :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Посмотрите на мой примерчик:

//=============================================================================
// With SPI Modes 0 and 3, data is always latched in on the rising edge of SCK and always output on the falling edge of SCK
//=============================================================================
uint_fast8_t spi_transfer(uint_fast8_t data)
{
    for (int_fast8_t i=0; i<8; i++)
    {
        SPI_SCK(0);

        SPI_MOSI(data & (1<<7));        // write data at the begin of cycle 
        spi_delay();

        SPI_SCK(1);
        spi_delay();
        data = (data<<1) | SPI_MISO();  // read data at the end of cycle 
    }

    return (data & 0xff);               // clr possible garbage
}

для отправки 16-ти битных данных легко модифицируется

+ следующие макросы через bitband реализуйте, если у F4 порты в его доступе...

SPI_SCK(X)
SPI_MOSI(X)
SPI_MISO()

Не будет хватать скорости - разверните циклы...

А вообще ваш код меня сильно-сильно коробит...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Может я чего не пойму, но у меня только приемник (slave), т.е. я не управляю, а слежу за состоянием SCK и NSS, и параллельно снимаю 4 потока данных,

И не пойму, как реализовать проверку состояния порта через bitband (Ваш SPI_MISO())???

P.S. Все те угрюмые навороты в моей проге только по причине нехватки скорости (проверка состояния входа через маску - ~40 ns) , как через bitband проверить, ведь регистр BSRR только для установки и сброса !??

Изменено пользователем Zol'berg

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

И не пойму, как реализовать проверку состояния порта через bitband (Ваш SPI_MISO())???

А регистр IDR на что?

Он у F4xx лежит в bitband области?

У F1xx лежит, что очень удобно.

 

...у меня только приемник (slave), т.е. я не управляю, а слежу за состоянием SCK и NSS...
С этого и стоило начинать...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В функции SSS() вполне достаточно одного цикла на все 4 канала :) И как уже советовали, этот цикл можно и развернуть (хотя это и компилятор может сделать).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну, к регистру IDR необходимо применять маску, я так и делаю, а это время, а 5 MHz SCK это много! Когда я работал на Асме на AVR было четко и ясно, за 2 такта я мог опросить любой бит одной командой, а тут за 8 еле еле, и C компилятор живет своей жизнью, хочет разгоняет, а хочет тормозит! И что делать?

 

 

В функции SSS() вполне достаточно одного цикла на все 4 канала

Да, ты прав, наворотил я здесь :blush:.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну, к регистру IDR необходимо применять маску, я так и делаю

Вы не понимаете что такое bitband + Карта памяти

Там маски не нужны будут. Сразу считываешь 0 или 1 и пакуешь в байты

data = (data<<1) | SPI_MISO();

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, я все равно никак не пойму что такое и как работает bitband применительно к СЧИТЫВАНИЮ порта, с регистром BSRR(BSRRL и BSRRH) я знаком! Если я не ошибаюсь bitband это метод установки/сброса и никак не касается считывания!?

Ну, а SPI_MISO() из чего сделано?

Изменено пользователем Zol'berg

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вы читать умеете? Я же дал вам ссылки - там всё написано.

Bitband вообще никак не связан ни счем.

Это просто способ атомарно прочитать или записать один бит.

И не важно где этот бит лежит. Хоть в регистре BSSR хоть IDR, хоть где...

 

Ну, а SPI_MISO() из чего сделано?

У кого как... У меня для cm3 из этого:

static __inline volatile uint32_t* bb_bit_address(volatile uint32_t* p, uint_fast8_t bit)
{
    return ( (volatile uint32_t*) (((uint32_t)p & 0xf0000000UL) | 0x02000000UL
              +(((uint32_t)p & 0x000fffffUL)<<5) | (bit<<2)));
}

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3. Решение: программная реализация протокола ввиду наличия лишь 3_ёх SPI в STM32F407 и некоторых отклонений входного потока от стандарта SPI (исходные условия), при тактовой в 168 MHz хватает впритык и только с оптимизатором -O3.

может имеет смысл попробовать приспособить USART в синхронном режиме? или совсем никак?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...