39б. Errata на практике: как читать список ошибок кристалла и обходить их
В разделе 39 сказано главное: перечень ошибок (errata sheet) читают до разработки, а не после третьего часа отладки. Раздел 39а показал, где лежат официальные документы. Здесь — сам перечень в руках: как узнать ревизию своего кристалла, как читать сводную таблицу и отдельный пункт, какие обходы пишутся в код и как их держать, чтобы через год было понятно, откуда взялась странная строчка.
Опоры: перечни ES0182 Rev 13 (F405/407/415/417) и ES0206 Rev 18 (F427/429/437/439), справочные руководства RM0090 и RM0033 — все из паков Keil; заголовки и HAL F4 из пака. Сводные таблицы разобраны своей моделью на ПК, обходы собраны armclang для F407.
Как это называется в документации
Оригинал Перевод errata sheet перечень ошибок кристалла device limitation ограничение (ошибка) устройства silicon revision ревизия кристалла revision code marked on device код ревизии в маркировке корпуса REV_ID, DEV_ID код ревизии и код устройства в DBGMCU_IDCODE workaround обходной путь partial workaround частичный обход: реже или не во всех режимах documentation limitation ошибка в документации, а не в кристалле
39б.1 Где лежит перечень
На этой машине перечни ошибок лежат в паках Keil, в папке Documentation,
под номерами вида DM000xxxxx.pdf — по имени файла не догадаться. Модель
открыла первую страницу каждого PDF в паках STM32 и нашла 19 перечней:
один в паке F2, 14 в паке F3 и 4 в паке F4. Для F407 — DM00037591.pdf,
это ES0182 Rev 13 на 45 страниц; для F429 — DM00068628.pdf, ES0206 Rev 18
на 54 страницы.
В паке F1 перечня нет: там шестнадцать PDF — описания, справочные
руководства и руководство по ядру. HAL F1 из архива STM32Cube на него
только ссылается: в комментарии к HAL_GetREVID() сказано, что у
F103x8/xB и ряда других F1 регистры DBGMCU_IDCODE и DBGMCU_CR доступны
лишь под отладчиком, «Refer to errata sheet». Сам перечень F1 скачивают со
страницы продукта (раздел 39а).
Перечень состоит из четырёх частей: опознание кристалла, сводная таблица, пункты по одному и история редакций.
39б.2 Какая у меня ревизия
Первая страница перечня говорит, как узнать ревизию: по коду под номером
заказа на корпусе или по полю REV_ID — битам 31:16 регистра
DBGMCU_IDCODE по адресу 0xE004 2000. Таблица кодов — в справочном
руководстве, у F4 это RM0090, раздел 38.6.1:
| REV_ID | F405/407 | F42x/F43x |
|---|---|---|
| 0x1000 | A | A |
| 0x1001 | Z | — |
| 0x1003 | 1 | Y |
| 0x1007 | 2 | 1 |
| 0x100F | Y и 4 | — |
| 0x101F | 5 и 6 | — |
| 0x2001 | — | 3 |
| 0x2003 | — | 5 и B |
Код устройства DEV_ID (биты 11:0) — 0x413 у F405/407 и 0x419 у F42x.
Документы расходятся в мелочи: у кода 0x2003 RM0090 пишет «5 и B», а
таблица 2 самого ES0206 — «4, 5 и B». Для выбора обходов верить перечню:
он новее и написан ровно про ошибки.
Подвох, который перечень описывает сам, — пункт 2.1.2 ES0182. У ревизии A
код устройства такой же, как у F2, а REV_ID читается 0x2000 вместо
0x1000. По RM0033 слово 0x2000 0411 — это F205/207 ревизии B. Обход из
перечня: отличать по ядру, у F2 — Cortex-M3, у F4 — Cortex-M4 (номер ядра
в SCB->CPUID — 0xC23 и 0xC24, раздел 2а):
if (dev == DEV_F2 && rev == 0x2000u && part == PART_M4)
return REV_A; /* ES0182 2.1.2 */
if (dev != DEV_F405_407)
return REV_UNKNOWN;
Модель повторяет эту функцию: 0x1007 6413 — F405/407 ревизии 2; 0x2000 0411 с ядром 0xC24 — ревизия A, а с ядром 0xC23 — F2 ревизии B; 0x1008 6413 — кода нет в таблице, и функция честно отвечает «неизвестно».
39б.3 Сводная таблица: что исправлено
Сводка — это таблица «пункт × ревизия». Отметки в ES0182: A — обход есть, N — обхода нет, P — обход частичный, прочерк — исправлено. В новых перечнях, как ES0206, добавилась D — ошибка документации, а прочерк означает «у этой ревизии ошибки нет».
Модель разобрала таблицу 4 ES0182 целиком — 68 пунктов в 15 группах (система — 17, USART — 9, I²C и SDIO — по 6, остальные меньше) и ещё две ошибки самого ядра Cortex-M4 в разделе 1:
| Ревизия | A — обход есть | N — обхода нет | P — частичный | нет ошибки |
|---|---|---|---|---|
| A | 52 | 14 | 1 | 1 |
| 1, 2, 4, 5, 6, Y, Z | 51 | 14 | 1 | 2 |
Главный вывод: за девять с лишним лет редакций (первая — сентябрь 2011, тринадцатая — январь 2021) у поздних ревизий исправлены два пункта из 68. По ревизиям различаются всего три строки: 2.1.1 (предвыборка флеша у A), 2.1.2 (код устройства у A) и 2.1.7 (вывод PDR_ON у Z и позже — обратная ситуация: ошибка появилась). Почти всё остальное — поведение кристалла навсегда.
У F42x картина живее: в таблице 3 ES0206 — 85 строк, у ревизии A отмечено 84, у последней (4, 5 и B) — 74; одиннадцать пунктов ревизии A в последней исправлены, один (2.3.10, пропуск пакетной записи в PSRAM) появился с ревизии 1. Сам перечень тоже не без ошибок: номер 2.10.2 записан в сводке дважды.
39б.4 Как устроен пункт
Каждый пункт — заголовок и два абзаца: Description (что и при каких условиях) и Workaround (что делать). Условия читают внимательно: они сужают беду. Пункт 2.5.3 ES0182 — нарушение времени повторного START у I²C — бывает только у ведущего, в стандартном режиме от 88 до 100 кГц; обход — опуститься до 88 кГц или перейти в быстрый режим. Пункт 2.5.5 — фронты I²C при шине 5 В: при VDD 3,3 В напряжение шины не выше (VDD + 0,3) / 0,7 = 5,14 В.
Пример целиком — пункт 2.1.13 «Delay after an RCC peripheral clock enabling».
Описание: после записи бита включения такта блок оживает не сразу — через
2 такта AHB у блоков на AHB и через 1 + (делитель AHB/APB) тактов у блоков
на APB. Обходов три: команда DSB, n команд NOP или холостое чтение того
же регистра. HAL F4 выбрал третий и написал его в каждый макрос включения
такта — с тем же заголовком в комментарии:
#define __HAL_RCC_GPIOA_CLK_ENABLE() do { \
__IO uint32_t tmpreg = 0x00U; \
SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\
/* Delay after an RCC peripheral clock enabling */ \
tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\
UNUSED(tmpreg); \
} while(0U)
В заголовках HAL F4 из пака эта строка встречается 243 раза. Так выглядит обход, записанный по правилам: код рядом с причиной, причина — заголовком пункта.
39б.5 Шесть обходов в коде
Файл errata39c.c собирает обходы из ES0182 для F407 — каждый с номером
пункта и редакцией перечня. Все они без условий: по сводке эти ошибки есть
у всех ревизий.
2.1.13 — пауза после включения такта. Своё включение без HAL — запись и холостое чтение:
void gpioa_clock_on(void)
{
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
(void)RCC->AHB1ENR; /* ES0182 2.1.13 */
}
Компилятор чтение не выбросил — регистр объявлен volatile. Дизассемблер
armclang: запись, затем то самое чтение:
gpioa_clock_on: MOV r0,#0x3830
MOVT r0,#0x4002
LDR r1,[r0,#0]
ORR r1,r1,#1
STR r1,[r0,#0]
LDR r0,[r0,#0]
BX lr
2.1.17 — снятие защиты резервной области. После записи бита DBP —
холостое чтение PWR_CR. HAL делает то же в HAL_PWR_EnableBkUpAccess()
(переменная dummyread).
2.14.1 — смена последовательности АЦП. Если преобразование запущено
программой, запись в ADC_SQRx сбрасывает текущее преобразование, и само
оно не перезапустится. Обход — снова взвести SWSTART:
ADC1->SQR3 = (ADC1->SQR3 & ~ADC_SQR3_SQ1) | (ch & 0x1Fu);
ADC1->CR2 |= ADC_CR2_SWSTART; /* ES0182 2.14.1 */
При запуске от таймера ошибки нет: новая последовательность начнётся со следующего запуска.
2.15.1 и 2.15.2 — остановка ЦАП с DMA. Флаг опустошения DMAUDR поток DMA не останавливает, а снятие DMAEN не снимает висящий запрос — после перезапуска ЦАП придёт лишний. Порядок из перечня:
if (DAC->SR & DAC_SR_DMAUDR1) /* шаг 1: было ли опустошение */
DAC->SR = DAC_SR_DMAUDR1;
DAC->CR &= ~DAC_CR_DMAEN1; /* шаг 2 */
s->CR &= ~DMA_SxCR_EN; /* шаг 3 */
DMAUDR1 сбрасывается записью единицы (RM0090, DAC_SR).
2.5.6 и 2.5.2 — ложная ошибка шины I²C. Ведущий может получить BERR по ошибке; передача при этом не страдает, и перечень велит только сбросить флаг. BERR сбрасывается записью нуля. Читать SR2, чтобы узнать «ведущий ли я», в обработчике нельзя: чтение SR1, а за ним SR2 сбрасывает ADDR. Поэтому режим — флаг своего драйвера:
void I2C1_ER_IRQHandler(void)
{
if ((I2C1->SR1 & I2C_SR1_BERR) && i2c1_master)
I2C1->SR1 = ~I2C_SR1_BERR; /* ES0182 2.5.6: BERR сбрасывается записью 0 */
}
Если же чужой ведущий поставил STOP посреди байта и START больше не выходит (2.5.2), обход — программный сброс блока битом SWRST.
1.2 — VDIV и VSQRT в коротком прерывании. Ошибка ядра 776924: деление
или корень FPU длится 14 тактов, и если за это время пришло и кончилось
прерывание из одной-двух команд без команд FPU, результат не запишется.
Обход — выключить ленивое сохранение FPU, бит LSPEN (бит 30 FPCCR по адресу
0xE000EF34), или не писать обработчиков короче трёх команд, не считая
команды возврата.
Ещё один обход — только для отладки: 2.1.3, три NOP после WFI, если
сон отлаживают с выключенной предвыборкой. Дизассемблер sleep_debug_safe
— ровно WFI, три NOP и возврат.
Не всё лечится кодом. Пункт 2.1.10 (DMA2 портит данные, если одновременно обслуживает DCMI, FSMC с внешним FIFO или вывод GPIO и блок на APB) решается раскладкой потоков на этапе проекта, а четырнадцать пунктов с отметкой N обхода не имеют вовсе — их учитывают, выбирая, какими блоками пользоваться.
39б.6 Условие по ревизии и как держать обходы
Проверка REV_ID нужна редко — только для строк, где сводка у ревизий
разная. У F407 пример один, и он сталкивается с другим пунктом. У ревизии A
нет очереди предвыборки флеша (2.1.1, обхода нет), а 2.1.15 для точного
АЦП советует предвыборку выключить, кэши команд и данных оставить:
void flash_accel_setup(enum f40x_rev rev, int precise_adc)
{
uint32_t acr = (FLASH->ACR & FLASH_ACR_LATENCY) | FLASH_ACR_ICEN | FLASH_ACR_DCEN;
if (rev != REV_A && !precise_adc)
acr |= FLASH_ACR_PRFTEN;
FLASH->ACR = acr;
}
Правила, которые делают обходы понятными через год:
- Комментарий — номер, документ и редакция.
ES0182 2.1.13, а в шапке файла — «ES0182 Rev 13». Номер без документа бесполезен: тот же пункт о паузе после включения такта в ES0182 — 2.1.13, в ES0206 — 2.2.7; сбой АЦП при смене последовательности — 2.14.1 и 2.4.1. - Без условия, если сводка одинакова. Проверка ревизии там, где ошибка есть у всех, — лишний код и лишний шанс ошибиться.
- Неизвестная ревизия — не «новая, значит исправленная».
defaultвf40x_revision()возвращает «неизвестно»: при новомREV_IDсначала сверяют перечень свежей редакции, потом решают. - Обходы — поиском по одной строке. Все места находятся по
ES0182: при смене камня или редакции перечня их проверяют списком. - Перечни меняются. В редакции 9 из ES0182 убрали два пункта с пометкой, что ошибка I²C к F40x/F41x не относится. Обход по старой редакции может оказаться лишним, а новый пункт — появиться.
39б.7 Грабли
- Ищут ошибку, которой нет в своём перечне. Знаменитое залипание флага BUSY у I²C — пункт перечня F1. В ES0182 Rev 13 у I²C шесть пунктов, 2.5.1–2.5.6, и залипания BUSY среди них нет. Обход чужого перечня — повод для новой ошибки.
- Ревизия A выдаёт себя за F2. Код устройства 0x411 и REV_ID 0x2000 — это F205/207 ревизии B по RM0033. Без проверки ядра прошивка для F4 решит, что стоит на чужом камне (2.1.2).
- REV_ID прочитали на F103 без отладчика. У F103x8/xB регистр
DBGMCU_IDCODEбез отладчика недоступен, программа своей ревизии не узнает. Ревизию таких камней записывают при сборке или приёмке. - Обход «оптимизировали». Холостое чтение после включения такта выглядит
лишним, и его удаляют «за ненадобностью». Без
volatileкомпилятор сделает это сам. Комментарий с номером пункта защищает строку от людей,volatile— от компилятора. - Номер пункта без редакции. Через пару редакций номер может указать на другое — или ни на что.
- Прочитан только Workaround. Условия в Description часто сужают ошибку до одного режима — 2.5.3 бывает лишь от 88 до 100 кГц; или, наоборот, расширяют: 2.1.10 касается и GPIO как параллельного выхода.
39б.8 Итоги
- На этой машине 19 перечней ошибок в паках Keil: F2 — 1, F3 — 14, F4 — 4; перечня F1 в паке нет. Для F407 — ES0182 Rev 13, 45 страниц.
- Ревизия — по маркировке или
REV_IDвDBGMCU_IDCODE(0xE004 2000): 0x1007 у F405/407 — ревизия 2. У ревизии A код как у F2 — отличать по ядру. - ES0182: 68 пунктов плюс 2 ошибки ядра; у поздних ревизий исправлены два, по ревизиям различаются три строки. Почти все обходы нужны без условий.
- Пункт — Description с условиями и Workaround; HAL F4 несёт обход 2.1.13 в 243 строках заголовков.
- Шесть обходов в
errata39c.c: пауза после включения такта, холостое чтение PWR_CR, повторный SWSTART, порядок остановки ЦАП с DMA, сброс ложного BERR, выключение LSPEN. - Комментарий обхода — документ, редакция и номер пункта: один и тот же пункт в ES0182 и ES0206 стоит под разными номерами.
Полные исходники
errata39c.c — обходы ошибок кристалла STM32F405/407.
/* errata39c.c — раздел 39б: обходы ошибок кристалла STM32F405/407 в коде.
*
* Источник — перечень ошибок ES0182 Rev 13 (январь 2021), файл DM00037591.pdf
* в паке Keil STM32F4xx_DFP 2.17.1. Номер пункта у каждого обхода — по этой
* редакции; в других перечнях тот же пункт стоит под другим номером.
*
* Сборка (проверка раздела):
* armclang --target=arm-arm-none-eabi -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard
* -DSTM32F407xx -include stm32f4xx.h -O2 -Wall -Wextra -c errata39c.c
*/
#include <stdint.h>
/* Адреса и поля — из заголовков пака; разойдутся с документом — сборка остановится. */
_Static_assert(DBGMCU_BASE == 0xE0042000UL, "DBGMCU_IDCODE по адресу 0xE004 2000 (RM0090, 38.6.1)");
_Static_assert(DBGMCU_IDCODE_REV_ID_Pos == 16U, "REV_ID — биты 31:16");
_Static_assert(DBGMCU_IDCODE_DEV_ID_Msk == 0xFFFUL, "DEV_ID — биты 11:0");
_Static_assert(FPU_BASE + 4UL == 0xE000EF34UL, "FPCCR по адресу 0xE000EF34 (ES0182, 1.2)");
_Static_assert(FPU_FPCCR_LSPEN_Pos == 30U, "LSPEN — бит 30 FPCCR (ES0182, 1.2)");
enum f40x_rev { REV_UNKNOWN = 0, REV_A, REV_Z, REV_1, REV_2, REV_Y_4, REV_5_6 };
#define DEV_F405_407 0x413u /* RM0090, 38.6.1 */
#define DEV_F2 0x411u /* RM0033: код F205/207 */
#define PART_M4 0xC24u /* номер ядра в SCB->CPUID */
/* Ревизия кристалла по DBGMCU_IDCODE.
* ES0182 2.1.2 «MCU device ID is incorrect» (только ревизия A): код устройства — как у F2,
* REV_ID — 0x2000 вместо 0x1000. Обход из перечня: отличать F4 от F2 по ядру. */
enum f40x_rev f40x_revision(void)
{
uint32_t id = DBGMCU->IDCODE;
uint32_t dev = id & DBGMCU_IDCODE_DEV_ID_Msk;
uint32_t rev = id >> DBGMCU_IDCODE_REV_ID_Pos;
uint32_t part = (SCB->CPUID & SCB_CPUID_PARTNO_Msk) >> SCB_CPUID_PARTNO_Pos;
if (dev == DEV_F2 && rev == 0x2000u && part == PART_M4)
return REV_A; /* ES0182 2.1.2 */
if (dev != DEV_F405_407)
return REV_UNKNOWN;
switch (rev) { /* RM0090, 38.6.1 */
case 0x1000u: return REV_A;
case 0x1001u: return REV_Z;
case 0x1003u: return REV_1;
case 0x1007u: return REV_2;
case 0x100Fu: return REV_Y_4;
case 0x101Fu: return REV_5_6;
default: return REV_UNKNOWN; /* новая ревизия: сверить с перечнем */
}
}
/* Флеш: ES0182 2.1.1 «ART Accelerator prefetch queue instruction is not supported» —
* у ревизии A очереди предвыборки нет (обхода нет, исправлено в Z и 1).
* ES0182 2.1.15 «Internal noise impacting the ADC accuracy» — для точного АЦП
* предвыборку выключить, кэши команд и данных оставить. */
void flash_accel_setup(enum f40x_rev rev, int precise_adc)
{
uint32_t acr = (FLASH->ACR & FLASH_ACR_LATENCY) | FLASH_ACR_ICEN | FLASH_ACR_DCEN;
if (rev != REV_A && !precise_adc)
acr |= FLASH_ACR_PRFTEN;
FLASH->ACR = acr;
}
/* ES0182 2.1.13 «Delay after an RCC peripheral clock enabling»: после включения такта
* нужна пауза — обход 3 из перечня: холостое чтение того же регистра. */
void gpioa_clock_on(void)
{
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
(void)RCC->AHB1ENR; /* ES0182 2.1.13 */
}
/* ES0182 2.1.17 «Possible delay in backup domain protection disabling/enabling after
* programming the DBP bit»: холостое чтение PWR_CR сразу после записи DBP. */
void backup_domain_unlock(void)
{
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
(void)RCC->APB1ENR; /* ES0182 2.1.13 */
PWR->CR |= PWR_CR_DBP;
(void)PWR->CR; /* ES0182 2.1.17 */
}
/* ES0182 2.14.1 «ADC sequencer modification during conversion»: при запуске программой
* смена SQRx сбрасывает текущее преобразование, и само оно не перезапускается —
* взвести SWSTART ещё раз. */
void adc1_set_first_channel(uint32_t ch)
{
ADC1->SQR3 = (ADC1->SQR3 & ~ADC_SQR3_SQ1) | (ch & 0x1Fu);
ADC1->CR2 |= ADC_CR2_SWSTART; /* ES0182 2.14.1 */
}
/* ES0182 2.15.1 и 2.15.2: DMAUDR поток DMA не останавливает, а DMAEN=0 не снимает
* запрос — порядок остановки DAC с DMA из перечня. */
void dac1_dma_stop(DMA_Stream_TypeDef *s)
{
if (DAC->SR & DAC_SR_DMAUDR1) /* шаг 1: было ли опустошение */
DAC->SR = DAC_SR_DMAUDR1;
DAC->CR &= ~DAC_CR_DMAEN1; /* шаг 2 */
s->CR &= ~DMA_SxCR_EN; /* шаг 3 */
while (s->CR & DMA_SxCR_EN) { }
/* шаги 4–5: заново настроить DAC, DMA, запуск — в вызывающем коде */
}
/* ES0182 2.5.6 «Spurious Bus Error detection in Master mode»: ведущему — только
* сбросить BERR, передача идёт дальше.
* ES0182 2.5.2 «Start cannot be generated after a misplaced Stop»: обход — SWRST.
* SR2 здесь не читаем: чтение SR1, а за ним SR2 сбрасывает ADDR (RM0090, I2C_SR1),
* поэтому «ведущий ли мы» — флаг своего драйвера. */
volatile uint8_t i2c1_master; /* 1 от START до STOP */
void I2C1_ER_IRQHandler(void)
{
if ((I2C1->SR1 & I2C_SR1_BERR) && i2c1_master)
I2C1->SR1 = ~I2C_SR1_BERR; /* ES0182 2.5.6: BERR сбрасывается записью 0 */
}
void i2c1_soft_reset(void) /* ES0182 2.5.2 */
{
I2C1->CR1 |= I2C_CR1_SWRST;
I2C1->CR1 &= ~I2C_CR1_SWRST;
/* дальше: заново записать CR2, CCR, TRISE и включить PE */
}
/* ES0182 1.2 «VDIV or VSQRT instructions might not complete correctly when very short
* ISRs are used» (ошибка ядра 776924): обход 1 — выключить ленивое сохранение FPU. */
void fpu_lazy_off(void)
{
FPU->FPCCR &= ~FPU_FPCCR_LSPEN_Msk; /* ES0182 1.2 */
}
/* ES0182 2.1.3 «Debugging Sleep/Stop mode with WFE/WFI entry»: три NOP после WFI. */
void sleep_debug_safe(void)
{
__WFI();
__NOP(); __NOP(); __NOP(); /* ES0182 2.1.3 */
}
Дальше: раздел 39в «Даташит чужой микросхемы → драйвер: HTS221 от первой страницы до проверки без платы» — как из даташита датчика вырастает драйвер и как проверить его без платы.