worklog: "Ты ж смотри, нашёл!"

Nov 26, 2017 16:30

Первое открытие.

И ведь наверняка в стандарте языка это описано. А если не определено там, то определено в документации на gcc. Но кто ж её читает?
Это я сам себе делаю выговор за то, что в структуре с битовыми полями (bitfield) допустил малозаметную, но коварную ошибку.
Дело в том, что когда объявляешь каждое новое поле новым оператором (см. пример 1), то они идут "снизу вверх" (от младшего адреса к старшему) - это логично и ожидаемо. А вот ежели описываешь их одним оператором (см. пример 2), то они меняются местами. Во всяком случае, для битфилдов gcc ARM Cortex-M7 это так.

Пример 1:

typedef struct {
uint8_t field1:3;
uint8_t field2:5;
uint8_t field3;
} struct1;
Здесь поле field1 займёт младшие 3 бита в первом байте, field2 займёт старшие 5 бит, field3 займёт следующий байт. Упаковка в памяти зависит от опций компиляции, я для таких структур (отображаемых на железо или же на какой-то протокол -- т.е. вольности недопустимы) всегда использую байтовую упаковку.

Пример 2:

typedef struct {
uint8_t
field1:3,
field2:5;
uint8_t field3;
} struct1;
Казалось бы, всё то же самое. Но нихрена! тут field1 попадёт в СТАРШИЕ три бита первого байта и, соотв., field2 займёт младшие 5 бит.

Хер такое поймаешь, пока не распотрошишь весь код до самых низов. Потому что симптом выглядел так: хост слал запрос на снятие состояния STALL для конечной точки с адресом 0x81 - я снимал это состояние (точно снимал! дебаггером смотрел - снимается!), а хост снова слал этот запрос. Причина оказалась абсолютно неочевидной и спрятанной совсем в другом месте.

Второе открытие.

USB -- очень хитрованская штука. В протоколе есть так называемые toggle-биты, служащие для простейшей проверки целостности потока кадров. Эти биты чередуются (условно говоря, 0-1-0-1), но из этой последовательности есть строго оговорённые исключения. Когда я писал функции отправки данных, то зачем-то вбил установку этого бита в состояние DATA0 для КАЖДОГО кадра данных, что неправильно, если в транзакции передачи таких кадров больше одного: стандарт требует чередование, а у меня все они промаркированы как DATA0. Хост видит первый, затем видит кривой и полностью игнорирует его (даже в сниффере не видно). Просто убрал установку этого бита (он включается один раз при разрешении работы конечной точки, а дальше железо само следит, когда-что) и вот...


Оно ещё не работает (нет функций для работы с собственно накопителем), но минимально-необходимый скелет готов. Устройство опознаётся, отдаёт все нужные репорты, лепота!

приключения Электроника

Previous post Next post
Up