Атомное приращение не работает должным образом в прерывании ⇐ C++
-
Гость
Атомное приращение не работает должным образом в прерывании
У меня есть Google Coral Dev Micro с SoC RT1176 (800 МГц Cortex-m7 и 400 МГц Cortex-m4), m7 работает под управлением FreeRTOS, а m4 работает под управлением «голого железа», компилируется с использованием GCC none eabi 9.3.1 со следующим флаги:
-Wall -Wno-psabi -mthumb -fno-common -ffunction-sections -fdata-sections -ffreestanding -fno-builtin -mapcs-frame --specs=nano.specs --specs=nosys.specs -u _printf_float -std=gnu99 -g -Os -save-temps -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -DNDEBUG Я хочу передать данные, сгенерированные в результате прерывания таймера, между двумя ядрами, используя общую память, и я создал свой собственный неблокируемый буфер FIFO в общей памяти:
fifo.h:
#include #include #include #include шаблон класс LockLessFifo { публика: LockLessFifo() : read_idx(0), write_idx(0), buffer({0}) {} size_t size() { если (read_idx StartM4(); uint32_t счетчик1, счетчик2; в то время как (истина) { counter1 =shared_fifo.get(); // Работает counter2 =shared_fifo.get(); // Всегда 0 const std::size_t fifo_size =shared_fifo.size(); constexpr std::size_t fifo_capacity =shared_fifo.capacity(); printf("[M7] счетчик: %lu/%lu размер: %u/%u\r\n", counter1, counter2, fifo_size, fifo_capacity); } } FIFO заполняется на m4 следующим образом:
main_m4.cpp
#include #include #include #include "fsl_pit.h" #include "shared_fifo.h" статический std::atomic counter1 = 0; статический std::atomic counter2 = 0; недействительный my_pit_irq() { // Очистить флаг IRQ PIT_ClearStatusFlags(PIT1, kPIT_Chnl_0, kPIT_TimerFlag); counter1.store(counter1.load() + 1); // Работает counter2.fetch_add(1); // Всегда 0 SDK_ISR_EXIT_BARRIER; } extern "C" [[noreturn]] void app_main(void* param) { (недействительный) параметр; configure_and_start_timer(); // удалено для ясности в то время как (истина) { shared_fifo.put(counter1.load()); // Работает shared_fifo.put(counter2.load()); // Всегда 0 } } Вывод этого кода:
(...) [M7] счетчик: 71666076/0 размер: 1470/1472 [M7] счетчик: 71666076/0 размер: 1470/1472 [M7] счетчик: 71666077/0 размер: 1470/1472 [M7] счетчик: 71666077/0 размер: 1470/1472 [M7] счетчик: 71666077/0 размер: 1470/1472 (...) Почему неправильное приращение (счетчик1) работает, а правильное приращение (счетчик2) — нет? Я взглянул на дизассемблирование:
Прерывание PIT:
_Z10my_pit_irqv: // Очистить флаг IRQ лдр r3, .L3 мовс r2, #1 ул r2, [r3, #268] ldr r2, .L3+4 // Загрузка .LANCHOR0 черт возьми ldr r3, [r2] // Загружаем значение, хранящееся по адресу .LANCHOR0 (.load()) черт возьми добавляет r3, r3, #1 // Увеличение на 1 черт возьми str r3, [r2] // Сохраняем увеличенное значение (.store()) ldr r3, .L3+8 // Загрузка .LANCHOR1 черт возьми dmb ish // Дубловый барьер? .L2: ldrex r2, [r3] // загрузка добавляет r2, r2, #1 // приращение strex r1, r2, [r3] // сохраняем cmp r1, #0 // проверка bne .L2 // повторная попытка черт возьми дсб 0xF бх лр .L4: .выровнять 2 .L3: .word 1074626560 .word .LANCHOR0 // счетчик1 .word .LANCHOR1 // счетчик2 И главное:
app_main: нажмите {r0, r1, r2, lr} ldr r6, .L14 // Счетчик1 лдр r4, .L14+4 ldr r5, .L14+8 // Counter2 .L13: // Это то же самое, что и для счетчика2 добавить r1, сп, #4 ldr r3, [r6] // Загружаем значение счетчика1 черт возьми мов р0, р4 ул r3, [sp, #4] bl _ZN5LockLockLessFifoImLj1472EE3putERKm // Помещаем в FIFO черт возьми // Это то же самое, что и для счетчика1 добавить r1, сп, #4 ldr r3, [r5] // Загружаем значение счетчика2 черт возьми мов р0, р4 ул r3, [sp, #4] bl _ZN5LockLockLessFifoImLj1472EE3putERKm // Помещаем в FIFO б .L13 .L15: .выровнять 2 .L14: .word .LANCHOR0 .word _ZN511shared_fifoE .word .LANCHOR1 Я не понимаю, в чем дело. Я знаю, что FIFO работает, поскольку приращение работает, и мы видим, как число, напечатанное в терминале, увеличивается. Сгенерированная сборка очень похожа и выглядит правильно. К сожалению, я не могу подключить отладчик (пока), так как мне нужно будет припаять разъем JTAG к плате, и нам нужно подождать, прежде чем вносить какие-либо изменения.
Спасибо, что нашли время взглянуть.
У меня есть Google Coral Dev Micro с SoC RT1176 (800 МГц Cortex-m7 и 400 МГц Cortex-m4), m7 работает под управлением FreeRTOS, а m4 работает под управлением «голого железа», компилируется с использованием GCC none eabi 9.3.1 со следующим флаги:
-Wall -Wno-psabi -mthumb -fno-common -ffunction-sections -fdata-sections -ffreestanding -fno-builtin -mapcs-frame --specs=nano.specs --specs=nosys.specs -u _printf_float -std=gnu99 -g -Os -save-temps -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -DNDEBUG Я хочу передать данные, сгенерированные в результате прерывания таймера, между двумя ядрами, используя общую память, и я создал свой собственный неблокируемый буфер FIFO в общей памяти:
fifo.h:
#include #include #include #include шаблон класс LockLessFifo { публика: LockLessFifo() : read_idx(0), write_idx(0), buffer({0}) {} size_t size() { если (read_idx StartM4(); uint32_t счетчик1, счетчик2; в то время как (истина) { counter1 =shared_fifo.get(); // Работает counter2 =shared_fifo.get(); // Всегда 0 const std::size_t fifo_size =shared_fifo.size(); constexpr std::size_t fifo_capacity =shared_fifo.capacity(); printf("[M7] счетчик: %lu/%lu размер: %u/%u\r\n", counter1, counter2, fifo_size, fifo_capacity); } } FIFO заполняется на m4 следующим образом:
main_m4.cpp
#include #include #include #include "fsl_pit.h" #include "shared_fifo.h" статический std::atomic counter1 = 0; статический std::atomic counter2 = 0; недействительный my_pit_irq() { // Очистить флаг IRQ PIT_ClearStatusFlags(PIT1, kPIT_Chnl_0, kPIT_TimerFlag); counter1.store(counter1.load() + 1); // Работает counter2.fetch_add(1); // Всегда 0 SDK_ISR_EXIT_BARRIER; } extern "C" [[noreturn]] void app_main(void* param) { (недействительный) параметр; configure_and_start_timer(); // удалено для ясности в то время как (истина) { shared_fifo.put(counter1.load()); // Работает shared_fifo.put(counter2.load()); // Всегда 0 } } Вывод этого кода:
(...) [M7] счетчик: 71666076/0 размер: 1470/1472 [M7] счетчик: 71666076/0 размер: 1470/1472 [M7] счетчик: 71666077/0 размер: 1470/1472 [M7] счетчик: 71666077/0 размер: 1470/1472 [M7] счетчик: 71666077/0 размер: 1470/1472 (...) Почему неправильное приращение (счетчик1) работает, а правильное приращение (счетчик2) — нет? Я взглянул на дизассемблирование:
Прерывание PIT:
_Z10my_pit_irqv: // Очистить флаг IRQ лдр r3, .L3 мовс r2, #1 ул r2, [r3, #268] ldr r2, .L3+4 // Загрузка .LANCHOR0 черт возьми ldr r3, [r2] // Загружаем значение, хранящееся по адресу .LANCHOR0 (.load()) черт возьми добавляет r3, r3, #1 // Увеличение на 1 черт возьми str r3, [r2] // Сохраняем увеличенное значение (.store()) ldr r3, .L3+8 // Загрузка .LANCHOR1 черт возьми dmb ish // Дубловый барьер? .L2: ldrex r2, [r3] // загрузка добавляет r2, r2, #1 // приращение strex r1, r2, [r3] // сохраняем cmp r1, #0 // проверка bne .L2 // повторная попытка черт возьми дсб 0xF бх лр .L4: .выровнять 2 .L3: .word 1074626560 .word .LANCHOR0 // счетчик1 .word .LANCHOR1 // счетчик2 И главное:
app_main: нажмите {r0, r1, r2, lr} ldr r6, .L14 // Счетчик1 лдр r4, .L14+4 ldr r5, .L14+8 // Counter2 .L13: // Это то же самое, что и для счетчика2 добавить r1, сп, #4 ldr r3, [r6] // Загружаем значение счетчика1 черт возьми мов р0, р4 ул r3, [sp, #4] bl _ZN5LockLockLessFifoImLj1472EE3putERKm // Помещаем в FIFO черт возьми // Это то же самое, что и для счетчика1 добавить r1, сп, #4 ldr r3, [r5] // Загружаем значение счетчика2 черт возьми мов р0, р4 ул r3, [sp, #4] bl _ZN5LockLockLessFifoImLj1472EE3putERKm // Помещаем в FIFO б .L13 .L15: .выровнять 2 .L14: .word .LANCHOR0 .word _ZN511shared_fifoE .word .LANCHOR1 Я не понимаю, в чем дело. Я знаю, что FIFO работает, поскольку приращение работает, и мы видим, как число, напечатанное в терминале, увеличивается. Сгенерированная сборка очень похожа и выглядит правильно. К сожалению, я не могу подключить отладчик (пока), так как мне нужно будет припаять разъем JTAG к плате, и нам нужно подождать, прежде чем вносить какие-либо изменения.
Спасибо, что нашли время взглянуть.
Мобильная версия