Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: STM32L0 HardFault: заморочки с выравниванием
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
k000858
Есть какие то ограничения в ядре, приводящие к HardFault?

Пришлось в одной функции привести указатель типа uint8_t* к типу uint32_t* изза чего получил HardFault.

В STM32F4 камне тот же самый код работает нормально.
Почему же M0+ вылетает в HardFault?
Сергей Борщ
QUOTE (k000858 @ Jan 31 2017, 14:19) *
Почему же M0+ вылетает в HardFault?
Потому что не поддерживает невыровненный доступ.
k000858
Цитата(Сергей Борщ @ Jan 31 2017, 15:21) *
Потому что не поддерживает невыровненный доступ.

можно поподробнее?
пока все проблемы выравнивая обходил стороной, проектов на M0+ минимум
adnega
Нужно гарантировать, что uint32_t* указывает на переменную, расположенную по адресу, кратному 4.
Сергей Борщ
QUOTE (k000858 @ Jan 31 2017, 14:27) *
можно поподробнее?
PM0223: STM32L0 Series Cortex®-M0+ programming manual, искать словосочетание "unaligned access"
k000858
Цитата(adnega @ Jan 31 2017, 15:35) *
Нужно гарантировать, что uint32_t* указывает на переменную, расположенную по адресу, кратному 4.

а, вот оно что. теперь более менее понимаю.


буфер в котором содержится нужное мне 32-битное значение uint8_t buf[100];
указатель на место, в котором содержится значением uint8_t* ptr = &buf[5];

на Cortex-M4 не заморачивался и делал вот так printf("val = %lu\n", *(uint32_t*)ptr);
на Cortex-M0+ такая запись приводит к HardFault'у. Причину теперь понимаю хорошо, и как не получать такой эффект тоже.

Пока сделал вот так:
Код
uint8_t value[4];
value[0]    = *ptr;
value[1]    = *(ptr + 1);
value[2]    = *(ptr + 2);
value[3]    = *(ptr+ 3);
uint32_t val    = (value[0]) | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
printf("val = %lu\n", val);


но чувствую, можно сделать это поизящнее. Подскажите красивый способ получения доступа к 32-битному значению
Alechek
Код
#pragma pack(push, 1)
struct _input {
uint32_t Value;
};
#pragma pack(pop)

struct _input * I = (struct _input *) ptr;
printf("val = %lu\n", I->Value);



k000858
Цитата(Alechek @ Feb 1 2017, 08:06) *
Код
#pragma pack(push, 1)
struct _input {
uint32_t Value;
};
#pragma pack(pop)

struct _input * I = (struct _input *) ptr;
printf("val = %lu\n", I->Value);

да, спасибо, такой способ вполне подойдет.
ViKo
Цитата(k000858 @ Feb 1 2017, 08:52) *
да, спасибо, такой способ вполне подойдет.

Отпишитесь о результате.
Kabdim
... или более каноничный через memcpy.
Код
uint32_t value;
memcpy(&value, ptr, sizeof(value));
printf("val = %lu\n", value);

Он вам кстати еще много где пригодится.
k000858
Цитата(Kabdim @ Feb 1 2017, 10:08) *
... или более каноничный через memcpy.
Код
uint32_t value;
memcpy(&value, ptr, sizeof(value));
printf("val = %lu\n", value);

Он вам кстати еще много где пригодится.

заработали оба способа
второй способ понравился даже больше. еще раз спасибо, теперь мне тема выравнивания еще более понятна.
ViKo
Допустим, memcpy умеет правильно обращаться по невыровненному адресу. Или тупо байтами копирует. Но как помогает приведение указателя к указателю на выровненную структуру? От этого положение данных не меняется же.
Kabdim
Почему ж она выровненная? Там прагма спереди и в конце стоит.
scifi
Цитата(Kabdim @ Feb 1 2017, 10:08) *
... или более каноничный через memcpy.
Код
uint32_t value;
memcpy(&value, ptr, sizeof(value));
printf("val = %lu\n", value);

Он вам кстати еще много где пригодится.

Если уж мы тут образованием занимаемся, то более кошерно формат указывать вот так:
Код
uint32_t value;
memcpy(&value, ptr, sizeof(value));
printf("val = %" PRIu32 "\n", value);

jcxz
Цитата(k000858 @ Feb 1 2017, 06:04) *
но чувствую, можно сделать это поизящнее. Подскажите красивый способ получения доступа к 32-битному значению

