Надежно занято-жду регистрации в C ++ - PullRequest
2 голосов
/ 17 марта 2020

См. Редактирование в конце

Я пишу код для процессора STM32F4, и я столкнулся с ситуацией, когда компилятор явно переупорядочивает чтение / запись моего регистра, разрушая занят ожидание l oop. В частности, когда я пишу

void writeLedSPI(uint16_t data) {
    using namespace Hardware;

    led_latch::clear();

    leds_spi::write(&data, 1);
    leds_spi::wait_tx_completed();

    led_latch::set();

    //The result should appear in the LED shift registers
}

, где

inline static void SPI::write(const uint16_t *data, std::size_t length) {

    for (unsigned int i = 0; i < length; i++){
        while (!tx_buffer_empty());
        regs()->DR = *(data++);
    }

}

inline static void SPI::wait_tx_completed() {
    while (regs()->SR & SPI_SR_BSY);
}

и

inline static void GPIO::set() {
    regs()->BSRR = bit;
}
inline static void GPIO::clear() {
    regs()->BSRR = bit << 16;
}

, что происходит под -O3, так это то, что вывод GPIO led_latch очищается до того, как запись SPI будет завершена (глядя на трассировку области действия, фактически к тому времени, когда SPI завершит работу с первым битом из 16). Похоже, что led_latch::set() перемещается до ожидания занятости l oop, или l oop полностью оптимизируется.

Это на самом деле немного сбивает с толку, так как все поля, поступающие из какая-либо из структур regs() volatile, поэтому я мог бы предположить, что компилятору не разрешается перемещать изменчивые операции чтения / записи друг за другом? Может быть, я что-то упускаю?

В любом случае, я пытался использовать технику в этом ответе , то есть вставлять различные экземпляры DoNotOptimize, но проблема в том, что мне нужна зависимость является неявным между чтением флага BSY, который возвращает false из SPI, и записью регистра BSRR, и я не могу заставить это работать.

Достаточно забавно, вставка ожидание занятости l oop, просто ожидающее счетчика отладки, делает свое дело:

const uint32_t start = DWT->CYCCNT;
while ((DWT->CYCCNT - start) < ticks);

, где ticks достаточно для SPI для передачи данных. Очевидно, что это хак, особенно в более сложном коде.

Итак: как мне удержать компилятор от перемещения led_latch::set() до ожидания занятости l oop в leds_spi::wait_tx_completed() на любом уровне оптимизации? Решение, в котором любое неприятное поведение, определяемое реализацией, могло бы быть скрыто в драйвере SPI, т.е. не было бы видимым в writeLedSPI, было бы идеальным.

Edit:

Проблема заключалась не в оптимизации самого компилятора, а в том, что при оптимизациях задержка от записи в регистр данных SPI до считывания флага SPI_SR_BSY составляла всего один такт (см. Godbolt ), к этому времени флаг еще не включился . Изменение кода на

leds_spi::write(&data, 1);
leds_spi::wait_tx_buffer_empty();
leds_spi::wait_tx_completed();

заставляет его работать. Я по-прежнему открыт для ответов о том, какую синхронизацию я мог бы сделать, чтобы сделать эту работу без трюка «просто нужно знать, чтобы дождаться tx_empty first», если это возможно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...