beu1000-evuba — Claude Code skill for BE-U1000 firmware
A skill for firmware work on BE-U1000 and the EVU-BA boards: memory map, boot modes, peripherals, and failures that look like something else.
npx skills add alexex1993/mcu-skills --skill beu1000-evuba -a claude-codeWhat beu1000-evuba does
The skill gathers what you would otherwise piece together from the reference manual, the board manual and a week of debugging. It covers the BE-U1000 die with its three RISC-V cores (BR-350 and BM-310), the memory map with eFlash and TCM, clocking, the XP1/XP8/XP9/XP10 headers, and the boot straps. Inside there is a hardware reference, a set of ready-made recipes, and a working template/ project built in four combinations (--full/--minimal × eFlash/TCM).
A separate section lists rules taken from real failures. Interrupt acknowledgement differs per block: for PWMA it is PWMA_ClearIT(), and that call has to be the last line of the handler, because clearing status flags does not release the request line. ADC sampling by interrupt does not work at all, only DMA. CANFD_RequestMode() from the SDK can silently fail and leave the node mute. SPI0 and SPI2 are slaves even though SPI_Init() returns without an error.
The boot mode is latched by XP1 jumpers before reset and no software setting changes it, which explains a good share of “the board does not work” cases. You need Baikal's SDK with its make-based build, the riscv32-none-elf toolchain, and dfu-util or XMODEM over UART0 for flashing. Debugging goes over JTAG through the on-board FT2232HL or an external probe.
What it helps with
- set the boot mode with XP1 jumpers before reset
- find out why a working board stays silent
- run ADC sampling over DMA without losing conversions
- configure CAN FD with the bit timing worked out
- flash the board over UART XMODEM or USB DFU
Example prompts for the agent
- Build the
template/project in--minimalmode for TCM - Why does PWMA fire exactly once on an edge trigger?
- Check my Makefile and
MEM_REG_ROMfor SDK 2.3
How to install beu1000-evuba
1Skills CLI (Vercel)
npx skills add alexex1993/mcu-skills --skill beu1000-evuba -a claude-code2Manually: put the files in place
~/.claude/skills/beu1000-evuba/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/beu1000-evuba/
1Skills CLI (Vercel)
npx skills add alexex1993/mcu-skills --skill beu1000-evuba -a codex2Manually: put the files in place
.agents/skills/beu1000-evuba/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add alexex1993/mcu-skills --skill beu1000-evuba -a cursor2Manually: put the files in place
.cursor/skills/beu1000-evuba/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install alexex1993/mcu-skills beu1000-evuba2Manually: put the files in place
.github/skills/beu1000-evuba/SKILL.md1Skills CLI (Vercel)
npx skills add alexex1993/mcu-skills --skill beu1000-evuba -a gemini-cli2Platform CLI
gemini skills install https://github.com/alexex1993/mcu-skills --path skills/baikal/beu1000-evubaManually: put the files in place
.devin/skills/beu1000-evuba/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add alexex1993/mcu-skills --skill beu1000-evuba -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about beu1000-evuba
How do I install beu1000-evuba in Claude Code?
Run npx skills add alexex1993/mcu-skills --skill beu1000-evuba -a claude-code, then confirm the file landed at ~/.claude/skills/beu1000-evuba/SKILL.md.
What does beu1000-evuba do?
It helps you write and debug firmware for BE-U1000 on EVU-BA boards: pinout, boot modes, eFlash and TCM, interrupts and CLIC, and peripherals such as PWMA, ADC, CAN FD and SPI.
How is this different from reading the manuals?
It collects failures that looked like something else: interrupt acknowledgement is block-specific, ADC_StartCalibration() does not calibrate, and SPI0 and SPI2 turn out to be slaves after a clean init.
Source text
SKILL.md · MIT
---
name: beu1000-evuba
description: Разработка прошивок для BE-U1000 (Baikal Electronics / Байкал Электроникс, три ядра RISC-V: BR-350 и BM-310) на отладочных платах EVU-BA-2.1, EVU-BA-2.0 и EVU-BA-1.2 — её eFlash и QSPI flash, UART0/BootROM CLI, PWMA (захват и импульсы), АЦП с DMA, CAN FD, SPI, WDT, CLIC, а также SDK Байкала и его система сборки на make. Use when working on this board or any BE-U1000 / BMCU_U project (SDK 2.1/2.3, riscv32-none-elf, dfu-util, OpenOCD): настройка проекта, Makefile и MEM_REG_ROM, карта выводов и разъёмы XP1/XP8/XP9/XP10, режимы начальной загрузки и страповые выводы, прошивка по UART XMODEM либо USB DFU, работа с eFlash и TCM, прерывания и CLIC, отладка по JTAG — и разбор, почему плата молчит, не стартует, зависает или почему периферия «настроилась», а обмена нет.
---
# BE-U1000 на плате EVU-BA-2.1
Знание об этой плате и этом кристалле, которое иначе собирается по RM,
техспецификации, ТО платы и неделе отладки. Почти все отказы здесь **тихие**:
плата работает, просто не так — или молчит, будучи исправной. Поэтому детали
лежат в справочных файлах, и их следует читать, а не додумывать.
- `reference/board-hardware.md` — полный справочник по железу: карта выводов и
разъёмов, питание, сброс, режимы загрузки, карта памяти, тактирование, обзор
SDK, **плюс** часть II: система сборки, прерывания, поваренная книга по
блокам, прошивка и таблица «признак → причина → лечение».
- `reference/recipes.md` — готовый код, извлечённый из `template/`: Makefile,
консоль, временная база, захват, импульс, АЦП+DMA, CAN FD, SPI, eFlash, WDT.
- `template/` — **работающий проект**: собран в четырёх сочетаниях
(`--full`/`--minimal` × eFlash/TCM) и **прогнан на плате** 2026-08-31 —
светодиод, консоль, меню, захват, импульс, АЦП. Плюс сценарий развёртывания.
Смотри `template/README.md`.
## Ориентация
| | |
|---|---|
| Кристалл | BE-U1000, QFN-88. **Три ядра RISC-V**: 0 и 1 — CloudBEAR BR-350 200 МГц (`ilp32f`), 2 — BM-310 100 МГц, **без FPU** (`ilp32`) |
| Память | eFlash 256 КБ `0xA0000000`, TCMA 64 КБ `0x40010000`, TCMB 96 КБ `0x40020000`, SRAM 32 КБ `0x70000000`, QSPI1 (плата, 16 МБ) `0x90000000` |
| Тактирование | генератор G1 **25 МГц** на `CLKI` (кварца у кристалла нет). Перемычка `CLK` на XP1 переключает на внутренний RC-генератор. PLL по умолчанию не включена |
| Светодиод / кнопка | LD1 `PC0` **активен высоким** / SB1 `PC13` **нажата = ноль**. SB2 — аппаратный сброс |
| Консоль и прошивка | **UART0 `PA6`/`PA7`, АФ #1**, разъём XS2 «JTAG UART0» (FT2232HL). Это единственный UART, выведенный на USB |
| Аналоговые входы | **8 штук, `VIN0…VIN7`** — выделенные выводы, XP8 конт. 28, 30, 32, 34, 36, 38, 35, 37; «AREF» — 7. Три АЦП 12 бит 1 MSps делят их |
| Импульсные блоки | 4 × PWMA (16 бит, захват, одиночный импульс, dead-time), 2 × PWMG, 2 × TIM (32 бита, 4 канала) |
| Шины | 2 × CAN FD (**трансивера на плате нет**), 4 × SPI (**ведущими умеют только SPI1 и SPI3**), 4 × I2C, до 7 UART, USB OTG (XS1) |
| Отладка | JTAG отладчиком на плате (тот же FT2232HL) либо внешним через XP6; **JTAG разведён на `PA0…PA4`** |
| Порты на гребёнках | XP9 — весь порт A, XP10 — весь порт B, XP8 — весь порт C плюс питание и входы АЦП |
## Сначала — режим загрузки, потом всё остальное
Режим защёлкивается перемычками XP1 **до сброса**; ни одна программная
настройка его не меняет. Половина «плата не работает» — это не тот режим.
| XP1 `DBG-M2-M1-M0` | Режим | Когда нужен |
|---|---|---|
| `0000` (все сняты) | **EFLASH** | боевой: запуск программы из eFlash |
| `0001` (только `M0`) | **UART** | прошивка и BootROM CLI по UART0 |
| `0011` (`M0`+`M1`) | **USB** | прошивка по DFU либо CLI по USB CDC (кабель в XS1) |
| `0010` | QSPI | запуск из внешней QSPI flash |
| `0100` / `1100` | JTAG EXT / JTAG INT | отладчик на плате или внешний / встроенный |
| `0110` | MULTI | перебор EFLASH → QSPI → USB CDC → UART |
Отдельная перемычка `CLK`: снята — генератор 25 МГц (норма), установлена —
внутренний RC-генератор. ⚠ Его диапазон в документах **разный**: техспецификация
кристалла v1.5.5 трижды называет **12…32 МГц**, ТО платы v2.1.2 — 20…40 МГц.
Кому верить, по бумагам не решить; на плате не мерено. Питание выбирается
перемычкой XP11: `USB` / `EXT5V` / `VIN` (10…48 В). Полные таблицы и рисунки —
`reference/board-hardware.md` §3.
## Правила, которые экономят дни (rules & pitfalls)
Каждое — из отказа, который **выглядел не тем, чем был**.
1. **Подтверждение приёма прерывания у каждого блока своё, и найти его надо
до отладки, а не после.** У PWMA/PWMG это `PWMA_ClearIT()` — разряд 15
регистра **разрешений** `DIER`, и он обязан быть **последней** строкой
обработчика; гашение флагов состояния линию запроса не снимает. У ADC —
`CR1.CLRINTRPT`. У CAN FD — запись единицы в `ICR`. У TIM — `TIM_ClearIT()`.
У UART подтверждения нет вовсе. Без него при запуске по уровню плата
«зависает» (обработчик вызывается бесконечно, очередь UART перестаёт
уходить), при запуске по фронту срабатывает **ровно один раз**, а регистр
состояния внутри обработчика читается нулём. Ни один пример SDK 2.1
`PWMA_ClearIT()` не вызывает — искать нечего, надо знать.
2. **Сбор с АЦП по прерыванию невозможен — только DMA.** Запрос снимается
единственно записью в `CR1`, а любая запись в `CR1` обрывает идущую
последовательность (там же `ADON` и `SWSTART`). Подтверждать на каждом
ранге — терять преобразование (измерено: 3 ранга из 6), не подтверждать —
вешать плату. С DMA всё сходится: 0 потерь из 500, **18 мкс на канал**.
Прерывание от самого АЦП не разрешается вовсе.
3. **`ADC_StartCalibration()` — не калибровка.** Она выставляет `ASER.CAL_SEL`,
а `ADC_IsCalibrationOnGoing()` читает тот же разряд обратно; железо его не
снимает, поэтому ожидание вечно **по построению** и плата встаёт молча. По
RM единица подменяет вход внутренним нулём, и «ноль» этот равен 1400…1700
отсчётов — середина шкалы. **Вычитать его нельзя:** данные превращаются в
правдоподобные нули («датчики не подключены»).
4. **`CANFD_RequestMode()` из SDK молча не срабатывает.** Она снимает `SRR.CEN`
и тут же проверяет режим; не успев — пропускает настройку, а `CEN` уже снят.
Узел нем: не передаёт, не принимает, ошибок не считает, и выглядит это как
«кадры уходят, никто не отвечает». Повторять запрос, пока `GetMode()` не
подтвердит.
5. **Тактовая CAN — 12,5 МГц, а не 25.** На 500 кбит/с приходится ровно 25
квантов на бит; привычные фиксированные 16 не делят это нацело, и шина не
открывается вовсе. Число квантов **считать**, точка выборки ~75 %.
6. **SPI0 и SPI2 — ведомые.** В именах регистров, в структурах и в HAL
разницы нет: `SPI_Init()` проходит без ошибок, а обмен даёт таймаут. Улика —
`BAUDR` и `SER` читаются нулями при записанных значениях. Различить можно
только по таблице выводов RM: `MSPI1`, `MSPI3` против `SSPI0`, `SSPI2`.
Отсюда правило шире одного блока: **прочитать обратно то, что записал**.
7. **С обычного ОЗУ (`0x70000000`) выборка команд не идёт.** Исполняемое ОЗУ
здесь одно — **TCMA `0x40010000`**. Функция, положенная в `.data`, просто не
возвращается; ядро уходит в трап с запрещёнными прерываниями, и наружу это
выглядит как обрыв вывода на середине строки.
8. **Код, который пишет eFlash, обязан исполняться из TCM.** Массив недоступен,
пока идёт его собственная операция, — и для данных, и для команд: холодный
проход по 4 КиБ кода во время стирания сектора занял **3408 мкс** против 85
вхолостую. Отказ не падение, а простой ядра на 3,3–4,1 мс и потерянные байты
UART. Удобный перенос — секция с именем **без точек** (`beu_nvmfunc`): для
таких компоновщик сам заводит `__start_…`/`__stop_…`.
9. **`common.mk` не следит за заголовками.** `DEPS` собирается как `main.d`, а
компилятор пишет `main.o.d`, и `-include` молча берёт пустоту. Правка общего
заголовка не вызывает пересборку, а на плате потом **отказывает не то, что
менялось** (у нас так объект устарел на 12 байт структуры, и плата умирала на
первом куске прошивки). Лечение — `-include $(wildcard $(OBJ_DIR)/*.o.d)` в
своём Makefile либо `make clean` после каждой правки заголовка.
10. **SDK переиспользует уже сгенерированный `output/debug/generated.ld`**
(`LD_SCRIPT := firstword $(wildcard $(PRJ_DIR)/*.ld) $(wildcard $(OUT_DIR)/*.ld)`).
Поэтому `MEM_REG_ROM=TCMA` после обычной сборки **молча не действует**:
предупреждений нет, образ уезжает не в ту память, на плате это «прошилось,
но не стартует». Вместе с `ORIGIN` не меняется и **кодовое слово для
BootROM** — а оно у каждой области своё (см. «Прошивка»), поэтому плата
молча уходит в режим USB. Перед сменой области — `make clean`; проверять
`ORIGIN` в `generated.ld` (под TCM должно быть `0x40010000`).
11. **Открытие `/dev/ttyUSB0` сбрасывает плату, и софтом это не обходится.**
Вывод ADBUS4 микросхемы FT2232HL заведён на цепь сброса; проверены обе
полярности DTR и обе RTS — сбрасывают все четыре, даже когда порт уже держит
открытым другой процесс. Следствие полезное: сброс перед прошивкой можно
отдавать линией DTR, кнопка SB2 не нужна. Следствие вредное: всякая правка,
живущая в ОЗУ, теряется при каждом подключении.
12. **В режиме `EFLASH` BootROM не поднимает UART0 и не печатает.** Его
`printf` по `0x4000b082` работает только там, где он сам ведёт консоль, то
есть в режимах `UART` и `USB CDC`. Программа, полагающаяся на него, в боевом
режиме молчит при полной исправности — а отлаживать это в режиме `UART`
бесполезно, там отказ не воспроизводится. Порт поднимать самому.
13. **Прерывания не вкладываются.** Обработчик GCC (`__attribute__((interrupt))`)
входит с закрытым `mstatus.MIE` и наружу его не открывает; уровни CLIC задают
лишь порядок выбора среди ожидающих. Значит **бюджет считается по самому
длинному обработчику в системе**, а не по своему: измеренный провал главного
цикла — 850…1300 мкс. И `float` в обработчике опасен: GCC сохраняет только
те регистры, которые использует сам.
14. **`CLIC_Config(1, 1)` даёт по одному разряду на уровень и приоритет.**
Значение 2 в `CLIC_ConfigIRQ()` при этом не «понизится» — прерывание не
сработает вовсе. И `__enable_irq()` забыть так же легко: обе ошибки дают
молчащую плату, неотличимую от зависшей.
15. **Потолок сторожевого таймера — 335 мс** (2^23−1 такта на 25 МГц), то есть
670 мс до перезагрузки в режиме с прерыванием. ⚠ В заголовке SDK список
периодов набран с опечаткой начиная с четвёртого значения (`0xFFFF` вместо
`0xFFF`) и завышает потолок **в 16 раз**. Регистра причины сброса у кристалла
нет — отметку держать в ОЗУ (например по `_heap_start`).
16. **Страповые выводы `PA10`, `PB10`, `PC4`, `PC6`, `PC8` нельзя подтягивать
вверх** — ни драйвером, ни преобразователем уровня. Внутри они подтянуты к
земле через 42 кОм, и это и есть режим `EFLASH`; внешняя единица уводит
собранную плату в чужой режим загрузки молча. `MODE[0]` при этом сидит на
`SPI1_MOSI`, а `MODE[1]` — на `SPI3_MOSI`: у обоих ведущих SPI линия MOSI
страповая, обойти нечем.
17. **Восемь аналоговых входов — предел кристалла, а не платы.** `VIN0…VIN7`
выделенные, у всех трёх АЦП общие, альтернативной функцией портов АЦП не
бывает ни на одном выводе. Нужно больше — только внешний мультиплексор
(`CD74HC4051` + буферный ОУ: `Ron` 100…200 Ω требует низкого импеданса
источника).
18. **Тактирование порта GPIO включается отдельно от тактирования периферии,
и без него вывод к ней не подключается.** `CRU_PIN_Init()` при этом
отрабатывает молча, регистры таймера верны, провод исправен — а захват не
видит ни одного фронта. Измерено на плате 2026-08-31: захват на `PB8` не
считал ничего, пока не добавили `CRU_APB1_EnableClock(CRU_APB1_PERIPH_GPIO1)`;
выход на `PC5` при этом работал — но лишь потому, что порт C уже
тактировался ради светодиода. Порты сидят на разных шинах:
A — `CRU_APB0_PERIPH_GPIO0`, B — `CRU_APB1_PERIPH_GPIO1`,
C — `CRU_APB2_PERIPH_GPIO2`.
19. **DMA не имеет доступа к TCM — ни к TCMA, ни к TCMB.** Буфер DMA обязан
лежать в SRAM (`0x70000000`). Измерено 2026-08-31 на трёх адресах: с
приёмником в TCMB (`0x40020400`) и в TCMA (`0x4001F000`) канал включается,
делает одну исходную транзакцию, ловит ошибку шины (`RAW_ERR = 1`) и
**сам выключается**, не подняв флага завершения; с приёмником в SRAM те же
четыре канала АЦП собираются за 66 мкс. Ловушка тихая вдвойне: наружу это
«АЦП не отдаёт результат», а конфигурация DMA и АЦП при этом верна. И она
подстерегает ровно там, где кажется безопасно: при сборке под TCM
(`MEM_REG_ROM=TCMA`) переменная `MEM_REG_RAM` по умолчанию тоже уводит
данные в TCM — для DMA нужно **`MEM_REG_RAM=SRAM`**.
20. **Ядро 2 (BM-310) — без FPU и практически без libc.** Тулчейн SDK собран без
мультибиблиотеки `rv32imc/ilp32`, и `<string.h>` там просто нет: сборка
падает с `fatal error: string.h: No such file or directory`, тогда как
хостовая и оба других ядра проходят. TCM у него свой и крошечный: 2 КБ + 8 КБ.
## Когда задача — импульсы и захват (угловой домен, ШИМ, энкодер)
Это то, ради чего берут PWMA, и наивный подход здесь неверен.
**Импульс складывается из двух совпадений.** У PWMA на канал один регистр
сравнения: `CCR = t_on` в режиме `ACTIVE` даёт фронт включения, затем в
обработчике `CCR = t_off` в режиме `INACTIVE` — фронт выключения. **Оба фронта
делает компаратор**, задержка обработчика в джиттер не превращается — она лишь
ограничивает снизу длительность импульса. Измерено: ошибка ширины **0 нс**,
джиттер 120…320 нс, постоянный сдвиг −1,8 мкс (одинаков для обоих фронтов,
ширины не касается).
**Счётчик 16-разрядный: на 25 МГц окно всего 2,62 мс.** Цель дальше окна
требует **прицельного пробуждения** — совпадения на заведомо достижимой точке, по
которому остаток пересчитывается. Ожидание переполнения вместо этого даёт
слепую полосу: цель чуть дальше окна перепрыгивает через ноль и оказывается «в
прошлом». И при перезарядке обязательно переставлять значение сравнения: старое,
оставшееся от предыдущего фронта, срабатывает как фронт выключения — импульс 5 мс
выходил короче ровно на период счётчика.
**Предзагрузка регистра сравнения (`preload`) — противоположные решения для
разных задач.** Для события (впрыск, искра) её надо **выключить**: цель,
записанная за 100 мкс до срабатывания, вступила бы в силу только через 2,6 мс.
Для ШИМ — **включить**: иначе новая скважность действует посреди импульса, и это
скачок тока в нагрузке на каждое обновление регулятора.
**Захват и вывод — на разных блоках PWMA.** У PWMA **один вектор прерывания на
все каналы блока**: на одном блоке перезарядка компаратора вывода исполнялась бы
внутри обработчика захвата. Выбор к тому же задан выводами: на порту B (XP10)
из блоков PWMA есть только PWMA1, PWMA2/PWMA3 сидят на порту C (XP8).
Числа, по которым считается запас: захват даёт джиттер **80 нс** через
прерывание; **оба фронта на одном канале — не чаще одного фронта в 100 мкс**
(полярность переключается в обработчике), причём пропущенный фронт даёт не отказ,
а **правдоподобный мусор** — период кратный, скважность произвольная; зарядка
канала вывода стоит **38 мкс**. Проверять это можно без генератора: перемычка с
выхода PWMA2 на вход PWMA1 (`PC5` XP8.6 → `PB8` XP10.13) — плата меряет себя
сама, и честно, потому что блоки разные.
## Когда задача — аналоговые входы
Кроме правил 2, 3, 17 и **19 (буфер DMA только в SRAM)**: у этого АЦП **нет
одновременной выборки**. Секвенсор
идёт по рангам, и последний канал группы отстаёт от первого на **90 мкс** при
шести каналах. Взаимозависимые величины (например положение и ток) ставить
соседними рангами. Две группы можно вести параллельно — контроллеры ADC0 и ADC1
независимы, входы у них общие. Линии DMA выделенные: у DMA_0 линия 13 —
`ADC_0 RX`, линия 15 — `ADC_1 RX` (RM табл. 8-1).
## Новый проект
Руками не собирать. `template/` — проверенный работающий проект:
```sh
~/.claude/skills/beu1000-evuba/template/variants/new-project.sh <каталог> [--full|--minimal]
cd <каталог> && make BAIKAL_SDK=$HOME/путь/к/SDK_2_1
```
- `--full` (по умолчанию) — консоль, временная база, захват, импульс, АЦП+DMA,
меню по UART0. **25 536 Б** из 256 КБ eFlash, ОЗУ 536 Б.
- `--minimal` — только мигание LD1. **8 484 Б**, ОЗУ 424 Б. С него начинать на
незнакомой плате: он отделяет отказ тракта прошивки от отказа своего кода.
Обе сборки проверены, как и вариант под TCM. ⚠ Под TCM собирать так:
`MEM_REG_ROM=TCMA MEM_REG_RAM=SRAM make` — **данные именно в SRAM**, иначе
буфер DMA окажется в TCM и АЦП замолчит (правило 19). И не забыть `make clean`
при смене области — правило 10.
`template/README.md` расписывает файлы по подсистемам. Если проект у
пользователя уже есть — приводить в порядок его Makefile по образцу шаблона,
а не переписывать код.
## Прошивка (flashing)
**Путь по умолчанию — BootROM CLI по UART0**, тем же кабелем, что и консоль.
Перемычки XP1: установлена только `M0` (режим `UART`), кабель USB Type-C в XS2,
питание выбрано на XP11.
```
d вход в CLI (ROM отвечает баннером и приглашением @)
14000018:1w снять защиту eFlash от стирания и записи
a0000000:40000:0:100m адрес, максимальный размер, настройки тактирования
:::101m стереть eFlash
:::3L ждать образ; ROM отвечает 'C' — запрос XMODEM/CRC
<XMODEM 128-байтными блоками, CRC-16>
a0000000g запуск с адреса eFlash
```
В TCM (`0x40010000`) — та же последовательность **без** снятия защиты и
стирания, команда загрузки `l`. Запись в TCM не трогает энергонезависимую
память и пропадает при снятии питания, поэтому первую проверку платы разумно
начинать с неё.
**Почему залитый образ обязан быть собран под ту же область.** BootROM в режиме
`EFLASH` ищет по адресу `0xA0000020` кодовое слово `0x0BEF5262` и, найдя,
передаёт управление на `0xA0000030`; **не найдя — уходит в режим `USB`**
(руководство пользователя §3.2.1). Кодовое слово кладёт система сборки SDK, и
оно **разное для каждой области**: `EFLASH 0x0BEF5262`, `TCMA 0x0B10D522`,
`TCMB 0x0B10DDA2`, `QSPI1 0x00000000` (`Tools/build/service.mk`). Заголовок
образа — 48 байт: по смещению 0 стоит `jal x0, 48` (перепрыгнуть заголовок),
`0x20` — кодовое слово, `0x28`/`0x2C` — адрес и размер копирования.
Отсюда два следствия. Собранный чужой системой сборки «голый» бинарник плата
сама не запустит — стартовать он будет только командой `g` из CLI. И образ,
собранный под другую область памяти, тоже не запустится, даже если залит по
верному адресу: кодовое слово не то. Поведение при этом обманчиво мягкое —
плата не виснет, а молча уходит в режим USB.
Кнопку RESET нажимать не нужно: `DTR#` FT2232HL заведён на цепь `DTR_RST` и
формирует сброс наравне с кнопкой (правило 11). Скрипт нашего проекта —
`tools/flasher/baikal_uart_flash.py`.
**Второй путь — USB DFU:** XP1 в `0011`, кабель в XS1 (USB OTG). Кристалл
поднимает DFU-устройство на стеке TinyUSB с идентификаторами **`cafe:2303`**
(проверять `lsusb`), заливка — `dfu-util --alt 2 --download образ.bin`. По
умолчанию устройство принадлежит root, и `dfu-util` падает с
`LIBUSB_ERROR_ACCESS`; лечится правилом udev
(`SUBSYSTEM=="usb", ATTR{idVendor}=="cafe", ATTR{idProduct}=="2303", TAG+="uaccess"`).
Путь нужен для больших образов (256 КБ по UART идут около 25 секунд), но требует
**второго кабеля** в XS2 для сброса: питание FT2232HL идёт оттуда, и без него
линия DTR мертва.
**Третий путь — JTAG:** XP1 в `0100` (отладчик на плате, XS2) либо внешний
отладчик в XP6 при любом питании, кроме XS2. Нужен для точек останова и для
подъёма платы, у которой испорчено всё остальное.
**Оживление.** Испортить плату прошивкой нельзя: режим загрузки задаётся
перемычками до сброса и от содержимого eFlash не зависит. Оборванная заливка
означает лишь негодный образ — вернуть XP1 в `UART` и залить заново.
## Отчётность
Говорить прямо, что проверено на железе, а что взято из документации. Здесь это
разделено так:
| Происхождение | Что именно |
|---|---|
| **Измерено на плате шаблоном этого скила** (2026-08-31, ядро 0, 25 МГц, образ в TCM, данные в SRAM) | мигание LD1 из TCM; консоль и меню по UART0; тактовые CCLK/PCLK0/1/2/HCLK = 25 МГц; импульс PWMA2 доходит до конца, каждый ловится захватом PWMA1 через перемычку `PC5`→`PB8`; АЦП+DMA — 4 канала за **66 мкс** (16,5 мкс на канал); **тактирование порта GPIO обязательно** (правило 18); **DMA не достаёт до TCM** (правило 19) |
| **Измерено на плате** (порт EHAL проекта ЭБУ, EVU-BA-2.1, ядро 0, 25 МГц без PLL) | джиттер захвата 80 нс; предел обоих фронтов 100 мкс; ширина импульса и джиттер вывода; зарядка канала 38 мкс; провал главного цикла 850…1300 мкс; АЦП 18 мкс на канал и 0 потерь из 500; смещение нуля АЦП; времена и зерно eFlash; тактовая CAN 12,5 МГц; сброс платы при открытии порта; потолок и поведение WDT |
| **Проверено сборкой** | размеры и области шаблона (25 536 Б и 8 484 Б, `ORIGIN` под TCM), поведение `generated.ld` и `*.o.d`, кодовое слово в готовом образе (`xxd -l 0x30`: `0x0BEF5262` под eFlash, `0x0B10D522` под TCMA) |
| **Сверено с первоисточником** | карта «вывод → контакт» разъёмов XP8/XP9/XP10 (ТО платы, рис. 3-6…3-8, сверено по рисункам); номера альтернативных функций (RM, рис. 5-20); страповые выводы и режимы загрузки (техспец. табл. 3-5, ТО платы табл. 3-3); карта памяти (`Tools/build/platform/BE_U1000/mem_map.mk`); заголовок образа и кодовые слова (`ldscript_template.ld`, `service.mk`); поведение BootROM (руководство пользователя §3.2.1) |
| **Не проверено ничем** | выводы CAN `PA14`/`PA15` и уровни на них — **трансивера на плате нет**, проверялась только внутренняя петля; диапазон внутреннего RC-генератора (документы расходятся); заливка шаблона в eFlash (проверялась только TCM: в eFlash платы лежит чужая прошивка, и стирать её ради опыта незачем) |
Отдельно держать в уме нерешённое: времена eFlash из приложения вдвое больше
измеренных из TCM (8253 мкс против 4061 на стирание блока), причина не
установлена, обе цифры приведены рядом намеренно; флаг повторного захвата
`CC0OF` взводится на каждом захвате при нулевых потерях, расхождение с RM не
выяснено.
© alexex1993 · MIT · alexex1993/mcu-skills