Если компилятор поддерживает соответствующий префикс, создать пакованные типы:
Код
typedef __packed u16 u16p8;
typedef __packed s16 s16p8;
typedef __packed u32 u32p8;
typedef __packed s32 s32p8;
typedef __packed u64 u64p8;
typedef __packed s64 s64p8;
либо, если в компиляторе нет соответствующего префикса, создать соотв. классы и перегрузить в них операторы присваивания и приведения типа:
Код
struct u32p8 {
  u8 bytes[4];
  operator u32() const { return u32load(&bytes); }
  u32p8 & operator =(u32 val) { u32save(&bytes, val); return *this; }
};

где: u32load() и u32save() - макросы, которые на процессоре поддерживающем невыровненный доступ определены просто как чтение или запись значения по этому адресу, а на процессоре не поддерживающем невыровненный доступ - они осуществляют побайтное чтение/запись слова.
ViKo
Разве не достаточно создать union для обращения к переменной по байтам и словом?
Kabdim
Цитата(scifi @ Feb 1 2017, 12:25) *
Если уж мы тут образованием занимаемся, то более кошерно формат указывать вот так:
Код
uint32_t value;
memcpy(&value, ptr, sizeof(value));
printf("val = %" PRIu32 "\n", value);

Всё хорошо в меру, те эмбедед библиотеки с которыми я работал такой формат не понимают. Можно конечно определить самому, но тогда насколько я помню у меня были проблемы с постоянными варнингами на size_t/ptrdiff_t.
jcxz
Цитата(ViKo @ Feb 1 2017, 11:35) *
Разве не достаточно создать union для обращения к переменной по байтам и словом?

Достаточно. О чём я выше и написал. Но ТС хочет красиво. А красиво это - или перегрузить в этом union-e/struct-е операторы присваивания/приведения типа или воспользоваться соответствующим префиксом компилятора если таковой есть (как в IAR).
ViKo
Красиво - это когда просто. sm.gif Перегрузка операторов - это не красиво. Вообще не знаю, зачем это придумали. Какая-то "феня" высоких интеллектуалов.
jcxz
Цитата(ViKo @ Feb 1 2017, 11:49) *
Красиво - это когда просто. sm.gif Перегрузка операторов - это не красиво. Вообще не знаю, зачем это придумали. Какая-то "феня" высоких интеллектуалов.

Чем именно не красиво???
Вам это нравится:
int x;
x = y;
а это нет:
u32p8 x;
x = y;
почему?
ViKo
Цитата(jcxz @ Feb 1 2017, 12:54) *
Чем именно не красиво???
u32p8 x;
x = y;
почему?

Покажите полное решение вопроса, с макросами или функциями. Вот чтобы скопипастить в исходник, и оно работало. biggrin.gif Тогда оценим красоту. А пока вижу только идею.
jcxz
Цитата(ViKo @ Feb 1 2017, 12:02) *
Покажите полное решение вопроса, с макросами или функциями. Вот чтобы скопипастить в исходник, и оно работало. biggrin.gif Тогда оценим красоту. А пока вижу только идею.

Идею я обрисовал, а чтобы работало - это может проверить только тот, у кого M0 или ARM7. У меня сейчас такого МК нет под рукой.
Вот для типов u16p8 и s16p8 (пакованный до байт беззнаковый и знаковый u16/s16):
CODE
#ifdef ADDR_ALIGN
#define u16load(p) (*(u8 *)(p) | (u16)((u8 *)(p))[1] << 8)
#define s16load(p) (*(u8 *)(p) | (s16)((s8 *)(p))[1] << 8)
#define u16save(p, x) { *(u8 *)(p) = (u8)(x); ((u8 *)(p))[1] = (x) >> 8; }
#else //ADDR_ALIGN
#define u16load(p) (*(u16 *)(p))
#define s16load(p) (*(s16 *)(p))
#define u16save(p, x) { *(u16 *)(p) = (x); }
#endif

#ifdef ADDR_ALIGN

struct u16p8 {
u8 bytes[2];
operator u16() const { return u16load(&bytes); }
u16p8 & operator =(u32 val) { u16save(&bytes, val); return *this; }
};
struct s16p8 {
u8 bytes[2];
operator s16() const { return s16load(&bytes); }
s16p8 & operator =(s32 val) { u16save(&bytes, val); return *this; }
};

#else //ADDR_ALIGN

