Я имею дело с аппаратными регистрами на контроллере STM32.
Я определил кучу структур вроде следующего:
#define PACKED __attribute__ ((packed))
#define ASSERT(cond) _Static_assert(cond, #cond)
typedef union {
struct PACKED {
uint16_t value;
uint16_t reserved;
};
uint32_t bits;
} LOL_LPTIM_16_BIT_VALUE;
ASSERT(sizeof(LOL_LPTIM_16_BIT_VALUE) == sizeof(uint32_t)); // OK
Тогда у меня есть такая структура:
typedef struct PACKED {
// ...
volatile LOL_LPTIM_16_BIT_VALUE autoreload;
// ...
} LOL_LPTIM;
Смещение поля autoreload
в структуре согласуется с документацией. У меня также есть следующие объекты (в соответствии с документацией и заголовочными файлами, предоставленными ST):
#define LOL_LPTIM1_BASE (LOL_APB1PERIPH_BASE + 0x7C00UL)
#define LOL_LPTIM2_BASE (LOL_APB1PERIPH_BASE + 0x9400UL)
#define LOL_LPTIM1 ((volatile LOL_LPTIM *) LOL_LPTIM1_BASE)
#define LOL_LPTIM2 ((volatile LOL_LPTIM *) LOL_LPTIM2_BASE)
У меня есть структура static const
, в которой хранятся эти указатели:
static const struct {
volatile LOL_LPTIM *lptim;
} timer[2] = {
{ .lptim = LPTIM1 },
{ .lptim = LPTIM2 }
}
Теперь , когда я пишу
*(uint32_t *) &(timer[0].lptim->autoreload.bits) = 0xffff;
или
*(uint16_t *) &(timer[0].lptim->autoreload.value) = 0xffff;
, код работает правильно, но когда я пишу
timer[0].lptim->autoreload.bits = 0xffff;
(что должно быть точно эквивалентным) или
timer[0].lptim->autoreload.value = 0xffff;
, то он не работает должным образом - он работает иначе, чем косвенный вариант, и значение регистра autoreload
, похоже, не установлено должным образом (периферийные устройства ведут себя иначе).
Что может быть возможной причиной этого несоответствия?
Godbolt показывает, что компилятор генерирует очень разный набор операций для этих двух случаев: https://godbolt.org/z/cno9yf
Они становятся более похожими, когда косвенная версия изменяется на
*(volatile uint32_t *) &(timer[0].lptim->autoreload.bits) = 0xffff;
(в выводе гораздо больше инструкций)