Jump to content

    
KnightIgor

CubeIDE: оптимизация выше -O0 нарушает работу программы

Recommended Posts

Привет, коллеги.
Немного предыстории.

 

С CMx начал работать еще с самого начала их появления (конец нулевых). Когда не было еще всяких HAL, а CMSIS в зародыше, написал свою HAL, которой пользуюсь до сих пор, причем и для CM3, и для CM0. До поры работал под KEIL, теперь частично и в STMCubeIDE, то есть gcc.

 

В моем HAL есть код типа FIFO, работающий с UART. Это была и есть оcнова основ: всякие терминалы, DMX, поддержка других протоколов. Короче, коду сто лет в обед, проверен многократно на STM32 CM3/CM0, EnergyMicro/Silabs EFM32, ATMEL SAM3D. Я в код уже и не заглядываю. И вот нонче, завершив более-менее проект под SMTCubeIDE, где моя HAL используется как странслированная в библиотеку *.a,  я собрал его release, при этом подключил HAL.a тоже release, оптимизация -O2. И тут начались непонятки: протокол по UART не работает толком. printf() вообще ведет себя странно: спорадически(!) происходит как бы добавление символов. Например, вместо "V1.0" выводится "V.1.0", вместо, скажем, FPxA - FxPxA, то есть какой-нибудь символ "копируется" на две позиции вперед. Если подключить к проекту release мою HAL.a debug (-O0), то непонятки уходят. Интересно, что HAL.lib под KEIL, тоже с -O2, работает в проектах под KEIL одинаково хорошо с HAL.lib -O0. То есть, проблема только под STMCubeIDE gcc и -O2.

 

Есть у кого идеи/опыт? Ага, STMCubeIDE 1.6.1.

Edited by KnightIgor

Share this post


Link to post
Share on other sites
1 hour ago, KnightIgor said:

Я в код уже и не заглядываю

С высокой вероятностью проблема в нем - симптомы-то указывают на некорректную работу FIFO.

Share this post


Link to post
Share on other sites
5 hours ago, KnightIgor said:

Есть у кого идеи/опыт? Ага, STMCubeIDE 1.6.1.

Недавно понял, что мой кольцевой буфер, написанный тоже в конце нулевых, не совсем хорошо работает на кортексах. А всё дело во вложенных прерываниях. Ведь я раньше работал с AVR/PIC, где такого не было. Вот и для армов написал буфер, где при работе из прерывания считал, что операция 100%-атомарна. Ведь другое-то прерывание не возникнет. Так эта ошибка и тянулась с десяток лет, будучи замаскированной. Пока недавно не выплыла) Может быть у вас что-то подобное?

Share this post


Link to post
Share on other sites
9 hours ago, aaarrr said:

С высокой вероятностью проблема в нем - симптомы-то указывают на некорректную работу FIFO.

Это явно так, но как оптимизация может порушить код, который до этого работал годы?! Как я указывал, оптимизация под KEIL не портила результат, горя не знал, только под gcc сейчас. Я даже не представляю, где копать.
@haker_fox: я изначально писал под CM3, предпринял соответствующие меры для атомарности (даже описывал здесь на формуме в обсуждении подобной темы, как, но очень давно).

Share this post


Link to post
Share on other sites
32 minutes ago, KnightIgor said:

Это явно так, но как оптимизация может порушить код, который до этого работал годы?!

Поскольку оптимизация не нарушает логику исходно работающего текста, проблема может лежать чуть глубже. Например в особенностях работы какой-либо периферии для которой один компилятор генерировал одну последовательность команд, а другой - другую. Именились времена выполнения и, баг! Ура) Не знаю, что посоветовать. Если есть вомзожность лучше поглядеть листинг на предмет очевидных ляпов компилятора. Но листинги обычно большие. Поэтому добавьте отладку, выводите куда-нибудь переменные буфера: хвост, голову, сам буфер. В общем, трудно что-либо посоветовать конкретное. Ведь и проблема описана очень размыто.

Share this post


Link to post
Share on other sites
11 hours ago, KnightIgor said:

Короче, коду сто лет в обед, проверен многократно на STM32 CM3/CM0, EnergyMicro/Silabs EFM32, ATMEL SAM3D. Я в код уже и не заглядываю. 

.....

Есть у кого идеи/опыт? Ага, STMCubeIDE 1.6.1.

Опыт есть такой: даже в старом и проверенном коде случаются ошибки. А что работало до сих пор- так просто звезды так сходились, а теперь вот по-другому сошлись. У меня бывало, что при переходе на другой уровень оптимизации или на другой компилятор код переставал работать- это явно косяк исходников.

Еще версия- что-то изменилось в реализации камня, а Ваши исходники по-старинке это обрабатывают, и это "по старинке" уже не прокатывает.

Кстати, про зайцев FIFO- это неактуально, DMA гораздо круче. В STM довольно гибкое конфигурирование DMA, можно многое туда переложить и не отвлекаться "по мелочам" на каждый байтик.

Share this post


Link to post
Share on other sites
1 час назад, KnightIgor сказал:

Это явно так, но как оптимизация может порушить код, который до этого работал годы?!