typedef __packed u16 u16p8;
typedef __packed s16 s16p8;

#endif //ADDR_ALIGN

Реализация остальные типов - на домашнее задание wink.gif
ADDR_ALIGN должен быть определён для случая если:
a) процессор не поддерживает выравнивание;
...И...
б) компилятор не понимает префикса __packed.
ViKo
И все это ради того, чтобы не дать указание компилятору-линкеру разместить переменную в правильном месте. laughing.gif
k000858
Цитата(ViKo @ Feb 1 2017, 13:29) *
И все это ради того, чтобы не дать указание компилятору-линкеру разместить переменную в правильном месте. laughing.gif

лично для меня это нужно, что бы обратиться к переменной, не заморачиваясь, куда же ее разместил линкер-компилятор
demiurg_spb
Цитата(ViKo @ Feb 1 2017, 13:29) *
И все это ради того, чтобы не дать указание компилятору-линкеру разместить переменную в правильном месте. laughing.gif

Нет. Это для того чтобы из принятого буфера прочесть переменную с произвольным смещением в этом буфере.
Такой вариант никто не предлагал:
Код
uint32_t __packed* p = &buff[33];
uint32_t x = *p;

Около года назад в gcc эта шляпа не сработала, а в кейле работала.
jcxz
Цитата(demiurg_spb @ Feb 1 2017, 12:34) *
Около года назад в gcc эта шляпа не сработала, а в кейле работала.

__packed не все компиляторы поддерживают. IAR поддерживает. И Keil значит - тоже.
ViKo
Цитата(demiurg_spb @ Feb 1 2017, 13:34) *
Нет. Это для того чтобы из принятого буфера прочесть переменную с произвольным смещением в этом буфере.

Для M0 здесь нет решения, кроме как читать по байтам.
Цитата
Такой вариант никто не предлагал:
Код
uint32_t __packed* p = &buff[33];
uint32_t x = *p;

Около года назад в gcc эта шляпа не сработала, а в кейле работала.

Поясните. Выше интересовался таким же вопросом. Указатель на упакованную структуру - как это помогает обратиться к слову по не выровненному адресу?
Ясно. Читает по байтам.
demiurg_spb
Цитата(jcxz @ Feb 1 2017, 13:44) *
__packed не все компиляторы поддерживают. IAR поддерживает. И Keil значит - тоже.
__packed и gcc поддерживаете, но не полностью - он не отрабатывает указатели с квалификатором __packed...

У меня так сделано:
Код
#if defined(__MINGW32__)
#    define __packed       __attribute__((__gcc_struct__, __packed__, __aligned__(1)))
#else
#    define __packed       __attribute__((__packed__, __aligned__(1)))
#endif

Цитата(ViKo @ Feb 1 2017, 13:46) *
Поясните. Выше интересовался таким же вопросом. Указатель на упакованную структуру - как это помогает обратиться к слову по не выровненному адресу?
Очень просто. Компилятор, видя такой указатель, сам генерирует правильные инструкции для доступа по невыровненному адресу.
jcxz
Цитата(ViKo @ Feb 1 2017, 12:46) *
Для M0 здесь нет решения, кроме как читать по байтам.
...
Ясно. Читает по байтам.

А Вы что хотели - некую чудесную инструкцию процессора? biggrin.gif
M0 физически не умеет невыровненный доступ - нет таких инструкций у него!
Так что такой доступ можно только эмулировать. Либо встроенными возможностями компилятора (__packed) либо реализовав это самостоятельно (свой класс с перегрузками, например).

PS: Более того - даже для ядра M4 (и прочих) если переменная находится (или может находиться) по невыровненному адресу, для неё надо указывать спецификатор __packed.
А то, что у ТС "В STM32F4 камне тот же самый код работает нормально" - о говорит только о кривости написания этого кода, который сейчас работает, завтра перекомпилят его на другом компиляторе (или новой версии того же компилятора) - он будет падать в тот же HF на том же самом ядре M4. Так как никто не мешает компилятору для обращения к этим данным использовать не инструкции LDR/STR, а например LDRD/STRD или LDM/STM, которые не умеют невыровненный доступ. И компилятор имеет право это сделать, например при оптимизации.
Так что надо взять за правило: на любом процессоре, если переменная невыровнена, она должна быть объявлена как невыровненная.
ViKo
Цитата(jcxz @ Feb 1 2017, 13:53) *
А Вы что хотели - некую чудесную инструкцию процессора?

