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)
Каждое — из отказа, который выглядел не тем, чем был.
- Подтверждение приёма прерывания у каждого блока своё, и найти его надо
до отладки, а не после. У PWMA/PWMG это
PWMA_ClearIT() — разряд 15
регистра разрешений DIER, и он обязан быть последней строкой
обработчика; гашение флагов состояния линию запроса не снимает. У ADC —
CR1.CLRINTRPT. У CAN FD — запись единицы в ICR. У TIM — TIM_ClearIT().
У UART подтверждения нет вовсе. Без него при запуске по уровню плата
«зависает» (обработчик вызывается бесконечно, очередь UART перестаёт
уходить), при запуске по фронту срабатывает ровно один раз, а регистр
состояния внутри обработчика читается нулём. Ни один пример SDK 2.1
PWMA_ClearIT() не вызывает — искать нечего, надо знать.
- Сбор с АЦП по прерыванию невозможен — только DMA. Запрос снимается
единственно записью в
CR1, а любая запись в CR1 обрывает идущую
последовательность (там же ADON и SWSTART). Подтверждать на каждом
ранге — терять преобразование (измерено: 3 ранга из 6), не подтверждать —
вешать плату. С DMA всё сходится: 0 потерь из 500, 18 мкс на канал.
Прерывание от самого АЦП не разрешается вовсе.
ADC_StartCalibration() — не калибровка. Она выставляет ASER.CAL_SEL,
а ADC_IsCalibrationOnGoing() читает тот же разряд обратно; железо его не
снимает, поэтому ожидание вечно по построению и плата встаёт молча. По
RM единица подменяет вход внутренним нулём, и «ноль» этот равен 1400…1700
отсчётов — середина шкалы. Вычитать его нельзя: данные превращаются в
правдоподобные нули («датчики не подключены»).
CANFD_RequestMode() из SDK молча не срабатывает. Она снимает SRR.CEN
и тут же проверяет режим; не успев — пропускает настройку, а CEN уже снят.
Узел нем: не передаёт, не принимает, ошибок не считает, и выглядит это как
«кадры уходят, никто не отвечает». Повторять запрос, пока GetMode() не
подтвердит.
- Тактовая CAN — 12,5 МГц, а не 25. На 500 кбит/с приходится ровно 25
квантов на бит; привычные фиксированные 16 не делят это нацело, и шина не
открывается вовсе. Число квантов считать, точка выборки ~75 %.
- SPI0 и SPI2 — ведомые. В именах регистров, в структурах и в HAL
разницы нет:
SPI_Init() проходит без ошибок, а обмен даёт таймаут. Улика —
BAUDR и SER читаются нулями при записанных значениях. Различить можно
только по таблице выводов RM: MSPI1, MSPI3 против SSPI0, SSPI2.
Отсюда правило шире одного блока: прочитать обратно то, что записал.
- С обычного ОЗУ (
0x70000000) выборка команд не идёт. Исполняемое ОЗУ
здесь одно — TCMA 0x40010000. Функция, положенная в .data, просто не
возвращается; ядро уходит в трап с запрещёнными прерываниями, и наружу это
выглядит как обрыв вывода на середине строки.
- Код, который пишет eFlash, обязан исполняться из TCM. Массив недоступен,
пока идёт его собственная операция, — и для данных, и для команд: холодный
проход по 4 КиБ кода во время стирания сектора занял 3408 мкс против 85
вхолостую. Отказ не падение, а простой ядра на 3,3–4,1 мс и потерянные байты
UART. Удобный перенос — секция с именем без точек (
beu_nvmfunc): для
таких компоновщик сам заводит __start_…/__stop_….
common.mk не следит за заголовками. DEPS собирается как main.d, а
компилятор пишет main.o.d, и -include молча берёт пустоту. Правка общего
заголовка не вызывает пересборку, а на плате потом отказывает не то, что
менялось (у нас так объект устарел на 12 байт структуры, и плата умирала на
первом куске прошивки). Лечение — -include $(wildcard $(OBJ_DIR)/*.o.d) в
своём Makefile либо make clean после каждой правки заголовка.
- 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).
- Открытие
/dev/ttyUSB0 сбрасывает плату, и софтом это не обходится.
Вывод ADBUS4 микросхемы FT2232HL заведён на цепь сброса; проверены обе
полярности DTR и обе RTS — сбрасывают все четыре, даже когда порт уже держит
открытым другой процесс. Следствие полезное: сброс перед прошивкой можно
отдавать линией DTR, кнопка SB2 не нужна. Следствие вредное: всякая правка,
живущая в ОЗУ, теряется при каждом подключении.
- В режиме
EFLASH BootROM не поднимает UART0 и не печатает. Его
printf по 0x4000b082 работает только там, где он сам ведёт консоль, то
есть в режимах UART и USB CDC. Программа, полагающаяся на него, в боевом
режиме молчит при полной исправности — а отлаживать это в режиме UART
бесполезно, там отказ не воспроизводится. Порт поднимать самому.
- Прерывания не вкладываются. Обработчик GCC (
__attribute__((interrupt)))
входит с закрытым mstatus.MIE и наружу его не открывает; уровни CLIC задают
лишь порядок выбора среди ожидающих. Значит бюджет считается по самому
длинному обработчику в системе, а не по своему: измеренный провал главного
цикла — 850…1300 мкс. И float в обработчике опасен: GCC сохраняет только
те регистры, которые использует сам.
CLIC_Config(1, 1) даёт по одному разряду на уровень и приоритет.
Значение 2 в CLIC_ConfigIRQ() при этом не «понизится» — прерывание не
сработает вовсе. И __enable_irq() забыть так же легко: обе ошибки дают
молчащую плату, неотличимую от зависшей.
- Потолок сторожевого таймера — 335 мс (2^23−1 такта на 25 МГц), то есть
670 мс до перезагрузки в режиме с прерыванием. ⚠ В заголовке SDK список
периодов набран с опечаткой начиная с четвёртого значения (
0xFFFF вместо
0xFFF) и завышает потолок в 16 раз. Регистра причины сброса у кристалла
нет — отметку держать в ОЗУ (например по _heap_start).
- Страповые выводы
PA10, PB10, PC4, PC6, PC8 нельзя подтягивать
вверх — ни драйвером, ни преобразователем уровня. Внутри они подтянуты к
земле через 42 кОм, и это и есть режим EFLASH; внешняя единица уводит
собранную плату в чужой режим загрузки молча. MODE[0] при этом сидит на
SPI1_MOSI, а MODE[1] — на SPI3_MOSI: у обоих ведущих SPI линия MOSI
страповая, обойти нечем.
- Восемь аналоговых входов — предел кристалла, а не платы.
VIN0…VIN7
выделенные, у всех трёх АЦП общие, альтернативной функцией портов АЦП не
бывает ни на одном выводе. Нужно больше — только внешний мультиплексор
(CD74HC4051 + буферный ОУ: Ron 100…200 Ω требует низкого импеданса
источника).
- Тактирование порта 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.
- 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.
- Ядро 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/ — проверенный работающий проект:
~/.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 не
выяснено.
1---2name: beu1000-evuba3description: BE-U1000 на плате EVU-BA-2.14---56# BE-U1000 на плате EVU-BA-2.178Знание об этой плате и этом кристалле, которое иначе собирается по RM,9техспецификации, ТО платы и неделе отладки. Почти все отказы здесь **тихие**:10плата работает, просто не так — или молчит, будучи исправной. Поэтому детали11лежат в справочных файлах, и их следует читать, а не додумывать.1213- `reference/board-hardware.md` — полный справочник по железу: карта выводов и14 разъёмов, питание, сброс, режимы загрузки, карта памяти, тактирование, обзор15 SDK, **плюс** часть II: система сборки, прерывания, поваренная книга по16 блокам, прошивка и таблица «признак → причина → лечение».17- `reference/recipes.md` — готовый код, извлечённый из `template/`: Makefile,18 консоль, временная база, захват, импульс, АЦП+DMA, CAN FD, SPI, eFlash, WDT.19- `template/` — **работающий проект**: собран в четырёх сочетаниях20 (`--full`/`--minimal` × eFlash/TCM) и **прогнан на плате** 2026-08-31 —21 светодиод, консоль, меню, захват, импульс, АЦП. Плюс сценарий развёртывания.22 Смотри `template/README.md`.2324## Ориентация2526| | |27|---|---|28| Кристалл | BE-U1000, QFN-88. **Три ядра RISC-V**: 0 и 1 — CloudBEAR BR-350 200 МГц (`ilp32f`), 2 — BM-310 100 МГц, **без FPU** (`ilp32`) |29| Память | eFlash 256 КБ `0xA0000000`, TCMA 64 КБ `0x40010000`, TCMB 96 КБ `0x40020000`, SRAM 32 КБ `0x70000000`, QSPI1 (плата, 16 МБ) `0x90000000` |30| Тактирование | генератор G1 **25 МГц** на `CLKI` (кварца у кристалла нет). Перемычка `CLK` на XP1 переключает на внутренний RC-генератор. PLL по умолчанию не включена |31| Светодиод / кнопка | LD1 `PC0` **активен высоким** / SB1 `PC13` **нажата = ноль**. SB2 — аппаратный сброс |32| Консоль и прошивка | **UART0 `PA6`/`PA7`, АФ #1**, разъём XS2 «JTAG UART0» (FT2232HL). Это единственный UART, выведенный на USB |33| Аналоговые входы | **8 штук, `VIN0…VIN7`** — выделенные выводы, XP8 конт. 28, 30, 32, 34, 36, 38, 35, 37; «AREF» — 7. Три АЦП 12 бит 1 MSps делят их |34| Импульсные блоки | 4 × PWMA (16 бит, захват, одиночный импульс, dead-time), 2 × PWMG, 2 × TIM (32 бита, 4 канала) |35| Шины | 2 × CAN FD (**трансивера на плате нет**), 4 × SPI (**ведущими умеют только SPI1 и SPI3**), 4 × I2C, до 7 UART, USB OTG (XS1) |36| Отладка | JTAG отладчиком на плате (тот же FT2232HL) либо внешним через XP6; **JTAG разведён на `PA0…PA4`** |37| Порты на гребёнках | XP9 — весь порт A, XP10 — весь порт B, XP8 — весь порт C плюс питание и входы АЦП |3839## Сначала — режим загрузки, потом всё остальное4041Режим защёлкивается перемычками XP1 **до сброса**; ни одна программная42настройка его не меняет. Половина «плата не работает» — это не тот режим.4344| XP1 `DBG-M2-M1-M0` | Режим | Когда нужен |45|---|---|---|46| `0000` (все сняты) | **EFLASH** | боевой: запуск программы из eFlash |47| `0001` (только `M0`) | **UART** | прошивка и BootROM CLI по UART0 |48| `0011` (`M0`+`M1`) | **USB** | прошивка по DFU либо CLI по USB CDC (кабель в XS1) |49| `0010` | QSPI | запуск из внешней QSPI flash |50| `0100` / `1100` | JTAG EXT / JTAG INT | отладчик на плате или внешний / встроенный |51| `0110` | MULTI | перебор EFLASH → QSPI → USB CDC → UART |5253Отдельная перемычка `CLK`: снята — генератор 25 МГц (норма), установлена —54внутренний RC-генератор. ⚠ Его диапазон в документах **разный**: техспецификация55кристалла v1.5.5 трижды называет **12…32 МГц**, ТО платы v2.1.2 — 20…40 МГц.56Кому верить, по бумагам не решить; на плате не мерено. Питание выбирается57перемычкой XP11: `USB` / `EXT5V` / `VIN` (10…48 В). Полные таблицы и рисунки —58`reference/board-hardware.md` §3.5960## Правила, которые экономят дни (rules & pitfalls)6162Каждое — из отказа, который **выглядел не тем, чем был**.63641. **Подтверждение приёма прерывания у каждого блока своё, и найти его надо65 до отладки, а не после.** У PWMA/PWMG это `PWMA_ClearIT()` — разряд 1566 регистра **разрешений** `DIER`, и он обязан быть **последней** строкой67 обработчика; гашение флагов состояния линию запроса не снимает. У ADC —68 `CR1.CLRINTRPT`. У CAN FD — запись единицы в `ICR`. У TIM — `TIM_ClearIT()`.69 У UART подтверждения нет вовсе. Без него при запуске по уровню плата70 «зависает» (обработчик вызывается бесконечно, очередь UART перестаёт71 уходить), при запуске по фронту срабатывает **ровно один раз**, а регистр72 состояния внутри обработчика читается нулём. Ни один пример SDK 2.173 `PWMA_ClearIT()` не вызывает — искать нечего, надо знать.742. **Сбор с АЦП по прерыванию невозможен — только DMA.** Запрос снимается75 единственно записью в `CR1`, а любая запись в `CR1` обрывает идущую76 последовательность (там же `ADON` и `SWSTART`). Подтверждать на каждом77 ранге — терять преобразование (измерено: 3 ранга из 6), не подтверждать —78 вешать плату. С DMA всё сходится: 0 потерь из 500, **18 мкс на канал**.79 Прерывание от самого АЦП не разрешается вовсе.803. **`ADC_StartCalibration()` — не калибровка.** Она выставляет `ASER.CAL_SEL`,81 а `ADC_IsCalibrationOnGoing()` читает тот же разряд обратно; железо его не82 снимает, поэтому ожидание вечно **по построению** и плата встаёт молча. По83 RM единица подменяет вход внутренним нулём, и «ноль» этот равен 1400…170084 отсчётов — середина шкалы. **Вычитать его нельзя:** данные превращаются в85 правдоподобные нули («датчики не подключены»).864. **`CANFD_RequestMode()` из SDK молча не срабатывает.** Она снимает `SRR.CEN`87 и тут же проверяет режим; не успев — пропускает настройку, а `CEN` уже снят.88 Узел нем: не передаёт, не принимает, ошибок не считает, и выглядит это как89 «кадры уходят, никто не отвечает». Повторять запрос, пока `GetMode()` не90 подтвердит.915. **Тактовая CAN — 12,5 МГц, а не 25.** На 500 кбит/с приходится ровно 2592 квантов на бит; привычные фиксированные 16 не делят это нацело, и шина не93 открывается вовсе. Число квантов **считать**, точка выборки ~75 %.946. **SPI0 и SPI2 — ведомые.** В именах регистров, в структурах и в HAL95 разницы нет: `SPI_Init()` проходит без ошибок, а обмен даёт таймаут. Улика —96 `BAUDR` и `SER` читаются нулями при записанных значениях. Различить можно97 только по таблице выводов RM: `MSPI1`, `MSPI3` против `SSPI0`, `SSPI2`.98 Отсюда правило шире одного блока: **прочитать обратно то, что записал**.997. **С обычного ОЗУ (`0x70000000`) выборка команд не идёт.** Исполняемое ОЗУ100 здесь одно — **TCMA `0x40010000`**. Функция, положенная в `.data`, просто не101 возвращается; ядро уходит в трап с запрещёнными прерываниями, и наружу это102 выглядит как обрыв вывода на середине строки.1038. **Код, который пишет eFlash, обязан исполняться из TCM.** Массив недоступен,104 пока идёт его собственная операция, — и для данных, и для команд: холодный105 проход по 4 КиБ кода во время стирания сектора занял **3408 мкс** против 85106 вхолостую. Отказ не падение, а простой ядра на 3,3–4,1 мс и потерянные байты107 UART. Удобный перенос — секция с именем **без точек** (`beu_nvmfunc`): для108 таких компоновщик сам заводит `__start_…`/`__stop_…`.1099. **`common.mk` не следит за заголовками.** `DEPS` собирается как `main.d`, а110 компилятор пишет `main.o.d`, и `-include` молча берёт пустоту. Правка общего111 заголовка не вызывает пересборку, а на плате потом **отказывает не то, что112 менялось** (у нас так объект устарел на 12 байт структуры, и плата умирала на113 первом куске прошивки). Лечение — `-include $(wildcard $(OBJ_DIR)/*.o.d)` в114 своём Makefile либо `make clean` после каждой правки заголовка.11510. **SDK переиспользует уже сгенерированный `output/debug/generated.ld`**116 (`LD_SCRIPT := firstword $(wildcard $(PRJ_DIR)/*.ld) $(wildcard $(OUT_DIR)/*.ld)`).117 Поэтому `MEM_REG_ROM=TCMA` после обычной сборки **молча не действует**:118 предупреждений нет, образ уезжает не в ту память, на плате это «прошилось,119 но не стартует». Вместе с `ORIGIN` не меняется и **кодовое слово для120 BootROM** — а оно у каждой области своё (см. «Прошивка»), поэтому плата121 молча уходит в режим USB. Перед сменой области — `make clean`; проверять122 `ORIGIN` в `generated.ld` (под TCM должно быть `0x40010000`).12311. **Открытие `/dev/ttyUSB0` сбрасывает плату, и софтом это не обходится.**124 Вывод ADBUS4 микросхемы FT2232HL заведён на цепь сброса; проверены обе125 полярности DTR и обе RTS — сбрасывают все четыре, даже когда порт уже держит126 открытым другой процесс. Следствие полезное: сброс перед прошивкой можно127 отдавать линией DTR, кнопка SB2 не нужна. Следствие вредное: всякая правка,128 живущая в ОЗУ, теряется при каждом подключении.12912. **В режиме `EFLASH` BootROM не поднимает UART0 и не печатает.** Его130 `printf` по `0x4000b082` работает только там, где он сам ведёт консоль, то131 есть в режимах `UART` и `USB CDC`. Программа, полагающаяся на него, в боевом132 режиме молчит при полной исправности — а отлаживать это в режиме `UART`133 бесполезно, там отказ не воспроизводится. Порт поднимать самому.13413. **Прерывания не вкладываются.** Обработчик GCC (`__attribute__((interrupt))`)135 входит с закрытым `mstatus.MIE` и наружу его не открывает; уровни CLIC задают136 лишь порядок выбора среди ожидающих. Значит **бюджет считается по самому137 длинному обработчику в системе**, а не по своему: измеренный провал главного138 цикла — 850…1300 мкс. И `float` в обработчике опасен: GCC сохраняет только139 те регистры, которые использует сам.14014. **`CLIC_Config(1, 1)` даёт по одному разряду на уровень и приоритет.**141 Значение 2 в `CLIC_ConfigIRQ()` при этом не «понизится» — прерывание не142 сработает вовсе. И `__enable_irq()` забыть так же легко: обе ошибки дают143 молчащую плату, неотличимую от зависшей.14415. **Потолок сторожевого таймера — 335 мс** (2^23−1 такта на 25 МГц), то есть145 670 мс до перезагрузки в режиме с прерыванием. ⚠ В заголовке SDK список146 периодов набран с опечаткой начиная с четвёртого значения (`0xFFFF` вместо147 `0xFFF`) и завышает потолок **в 16 раз**. Регистра причины сброса у кристалла148 нет — отметку держать в ОЗУ (например по `_heap_start`).14916. **Страповые выводы `PA10`, `PB10`, `PC4`, `PC6`, `PC8` нельзя подтягивать150 вверх** — ни драйвером, ни преобразователем уровня. Внутри они подтянуты к151 земле через 42 кОм, и это и есть режим `EFLASH`; внешняя единица уводит152 собранную плату в чужой режим загрузки молча. `MODE[0]` при этом сидит на153 `SPI1_MOSI`, а `MODE[1]` — на `SPI3_MOSI`: у обоих ведущих SPI линия MOSI154 страповая, обойти нечем.15517. **Восемь аналоговых входов — предел кристалла, а не платы.** `VIN0…VIN7`156 выделенные, у всех трёх АЦП общие, альтернативной функцией портов АЦП не157 бывает ни на одном выводе. Нужно больше — только внешний мультиплексор158 (`CD74HC4051` + буферный ОУ: `Ron` 100…200 Ω требует низкого импеданса159 источника).16018. **Тактирование порта GPIO включается отдельно от тактирования периферии,161 и без него вывод к ней не подключается.** `CRU_PIN_Init()` при этом162 отрабатывает молча, регистры таймера верны, провод исправен — а захват не163 видит ни одного фронта. Измерено на плате 2026-08-31: захват на `PB8` не164 считал ничего, пока не добавили `CRU_APB1_EnableClock(CRU_APB1_PERIPH_GPIO1)`;165 выход на `PC5` при этом работал — но лишь потому, что порт C уже166 тактировался ради светодиода. Порты сидят на разных шинах:167 A — `CRU_APB0_PERIPH_GPIO0`, B — `CRU_APB1_PERIPH_GPIO1`,168 C — `CRU_APB2_PERIPH_GPIO2`.16919. **DMA не имеет доступа к TCM — ни к TCMA, ни к TCMB.** Буфер DMA обязан170 лежать в SRAM (`0x70000000`). Измерено 2026-08-31 на трёх адресах: с171 приёмником в TCMB (`0x40020400`) и в TCMA (`0x4001F000`) канал включается,172 делает одну исходную транзакцию, ловит ошибку шины (`RAW_ERR = 1`) и173 **сам выключается**, не подняв флага завершения; с приёмником в SRAM те же174 четыре канала АЦП собираются за 66 мкс. Ловушка тихая вдвойне: наружу это175 «АЦП не отдаёт результат», а конфигурация DMA и АЦП при этом верна. И она176 подстерегает ровно там, где кажется безопасно: при сборке под TCM177 (`MEM_REG_ROM=TCMA`) переменная `MEM_REG_RAM` по умолчанию тоже уводит178 данные в TCM — для DMA нужно **`MEM_REG_RAM=SRAM`**.17920. **Ядро 2 (BM-310) — без FPU и практически без libc.** Тулчейн SDK собран без180 мультибиблиотеки `rv32imc/ilp32`, и `<string.h>` там просто нет: сборка181 падает с `fatal error: string.h: No such file or directory`, тогда как182 хостовая и оба других ядра проходят. TCM у него свой и крошечный: 2 КБ + 8 КБ.183184## Когда задача — импульсы и захват (угловой домен, ШИМ, энкодер)185186Это то, ради чего берут PWMA, и наивный подход здесь неверен.187188**Импульс складывается из двух совпадений.** У PWMA на канал один регистр189сравнения: `CCR = t_on` в режиме `ACTIVE` даёт фронт включения, затем в190обработчике `CCR = t_off` в режиме `INACTIVE` — фронт выключения. **Оба фронта191делает компаратор**, задержка обработчика в джиттер не превращается — она лишь192ограничивает снизу длительность импульса. Измерено: ошибка ширины **0 нс**,193джиттер 120…320 нс, постоянный сдвиг −1,8 мкс (одинаков для обоих фронтов,194ширины не касается).195196**Счётчик 16-разрядный: на 25 МГц окно всего 2,62 мс.** Цель дальше окна197требует **прицельного пробуждения** — совпадения на заведомо достижимой точке, по198которому остаток пересчитывается. Ожидание переполнения вместо этого даёт199слепую полосу: цель чуть дальше окна перепрыгивает через ноль и оказывается «в200прошлом». И при перезарядке обязательно переставлять значение сравнения: старое,201оставшееся от предыдущего фронта, срабатывает как фронт выключения — импульс 5 мс202выходил короче ровно на период счётчика.203204**Предзагрузка регистра сравнения (`preload`) — противоположные решения для205разных задач.** Для события (впрыск, искра) её надо **выключить**: цель,206записанная за 100 мкс до срабатывания, вступила бы в силу только через 2,6 мс.207Для ШИМ — **включить**: иначе новая скважность действует посреди импульса, и это208скачок тока в нагрузке на каждое обновление регулятора.209210**Захват и вывод — на разных блоках PWMA.** У PWMA **один вектор прерывания на211все каналы блока**: на одном блоке перезарядка компаратора вывода исполнялась бы212внутри обработчика захвата. Выбор к тому же задан выводами: на порту B (XP10)213из блоков PWMA есть только PWMA1, PWMA2/PWMA3 сидят на порту C (XP8).214215Числа, по которым считается запас: захват даёт джиттер **80 нс** через216прерывание; **оба фронта на одном канале — не чаще одного фронта в 100 мкс**217(полярность переключается в обработчике), причём пропущенный фронт даёт не отказ,218а **правдоподобный мусор** — период кратный, скважность произвольная; зарядка219канала вывода стоит **38 мкс**. Проверять это можно без генератора: перемычка с220выхода PWMA2 на вход PWMA1 (`PC5` XP8.6 → `PB8` XP10.13) — плата меряет себя221сама, и честно, потому что блоки разные.222223## Когда задача — аналоговые входы224225Кроме правил 2, 3, 17 и **19 (буфер DMA только в SRAM)**: у этого АЦП **нет226одновременной выборки**. Секвенсор227идёт по рангам, и последний канал группы отстаёт от первого на **90 мкс** при228шести каналах. Взаимозависимые величины (например положение и ток) ставить229соседними рангами. Две группы можно вести параллельно — контроллеры ADC0 и ADC1230независимы, входы у них общие. Линии DMA выделенные: у DMA_0 линия 13 —231`ADC_0 RX`, линия 15 — `ADC_1 RX` (RM табл. 8-1).232233## Новый проект234235Руками не собирать. `template/` — проверенный работающий проект:236237```sh238~/.claude/skills/beu1000-evuba/template/variants/new-project.sh <каталог> [--full|--minimal]239cd <каталог> && make BAIKAL_SDK=$HOME/путь/к/SDK_2_1240```241242- `--full` (по умолчанию) — консоль, временная база, захват, импульс, АЦП+DMA,243 меню по UART0. **25 536 Б** из 256 КБ eFlash, ОЗУ 536 Б.244- `--minimal` — только мигание LD1. **8 484 Б**, ОЗУ 424 Б. С него начинать на245 незнакомой плате: он отделяет отказ тракта прошивки от отказа своего кода.246247Обе сборки проверены, как и вариант под TCM. ⚠ Под TCM собирать так:248`MEM_REG_ROM=TCMA MEM_REG_RAM=SRAM make` — **данные именно в SRAM**, иначе249буфер DMA окажется в TCM и АЦП замолчит (правило 19). И не забыть `make clean`250при смене области — правило 10.251`template/README.md` расписывает файлы по подсистемам. Если проект у252пользователя уже есть — приводить в порядок его Makefile по образцу шаблона,253а не переписывать код.254255## Прошивка (flashing)256257**Путь по умолчанию — BootROM CLI по UART0**, тем же кабелем, что и консоль.258Перемычки XP1: установлена только `M0` (режим `UART`), кабель USB Type-C в XS2,259питание выбрано на XP11.260261```262d вход в CLI (ROM отвечает баннером и приглашением @)26314000018:1w снять защиту eFlash от стирания и записи264a0000000:40000:0:100m адрес, максимальный размер, настройки тактирования265:::101m стереть eFlash266:::3L ждать образ; ROM отвечает 'C' — запрос XMODEM/CRC267<XMODEM 128-байтными блоками, CRC-16>268a0000000g запуск с адреса eFlash269```270271В TCM (`0x40010000`) — та же последовательность **без** снятия защиты и272стирания, команда загрузки `l`. Запись в TCM не трогает энергонезависимую273память и пропадает при снятии питания, поэтому первую проверку платы разумно274начинать с неё.275276**Почему залитый образ обязан быть собран под ту же область.** BootROM в режиме277`EFLASH` ищет по адресу `0xA0000020` кодовое слово `0x0BEF5262` и, найдя,278передаёт управление на `0xA0000030`; **не найдя — уходит в режим `USB`**279(руководство пользователя §3.2.1). Кодовое слово кладёт система сборки SDK, и280оно **разное для каждой области**: `EFLASH 0x0BEF5262`, `TCMA 0x0B10D522`,281`TCMB 0x0B10DDA2`, `QSPI1 0x00000000` (`Tools/build/service.mk`). Заголовок282образа — 48 байт: по смещению 0 стоит `jal x0, 48` (перепрыгнуть заголовок),283`0x20` — кодовое слово, `0x28`/`0x2C` — адрес и размер копирования.284285Отсюда два следствия. Собранный чужой системой сборки «голый» бинарник плата286сама не запустит — стартовать он будет только командой `g` из CLI. И образ,287собранный под другую область памяти, тоже не запустится, даже если залит по288верному адресу: кодовое слово не то. Поведение при этом обманчиво мягкое —289плата не виснет, а молча уходит в режим USB.290291Кнопку RESET нажимать не нужно: `DTR#` FT2232HL заведён на цепь `DTR_RST` и292формирует сброс наравне с кнопкой (правило 11). Скрипт нашего проекта —293`tools/flasher/baikal_uart_flash.py`.294295**Второй путь — USB DFU:** XP1 в `0011`, кабель в XS1 (USB OTG). Кристалл296поднимает DFU-устройство на стеке TinyUSB с идентификаторами **`cafe:2303`**297(проверять `lsusb`), заливка — `dfu-util --alt 2 --download образ.bin`. По298умолчанию устройство принадлежит root, и `dfu-util` падает с299`LIBUSB_ERROR_ACCESS`; лечится правилом udev300(`SUBSYSTEM=="usb", ATTR{idVendor}=="cafe", ATTR{idProduct}=="2303", TAG+="uaccess"`).301Путь нужен для больших образов (256 КБ по UART идут около 25 секунд), но требует302**второго кабеля** в XS2 для сброса: питание FT2232HL идёт оттуда, и без него303линия DTR мертва.304305**Третий путь — JTAG:** XP1 в `0100` (отладчик на плате, XS2) либо внешний306отладчик в XP6 при любом питании, кроме XS2. Нужен для точек останова и для307подъёма платы, у которой испорчено всё остальное.308309**Оживление.** Испортить плату прошивкой нельзя: режим загрузки задаётся310перемычками до сброса и от содержимого eFlash не зависит. Оборванная заливка311означает лишь негодный образ — вернуть XP1 в `UART` и залить заново.312313## Отчётность314315Говорить прямо, что проверено на железе, а что взято из документации. Здесь это316разделено так:317318| Происхождение | Что именно |319|---|---|320| **Измерено на плате шаблоном этого скила** (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) |321| **Измерено на плате** (порт EHAL проекта ЭБУ, EVU-BA-2.1, ядро 0, 25 МГц без PLL) | джиттер захвата 80 нс; предел обоих фронтов 100 мкс; ширина импульса и джиттер вывода; зарядка канала 38 мкс; провал главного цикла 850…1300 мкс; АЦП 18 мкс на канал и 0 потерь из 500; смещение нуля АЦП; времена и зерно eFlash; тактовая CAN 12,5 МГц; сброс платы при открытии порта; потолок и поведение WDT |322| **Проверено сборкой** | размеры и области шаблона (25 536 Б и 8 484 Б, `ORIGIN` под TCM), поведение `generated.ld` и `*.o.d`, кодовое слово в готовом образе (`xxd -l 0x30`: `0x0BEF5262` под eFlash, `0x0B10D522` под TCMA) |323| **Сверено с первоисточником** | карта «вывод → контакт» разъёмов 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) |324| **Не проверено ничем** | выводы CAN `PA14`/`PA15` и уровни на них — **трансивера на плате нет**, проверялась только внутренняя петля; диапазон внутреннего RC-генератора (документы расходятся); заливка шаблона в eFlash (проверялась только TCM: в eFlash платы лежит чужая прошивка, и стирать её ради опыта незачем) |325326Отдельно держать в уме нерешённое: времена eFlash из приложения вдвое больше327измеренных из TCM (8253 мкс против 4061 на стирание блока), причина не328установлена, обе цифры приведены рядом намеренно; флаг повторного захвата329`CC0OF` взводится на каждом захвате при нулевых потерях, расхождение с RM не330выяснено.