А как годы не проявлялся баг, найденный мной недавно в IAR? (https://electronix.ru/forum/index.php?app=forums&module=forums&controller=topic&id=159438)

Тоже работало годы. И не у одного человека (как в Вашем случае), а у множества пользователей IAR во множестве разного кода. И не проявлялось. Или проявлялось, но причина не понималась.

Так же и у Вас - не проявлялось или проявлялось, но списывалось на другие причины.

 

PS: Вы должны быть рады, что баг проявился и его наконец-то можно найти и исправить.  :smile:   Гораздо хуже, когда девайс изредка глючит по непонятной причине....

Потому и советуют: тестить код на всех возможных уровнях оптимизации. А если глючит хоть на каком-то уровне - искать баги.

 

Не бывает безглючного кода. Бывает только код с ещё ненайденными багами.  :wink:

Share this post


Link to post
Share on other sites
11 hours ago, KnightIgor said:

Я в код уже и не заглядываю.

Моя история - ошибка с временем жизни в пять лет. За это время главный наладчик успел заработать цирроз печени, а сколько народу на линии сменилось уже не сосчитать... Всё дело в одной пропущенной букве: константа должна быть одинарной точности, а GCC её принудительно делал двойной точности. В очень редких случаях этот код начинал выполнятся, и работал чуть дольше чем с одинарной точностью. В результате лента конвейера успевала продвинуться на пару миллиметров, отчего лапы захвата тары звонко долбились об пластик. Это было громко, намного громче фонового шума всего цеха. Отчего вокруг ленты возникал кипишь, плавно переходящий в панику.

Пропущенная буква - "f".  

Share this post


Link to post
Share on other sites
3 minutes ago, AVI-crak said:

Моя история - ошибка с временем жизни в пять лет. За это время главный наладчик успел заработать цирроз печени, а сколько народу на линии сменилось уже не сосчитать... Всё дело в одной пропущенной букве: константа должна быть одинарной точности, а GCC её принудительно делал двойной точности. В очень редких случаях этот код начинал выполнятся, и работал чуть дольше чем с одинарной точностью. В результате лента конвейера успевала продвинуться на пару миллиметров, отчего лапы захвата тары звонко долбились об пластик. Это было громко, намного громче фонового шума всего цеха. Отчего вокруг ленты возникал кипишь, плавно переходящий в панику.

Пропущенная буква - "f".  

За ответ, даже если он не помогает мне конкретно, просто 5+, особенно за цирроз печени!!!

Share this post


Link to post
Share on other sites
53 minutes ago, vlad_new said:

Ищите где Вы забыли volatile объявить глобальные переменные. Именно при включении оптимизации оно и выстреливает.

 

Это интересная мысль. В моем (проблемном) коде нет таких переменных, хотя есть буферы, объявленые как static, то есть, доступные локально или по ссылке глобально.

Кстати, тема кэширования и многопоточности. Об этом много писалось, что при выходе из прерываний в cortex следует подумать о __DSB(), иначе можно влететь в то же прерывание после выхода из него. Выключенная оптимизация добавляет в код достаточно большую преамбулу и хвост: в gcc это какое-то жонглирование с R3. Оптимизация сокращает эти операции, что ускоряет выход из прерывания, но добавление __DSB() (а также __ISB() и __DMB() и всяких __NOP()) таки не решило мою проблему.

Edited by KnightIgor

Share this post


Link to post
Share on other sites
18 минут назад, aaarrr сказал:

Может, все-таки показать код? А то тема ни о чем выходит.

Уфф, если хотите повозиться, нет проблем:

 

Скрытый текст

/*----------------------------------------------------------------------------
 * Name:    stm32f10x_io.c (former "serial.c")
 * Purpose: STDIO over USART implementation
 *
 * Version: V1.00, tested on STM32F103RB and -RC
 *----------------------------------------------------------------------------
 * History:
 *          V1.00 Initial Version
 *----------------------------------------------------------------------------*/
//
#include "stm32f10x_proc.h"
//
#include "stm32f10x_io.h"   // include AFTER stm32f10x_proc.h to get project.h
#include <HAL_Dispatch.h>   // HAL Dispatcher
#include <misc.h>

#ifdef	USB_CDC	            // defined in "board.h"
                            // USB Virtual COM support
    #ifndef RL_USB          // to define in the PROJECT OPTIONS
    	#include "hw_config.h"

    #else
        static uint8_t PendingTX = 0;
        extern uint8_t USART_To_USB_Send_Data(char c);
        extern uint8_t USART_To_USB_Ready(void);
    #endif
#endif

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
//  Control USART_IT_RXNE bit
//
static void RXIE(uint8_t enable)
{
    if (enable) USART_RXIE_SET  (STDIO_DEVICE);
    else        USART_RXIE_CLEAR(STDIO_DEVICE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
//  Control USART_IT_TXE bit
//
static void TXIE(uint8_t enable)
{
    if (enable) USART_TXIE_SET  (STDIO_DEVICE);
    else        USART_TXIE_CLEAR(STDIO_DEVICE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
//  Reading RXNE bit
//
static uint8_t RX_Ready(void)
{
    return !!USART_RI_SR(STDIO_DEVICE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
//  Reading TXE bit
//
static uint8_t TX_Ready(void)
{
#ifdef	USB_CDC				// defined in project.h
    // STDIO -> USB
    if (!USART_To_USB_Ready())
    {
        PendingTX = 1;
        TXIE  (!PendingTX); // disable TX interrupt to suspend
        return !PendingTX;  // return as if not ready
    }
#endif
    return !!USART_TI_SR(STDIO_DEVICE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
//  Reading TC bit
//
static uint8_t TX_Complete(void)
{
    return !!USART_TC_SR(STDIO_DEVICE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
//  Writing DR
//
static void Write_TX(uint16_t value)
{
#ifdef	USB_CDC				// defined in project.h
    // STDIO -> USB
                            // (char) conversion is very important!
    USART_To_USB_Send_Data((unsigned char)value);
#endif

    USART_PUT_DR(STDIO_DEVICE, value);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
//  Reading DR
//
static int32_t Read_RX(void)
{
	// The read sequence below is necessary to clear the flags, see RM.
    uint16_t st = READ_BIT(STDIO_DEVICE->SR, USART_RX_FLAGS);
    int32_t  dr = USART_GET_DR(STDIO_DEVICE);

    if (st) // any RX related flag
    {
        if (READ_BIT(st, USART_FLAG_FE) && dr == 0) // BREAK - prepare for DMX
        {
            stdioBreakDetected(stdioFIFO());
        }
    }
    return dr;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
//  Driver record and buffer definitions
//
DEFINE_USART_BUFFERS(FIFO, STDIO_BufferSize, STDIO_BufferSize);

// -----------------------------------------------------------------------------
//
//  Implementation for declared in HAL_UART.h
//  Returns the pointer to the internal stdio FIFO structure
//
bufio_FIFO *stdioFIFO(void) {return &FIFO;}

// -----------------------------------------------------------------------------
//
//  Implementation for declared in HAL_UART.h
//
//  USB CDC support, USB CDC clone of STDIO:
//  The function called every time, the USB subsystem accepted
//  the STDIO cloned char.
//
//  Should always return TRUE to be used in some conditional expressions!
//
__attribute__((weak))
uint8_t stdioCharRequest(void)
{
#ifdef	USB_CDC				// defined in project.h
    // STDIO -> USB
    if (PendingTX)
    {
        PendingTX = 0;
        TXIE(!PendingTX);   // reenable the TX interrupt
    }
#endif
    return true;
}

#ifdef	USB_CDC
// -----------------------------------------------------------------------------
//
//  STRONG for HAL_UART.c
//
//  'wait before send' routine for the case, the output buffer found
//  to be full and the char cannot be sent immediately.
//
//  The weak implementation waits 100ms by DWT. For USB COM implementation
//  WAIT_WHILE(condition, ms_timeout) is recommended.
//
//  Returns the char sent.
//
int stdioWaitBeforeSend(int c)
{
//    WAIT_TIME(bufio_WriteChar(stdioFIFO(), c) < 0, 100);
    WAIT_WHILE(bufio_WriteChar(stdioFIFO(), c) < 0, 100);
    return c;
}
#endif
/******************************************************************************/
//
//	USART IRQ Handler
//
/******************************************************************************/
void STDIO_IRQHandler(void)
{
    STDIO_DEVICE->SR;           // unbelievable but reading SR before
    bufio_IRQHandler(&FIFO);    // helps to avoid byte loss. 28.04.14.
}
/******************************************************************************/

void STDIO_ReInit(uint32_t baudrate)
{
	USART_InitTypeDef
	USART_InitStructure = {
		baudrate,
		USART_WordLength_8b,
		USART_StopBits_1,
		USART_Parity_No,
		USART_Mode_Rx | USART_Mode_Tx,
		USART_HardwareFlowControl_None
	};
    if (!baudrate) baudrate = STDIO_BAUD;
  	USART_InitStructure.USART_BaudRate = baudrate;
    USART_INIT(STDIO_DEVICE, USART_InitStructure);
}
/******************************************************************************/
//
//  Init an USART
//
__attribute__((weak)) void initialise_monitor_handles(void) {}
void Init_STDIO(uint32_t baudrate)
{
    static const RCC_PCLK_TypeDef DeviceClock[] = STDIO_RCC;
    static const GPIO_PIN_TypeDef DeviceGPIOs[] = STDIO_GPIO;

    static const NVIC_InitTypeDef
        NVIC_InitStructure = {
            STDIO_IRQn,
            STDIO_IT_Priority,
            0,
            ENABLE
        };
    initialise_monitor_handles();

    RCC_Configuration (DeviceClock,  sizeof(DeviceClock));
    STDIO_ReInit(baudrate);
    GPIO_Configuration(DeviceGPIOs,  sizeof(DeviceGPIOs));
    USART_ENABLE(STDIO_DEVICE);
    IRQ_INIT(NVIC_InitStructure);
    bufio_Init(&FIFO);
}
// --- EOF ---------------------------------------------------------------------

 

 

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.