Так что надо взять за правило: на любом процессоре, если переменная невыровнена, она должна быть объявлена как невыровненная.

Я руками (головой) размещаю переменные в структуре так, чтобы стояли ровно. biggrin.gif Если есть дыры, забиваю их uint8_t RESERVED_n. А саму структуру линкер знает, куда разместить. Без указаний.
И компиляторы не пугают, ни прошлые, ни будущие.
jcxz
Цитата(ViKo @ Feb 1 2017, 13:11) *
Я руками (головой) размещаю переменные в структуре так, чтобы стояли ровно. biggrin.gif Если есть дыры, забиваю их uint8_t RESERVED_n. А саму структуру линкер знает, куда разместить. Без указаний.

Очевидно Вы не работали никогда например с протоколами передачи данных. В кадрах которых данные находятся упакованными. И объявлять такие кадры удобно структурами.
Поэтому Вам и сложно представить такое применение.
И дырок в таких кадрах нет - ну не заложили их разработчики этого протокола!
ViKo
Цитата(jcxz @ Feb 1 2017, 14:17) *
Очевидно Вы не работали никогда например с протоколами передачи данных. В кадрах которых данные находятся упакованными. И объявлять такие кадры удобно структурами.
Поэтому Вам и сложно представить такое применение.
И дырок в таких кадрах нет - ну не заложили их разработчики этого протокола!

Очевидно, что с таким пакетом работал бы по байтам. Ибо все "красивые решения" в конечном итоге так и работают. Под маской продвинутости прячут простоту.
scifi
Цитата(ViKo @ Feb 1 2017, 14:22) *
Очевидно, что с таким пакетом работал бы по байтам. Ибо все "красивые решения" в конечном итоге так и работают. Под маской продвинутости прячут простоту.

Это называется "сериализация и десериализация". Это кошерно, халяльно и по феншую, народ одобряет. Там ещё индейцы большие и малые.
Но, к примеру, в lwip сделано через структуры. Это действительно удобнее.
adnega
Цитата(ViKo @ Feb 1 2017, 14:22) *
Очевидно, что с таким пакетом работал бы по байтам.

А в следующей версии добавят байтик в середину и все смещения нужно будет выискивать по-новой.
Или некоторые переменные станут невыровненными и т.п.

В gcc объявляю так:
Код
typedef struct sPACKET_RT_SET_TIME
{
    BYTE    cmd;
    BYTE    cid;
    DWORD    datetime;
} __attribute((packed)) sPACKET_RT_SET_TIME;

А все остальное - дело компилятора.
Правда, есть еще пара ключиков, относящихся к теме
Цитата
-fpack-struct -Wpadded
jcxz
Цитата(ViKo @ Feb 1 2017, 13:22) *
Очевидно, что с таким пакетом работал бы по байтам. Ибо все "красивые решения" в конечном итоге так и работают. Под маской продвинутости прячут простоту.

А завтра переносим Вашу реализацию на процессор, умеющий работать с невыровненными данными и получаем неэффективность из-за кучи лишней возни с байтовым доступом и склеиванием этих байт в слова хотя на данном CPU это не нужно. И всё придётся переписывать.
А если работа идёт через члены пакованных структур, то код вообще менять не нужно - компилятор сам знает, что на данном CPU можно не разбирать {u32 x = y} на байты.
ViKo
Легким движением руки структура превращается... превращается...
Код
typedef struct sPACKET_RT_SET_TIME
{
    DWORD    datetime;
    BYTE    cmd;
    BYTE    cid;
}


Цитата(jcxz @ Feb 1 2017, 15:11) *
А если работа идёт через члены пакованных структур, то код вообще менять не нужно - компилятор сам знает, что на данном CPU можно не разбирать {u32 x = y} на байты.

Убедили! biggrin.gif беру __packed.
Kabdim
Цитата(jcxz @ Feb 1 2017, 15:11) *
А если работа идёт через члены пакованных структур, то код вообще менять не нужно - компилятор сам знает, что на данном CPU можно не разбирать {u32 x = y} на байты.

Как минимум для DS5 это несколько сложнее и требует внимания программиста. Для упакованной структуры нет предположений о том что её начало выравнено, поэтому такая оптимизация не произойдет в общем случае. Всегда будет рассматриваться худший случай невыравненного размещения. А невыравненный доступ на абсолютном большинстве архитектур дороже выравненного, поэтому если есть возможность привести данные в выравненное состояние то этим стоит воспользоваться.
jcxz
Цитата(Kabdim @ Feb 1 2017, 14:36) *
А невыравненный доступ на абсолютном большинстве архитектур дороже выравненного, поэтому если есть возможность привести данные в выравненное состояние то этим стоит воспользоваться.

