Comments 15
Как интересно, счетчик сделали 64-битным, а атомарное 64-битное чтение не завезли?
Неужто инструкция LD неатомарная?
В микроконтроллерах обычно предусмотрен аппаратный механизм, обеспечивающий “атомарность” при чтении таких регистров. Одна часть читается напрямую, а вторая через промежуточный регистр-защелку. При чтении младшей части, старшая защелкивается в промежуточном регистре (а может наоборот). Надо просто соблюдать последовательность чтения младший-старший.
В этом контроллере что-то нестандартно сделано. Надо документацию посмотреть. Или может это ранняя ревизия, инженерный образец.
Это абсолютно стандрартный подход для Performance Counters, в RISC-V найти аппаратную защелку для даннго типа инструкций практически нереально. Подобное поведение описано спецификацией.
в RISC-V найти аппаратную защелку для даннго типа инструкций практически нереально
Этот аппаратный механизм не связан с набором команд и конкретной архитектурой. Он применяется в ситуации, когда разрядность регистра больше чем разрядность шины данных, если младшая и старшая часть должны читаться/записываться одновременно. Это еще с древних времен используется.
On 8-bit AVR microcontrollers, accessing 16-bit I/O registers requires a strict byte-access order and a hardware temporary high-byte register to ensure atomicity. You must write the Low byte first (storing it in the temp register), then write the High byte (which triggers a simultaneous update). For reading, you must read the Low byte first (capturing the high byte into the temp register), then read the High byte.
Предположим, что есть защелка. Читаем младшую часть(старшая в защелку), получаем прерывание, уходим на обработчик в котором тоже читается этот счетчик и портит защелку. Возвращаемся и читаем уже непонятно что. Разве не так?
Нет, на QingKe V4 (RV32IMAC) не завезли. STK_CNTL и STK_CNTH читаются двумя отдельными LW
Только запрет прерываний для этой комманды не поможет. Нужно использовать специальную обертку:
```
static uint64_t __riscv_get_cycle64(void)
{
#if __riscv_xlen == 64
uint64_t val;
asm volatile(
"rdcycle %0"
: "=r"(val));
return val;
#else
uint32_t hi1, hi2, low;
do
{
asm volatile(
"rdcycleh %0\n"
"rdcycle %1\n"
"rdcycleh %2"
: "=r"(hi1), "=r"(low), "=r"(hi2));
} while (hi1 != hi2);
return ((uint64_t)hi1 << 32) | low;
#endif
}
```
Я бы завернул в цикл просто вместо одиночного if. И даже без запрета прерываний оно бы в итоге смогло считать корректное значение.
Впрочем, и без цикла запрет прерываний не так уж нужен. Не исполняются обработчики прерываний десятки секунд. А если исполняются, то у нас уже всё сломалось.
Это реальная задача из собеса в яндекс. Как прочитать 64-битный каунтер 32-битными операциями.
Как увидел uint64_t сразу стало понятно, подобная ошибка достаточно типичная. К примеру на MS430 такое сплошь и рядом, там даже операция чтения uint16_t атомарная, но в силу асинхронного источника тактирования - может разъехаться старший и младший байт. Прерывания я бы не маскировал, просто бесконечный цикл пока два чтения расходятся.
Нельзя просто взять и прочитать 64-битный SysTick на CH32V203