Драйвер STM32 VCP - указатель становится недействительным только при оптимизации - PullRequest
0 голосов
/ 13 мая 2018

Я работаю над встроенным проектом с микроконтроллером STM32F405, и у меня несколько запутанное поведение.Я портирую существующий (рабочий) проект из STM32F1 в STM32F4, но я добавил стандартную периферийную библиотеку USB ST ST для VCP.

Если я скомпилирую программу с оптимизацией -O0, он будет работать какожидается бесконечно.Однако если я скомпилирую с -O2, то проект будет работать в течение 10-15 минут, но тогда я получу то, что мне кажется переполнением стека, происходящим в коде драйвера ST VCP.

Фактическая ошибка проявляетсякак указатель (GREGS) становится недействительным, даже если указатель ранее использовался в той же функции.Этот указатель относится к регистрам конфигурации аппаратного прерывания для периферийного устройства USB, поэтому фактические данные не исчезли, однако при обращении к указателю возникает ошибка, и с помощью отладчика я вижу, что указатель недействителен.(Я скопировал фактическую функцию из usb_dcd_int.c ниже, с указанием проблемных линий.)

static uint32_t DCD_HandleRxStatusQueueLevel_ISR(USB_OTG_CORE_HANDLE *pdev)
{
    USB_OTG_GINTMSK_TypeDef  int_mask;
    USB_OTG_DRXSTS_TypeDef   status;
    USB_OTG_EP *ep;

    /* Disable the Rx Status Queue Level interrupt */
    int_mask.d32 = 0;
    int_mask.b.rxstsqlvl = 1;
    /*****************************************************************/
    /*********** POINTER IS READ HERE - NO PROBLEMS ******************/
    /*****************************************************************/
    USB_OTG_MODIFY_REG32( &pdev->regs.GREGS->GINTMSK, int_mask.d32, 0);

    /* Get the Status from the top of the FIFO */
    status.d32 = USB_OTG_READ_REG32( &pdev->regs.GREGS->GRXSTSP );

    ep = &pdev->dev.out_ep[status.b.epnum];

    switch (status.b.pktsts)
    {
    case STS_GOUT_NAK:
        break;
    case STS_DATA_UPDT:
        if (status.b.bcnt)
        {
          USB_OTG_ReadPacket(pdev,ep->xfer_buff, status.b.bcnt);
          ep->xfer_buff += status.b.bcnt;
          ep->xfer_count += status.b.bcnt;
        }
        break;
    case STS_XFER_COMP:
        break;
    case STS_SETUP_COMP:
        break;
    case STS_SETUP_UPDT:
        /* Copy the setup packet received in FIFO into the setup buffer in RAM */
        USB_OTG_ReadPacket(pdev , pdev->dev.setup_packet, 8);
        ep->xfer_count += status.b.bcnt;
        break;
    default:
        break;
    }

    /* Enable the Rx Status Queue Level interrupt */
    /*****************************************************************/
    /************************* GREGS == :-(   ************************/
    /*****************************************************************/
    USB_OTG_MODIFY_REG32( &pdev->regs.GREGS->GINTMSK, 0, int_mask.d32);

    return 1;
}

Я использую ванильный GNU make и gcc-arm-none-eabi 5-4-2016q3 в качестве моего toolcahin, ванильного компоновщика STи сценарий запуска с 2015 года для STM32F405, а код VCP - с марта 2012 года. Я новичок в сценариях запуска и компоновщика, но я не вижу ничего подозрительного ни в одном из них.Я также не вижу ничего вопиющего в коде ST VCP, но я определенно не понимаю каждую строку.

У меня три вопроса:

  1. Похоже ли это на переполнение стека?
  2. Как распределяется стек для IRQ?В реализации ST прерывание VCP имеет очень глубокое дерево вызовов.Нужно ли мне просто выделять больше для IRQ VCP?
  3. Какие оптимизации в -O2 могут вызвать такое поведение?Мне интересно, смогу ли я выборочно отключить некоторые оптимизации, которые могли бы помочь мне отследить мою ошибку.

1 Ответ

0 голосов
/ 13 мая 2018
  1. Если есть разница без оптимизации и с оптимизацией, то это звучит, как оптимизация + изменчивая проблема. Хорошая мысль - понять классификатор типа volatile и его связь с оптимизацией на Си. В сети много хороших статей.
  2. Стек просто существует, вы не можете «выделить» больше стека (по крайней мере, в некотором смысле во встроенной системе). Функция может «распределять» стек, таким образом, она использует стек для хранения локальных переменных, регистрирует состояния и перемещает указатель стека. Когда происходит IRQ, текущее состояние выполнения сохраняется поверх стека, а затем выполняется функция обработчика IRQ. Вы можете определить, происходит ли переполнение стека, установив точку останова при доступе к адресу памяти, который является концом стека. На STM32 указатель стека уменьшается, когда вы кладете что-то в стек. Вы можете проверить использование стека с помощью -fstack-usage. Но это не связано с проблемой. Компиляция с лучшей оптимизацией создает код с меньшим использованием стека.
  3. Я думаю, все / любые.

Теперь я не знаю, что вы подразумеваете под The actual bug manifests as a pointer (GREGS) getting dereferenced even though the pointer had been used earlier in the same function.. Намерение программистов было разыменовать GINTMSK дважды. Указатель GINTMSK объявлен как volatile, он будет разыменовываться при каждом использовании и не оптимизироваться. Это также является намерением, поскольку GINTMSK является аппаратно-отображаемой регистровой переменной.
Из вашего описания похоже, что значение pdev->regs.GREG где-то изменено в этом переключателе между ними.
USB_OTG_ReadPacket () выглядит довольно просто, но, возможно, буфер указывает в неправильное место и перезаписывает структуру pdev?
Может быть, во время этого прерывания запускается другое прерывание с другим приоритетом и изменяет структуру pdev. Попробуйте добавить __disable_irq() и __enable_irq() охранников.
Если вы переносите этот проект, вы можете перейти к библиотеке STM32 HAL и использовать программу STM32CubeMX для генерации некоторого кода. Библиотеки STM32 становятся лучше с каждой версией, и некоторые из самых старых имели проблемы с различными оптимизациями.

...