См. Редактирование в конце
Я пишу код для процессора 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
», если это возможно.