Ну так на M3/M4 код генерируемый компилятором от этого не меняется. Меняется только время доступа к таким данным. Работать будет всё равно пусть медленнее немного.
ViKo
ВЫРОВНЯТЬ, -яю, -яешь; св. (нсв. также ровнять). кого-что. 1. Сделать ровным, прямым без изгибов. В. поверхность. В. дорогу. В. шеренгу, строй. 2. что. Расположить, направить по прямой линии в вертикальной или горизонтальной плоскости. В. самолёт. 3. Сделать размеренным, равномерным. В. дыхание. В. шаг.
<Выравнивать, -аю, -аешь; нсв. Выравниваться, -ается; страд. Выравнивание (см.).
http://www.gramota.ru/slovari/dic/?lop=x&a...%8F%D1%82%D1%8C
Нажмите для просмотра прикрепленного файла

выравнять(ся), -яю(сь), -яет(ся) (к равный)
http://www.gramota.ru/slovari/dic/?word=%D...D1%8C&all=x
Нажмите для просмотра прикрепленного файла
Kabdim
Цитата
выра́внивать

Глагол, несовершенный вид, переходный, тип спряжения по классификации А. Зализняка — 1a. Соответствующий глагол совершенного вида — выровнять.

Приставка: вы-; корень: -равн-; суффикс: -ива; глагольное окончание: -ть [Тихонов, 1996].

rolleyes.gif

Цитата(ViKo @ Feb 1 2017, 16:05) *
ВЫРОВНЯТЬ, -яю, -яешь; св. (нсв. также ровнять). кого-что. 1. Сделать ровным, прямым без изгибов. В. поверхность. В. дорогу. В. шеренгу, строй. 2. что. Расположить, направить по прямой линии в вертикальной или горизонтальной плоскости. В. самолёт. 3. Сделать размеренным, равномерным. В. дыхание. В. шаг.
<Выравнивать, -аю, -аешь; нсв. Выравниваться, -ается; страд. Выравнивание (см.).
Нажмите для просмотра прикрепленного файла
http://www.gramota.ru/slovari/dic/?lop=x&a...%8F%D1%82%D1%8C
выравнять(ся), -яю(сь), -яет(ся) (к равный)
http://www.gramota.ru/slovari/dic/?word=%D...D1%8C&all=x
Нажмите для просмотра прикрепленного файла

см. выделенное жирным rolleyes.gif
Последние два поста (начиная с этого) они конечно очень к тебе относятся.
ViKo
Цитата(Kabdim @ Feb 1 2017, 16:10) *
Последние два поста (начиная с этого) они конечно очень к тебе относятся.

Со вчерашнего дня думал, как писать. laughing.gif Пропустишь раз, потом наверстывай.
Kabdim
Я там видимо опечатался blush.gif "к теме" имел ввиду.
Цитата(jcxz @ Feb 1 2017, 16:00) *
Ну так на M3/M4 код генерируемый компилятором от этого не меняется. Меняется только время доступа к таким данным. Работать будет всё равно пусть медленнее немного.

Внутренний перфекционист негодует когда работает медленней, наверное его пора топить. sm.gif
adnega
Цитата(ViKo @ Feb 1 2017, 15:14) *
Легким движением руки структура превращается... превращается...
Код
typedef struct sPACKET_RT_SET_TIME
{
    DWORD    datetime;
    BYTE    cmd;
    BYTE    cid;
}

Я при разработке протоколов стараюсь выравнивать данные, но не всегда есть такая возможность.
В данном случае менять порядок нельзя, т.к. первые два байта нужны для адресации контроллера
и указания номера команды, а уже за ними следуют различные данные.
Я бы проголосавал за такой вариант, будь моя воля.
Код
typedef struct sPACKET_RT_SET_TIME
{
    BYTE    cmd;
    BYTE    cid;
    WORD    none;
    DWORD    datetime;
}

Axel
Цитата
Внутренний перфекционист негодует... sm.gif

Так похоже поводов нет: если предположить, что реализация (в IAR) __aeabi_uread4 аналогична __aeabi_memcpy, то оба варианта идентичны по затратам.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.