Автономный программатор SWD
При разработке и последующем производстве электроники неизбежно встаёт вопрос проверки собранных изделий. Если компания небольшая, а производство мелкосерийное, то в жертву рутине можно принести какого-нибудь начинающего специалиста, либо самого разработчика. Как правило, требуется проверить несколько напряжении, пару цифровых интерфейсов, световую, звуковую индикацию и т. п. Но предварительно в контроллер необходимо записать программу. В мелкой серии длительность тестирования особой роли не играет. Трудности начинаются, когда каждый месяц производится десяток тысяч изделий.
Вопрос в том, что прошивка устройства внешним программатором — это отдельная технологическая операция, требующая времени и оплаты. Столкнувшись с такой постановкой задачи, пришлось некоторое время повозиться, чтобы реализовать автономный SWD программатор в составе технологической оснастки. Все операции по проверке собранных печатных плат, включая программирование, объединяются в тестовую последовательность, а полное время проверок сводится к возможному минимуму. Надеюсь, что представленная информация облегчит дело тем самураям, которые решат пройти по схожему пути.
Микроконтроллеры с ядром Cortex-M поддерживают множество вариантов записи программы во Flash память. В зависимости от чипа программу можно зашить через интерфейсы USART, CAN, USB, I2C, SPI, используя встроенный bootloader. Однако этой возможностью удаётся воспользоваться не всегда. Чаще всего нужные лапки микросхемы уже заняты, иногда топология платы очень плотная и выводить дополнительные сигналы проблематично, либо сталкиваемся с пресловутым человеческим фактором: изделие разработали, а интерфейс для загрузчика вывести забыли. Зато разъем программирования SWD присутствует наверняка, и использовать его для прошивки как минимум логично.
Задачу удобно разбить на три составляющие: линия связи и протокол, логическая модель отладчика и практика. Сперва рассмотрим, как байты передаются по проводам и за что отвечают, затем затронем вопрос внутренней структуры отладчика и общения с ним. Завершит публикацию пример реализации автономного SWD программатора для контроллеров с ядром Cortex M.
Линия связи и протокол SWD
С одной стороны, чем меньше проводов в интерфейсе для отладки, тем лучше: не занимаем ножек микросхемы и экономим место на печатной плате. С другой — необходимо обеспечить высокую скорость передачи данных, а чем меньше сигналов в распоряжении, тем сложнее этого добиться. Протокол также накладывает свой отпечаток на физику передачи, т.к. он должен «помогать» внутреннему логическому автомату (ARM CoreSight) выполнять свою работу. Интерфейс отладки SWD компании ARM удачно разрешает обозначенные противоречия.
Лучше всего начать с примера и разобраться с особенностями SWD по ходу обсуждения. На рисунке 1 показана диаграмма успешной операции записи. Один провод SWCLK отвечает за тактовый сигнал, второй SWDIO — за передачу информации. Со стороны мастера процесс проходит в три этапа: передача байта управления → переключение линии SWDIO на прием и чтение подтверждения → переключение лини SWDIO на передачу и запись данных. Обратите внимание, что подтверждение ACK и WDATA передаются, начиная с младшего бита.
Рисунок 1 — Диаграмма успешной операции записи SWD. Источник изображения [2]
Естественно возникает вопрос, что зашифровано в первом байте, да и вообще во всей посылке? Вся информация сведена в таблице 1.
Таблица 1 — Расшифровка посылки SWD интерфейса
Start | Стартовый бит, всегда со значением 1 |
APnDP | Выбор группы регистров: 0 — регистры DP, 1 — регистры AP Логически регистры отладчика разделены на две группы, о чем будет сказано далее. |
RnW | Выбор операции: 0 — чтение, 1 — запись |
A[2:3] | Адрес регистра DP или AP.Т. к. у нас в распоряжении 2 бита, то может сложиться впечатление, что регистров каждой группы только 4. Это не так. В зависимости от операции чтения или записи, а также значения бита CTRLSEL регистра SELECT выбираются разные регистры по одним и тем же адресам. Карта регистров представлена далее. |
Parity | Биты проверки на четность над полями байта управления и полем данных. Биты ACK[0:2] в проверку четности не входят. |
Stop | Стоповый бит, всегда со значением 0. |
Park | Единичный бит, во время которого мастер не управляет линией, а её состояние определяется за счет аппаратной подтяжки. |
Trn | Смена направления ножки SWDIO — время, в течении которого состояние линии неопределённо. Длительность Park по умолчанию составляет один тактовый импульс, но может быть настроено в Wire Control Register. |
ACK[0:2] | Биты подтверждения ведомого устройства: 001 — OK, успешная операция; 010 — WAIT, мастер должен повторить операцию позже; 100 — FAULT, мастер должен проверить биты Control/Status Register, чтобы определить, какая именно ошибка произошла. Ведомое устройство не будет отвечать на команды, пока ошибка не будет очищена в регистре ABORT. |
WDATA[0:31] | 32 бита данных от мастера, передаются, начиная с младшего бита (LSB-first). |
RDATA[0:31] | 32 бита данных от ведомого, передаются, начиная с младшего бита (LSB-first). |
Аналогичным образом передается пакет на чтение. В этом случае после получения подтверждения не требуется менять направление линии, данные от ведомого устройства продолжают поступать по мере тактирования линии.
Рисунок 2 — Диаграмма успешной операции чтения SWD. Источник изображения [2].
На осциллограмме следующего рисунка показано, как на практике выглядит операция чтения IDCODE микроконтроллера APM32F030. Проверка четности над полями APnDP, RnW и A[2:3] дает значение 1, код подтверждения b001, данные b0000×1011110000010001|01000111011×1 (0xBC11477). Вертикальными прямым разделены поля посылки, в которых закодирован код JEDEC [1:11] и серийный номер [12:27].
Рисунок 3 — Осциллограмма чтения регистра IDCODE
Если ведомый получает от мастера новый запрос во время выполнения команды, то вместо подтверждения возвращается состояние WAIT. Столкнувшись с таким поведением, мастер может либо повторять запросы некоторое количество раз, либо прервать выполнение операции при помощи регистра ABORT.
Рисунок 3 — Диаграмма состояния WAIT на линии SWD. Источник изображения [2].
Нештатные ситуации также должны решаться на уровне протокола. Может случиться ошибка записи/чтения, переполнения, транзакции, сравнения и т.п. Для этого существует ответ FAULT. С причиной ошибки можно ознакомиться при помощи регистра управления и статуса (Control/Status Register).
Рисунок 4 — Диаграмма состояния FAULT на линии SWD. Источник изображения [2].
Для любой линии связи необходимо учитывать физику распространения сигнала по проводникам. SWD интерфейс работает с частотой мастера, которую можно задать в широких пределах. Существует некоторый рабочий диапазон от 10 кГц до 10 МГц, внутри которого обмен будет стабильным. Все зависит от линии связи и целевого контроллера: не все микросхемы захотят работать на низких частотах, не все выдержат высокие. Требования к таймингам SWD приведены в документе [3]. Мастер записывает данные по падающему фронту тактового сигнала, а считывает по восходящему, ведомый считывает и записывает данные по восходящему фронту. Т.к. у ведомого нет собственного тактирования, то:
во время обмена ведомый считывает данные по восходящему фронту и обрабатывает их по падающему;
мастер подготавливает данные по падающему фронту и выдает их на линию по восходящему.
Согласно документации линию SWCLK подтягивают к питанию, а поскольку линия SWDIO меняет направление, то на нее рекомендуется устанавливать защитный резистор.
Рисунок 5 — Подключение микроконтроллера по линии SWD. Источник изображения [3]
Правда есть оговорка, все будет работать и без подтяжек, т.к. программатор сам устанавливает на SWCLK, SWDIO и nRST нужные уровни. Кроме того, программатор может выполнить сброс ведомого программно, а подтяжка nRST к питанию зачастую есть внутри микроконтроллера. И всё таки для целей проверки сигнал nRST лучше выводить, иногда во время тестирования удобно держать микроконтроллер в состоянии сброса.
Логическая модель отладчика
Считаю правильным при описании отладчика не переводить все английские названия на русский. Это упростит понимание и последующее использование документации. Кроме того, затруднительно найти устоявшиеся названия для специфических частей микросхемы и её регистров. Под логической моделью подразумевается некоторый программно-аппаратный автомат, который реализован в чипе и позволяет достучаться до её внутренностей. Он имеет название DAP (Debug Access Port) и представляет собой практическую реализацию ARM Debug Interface 5.1 (ADIv5.1). Понятно, что есть регистры и какие-то связи между ними. Но за что они отвечают и как конфигурируются? В данном разделе предпринята попытка разобраться в схеме внутреннего взаимодействия DAP.
DAP разделен на две части: Debug Port (DP) и Access Port (AP). Физически отладчик подключается к DP, который может подключаться к одному или нескольким AP. На рисунке представлен самый простой случай, когда AP один и представляет собой MEM-AP (Memory Access Port), т.е. блок, позволяющий получить доступ ко всей памяти микросхемы и регистрам, находящимся на шинах AHB/APB. Порт доступа, вполне может быть выполнен как JTAG-AP, и тогда он будет управлять контроллером с учётом требований стандарта IEEE 1149.1. Изображенные на рисунке блоки находятся внутри микросхемы, наравне с таймерами или цифровыми интерфейсами они изготовлены из полупроводника.
Рисунок 6 — Интерес отладки, реализованный как MEM-AP. Источник изображения [2]
На DP возложены задачи инициализации, сброса и проверки статусов, AP обеспечивает доступ к различным ресурсам микроконтроллера. За выбор DP или AP отвечает бит APnDP (см. протокол SWD), а за адресацию регистров биты A[2:3]. Проблема заключается в том, что двух бит не достаточно, чтобы охватить весь диапазон адресов. Поэтому дополнительно используется информация, уже хранящаяся в регистрах, а их выбор осуществляется в зависимости от операции чтения или записи. Далее показан байт управления протокола SWD и карта регистров DP, жирным курсивом выделены биты, участвующие в выборе адреса.
Start | APnDP | RnW | A[2:3] | Parity | Stop | Park |
Таблица 2 — Карта регистров DP
Имя | APnDP | W/R | A[3:2] | CTRLSEL (SELECT REG) | Описание |
ABORT Abort Register | 0 — регистры DP | W | 0×00 | X | [4] ORUNERRCLR — сброс ошибки переполнения [3] WDATAERR — сброс ошибки записи [2] STICKYERR — сброс ошибки транзакции [1] STICKYCMP — сброс ошибки сравнения [0] DAPABORT — прервать выполнение операции |
IDCODE Identification Code Register | R | X | [27:28] PARNO — JTAG-DP или SW-DP [11:1] DESIGNER — JEDEC идентификатор | ||
CTRL/STAT Control/Status Register | R/W | 0×04 | 0 | [31] CSYSPWRUPACK — статус питания [30] CSYSPWRUPREQ — запрос статуса питания [29] CDBGPWRUPACK — статус питания отладчика [28] CDBGPWRUPREQ — запрос статуса питания отладчика [27] CDBGRSTACK — статус сброса [28] CDBGRSTREQ — запрос статуса сброса [21:12] TRNCNT — счетчик транзакций [11:8] MASKLANE — маска байт при операции сравнения [7] WDATAERR — ошибка записи [6] READOK — успех предыдущей операции чтения [5] STICKYERR — ошибка транзакции [4] STICKYCMP — ошибка сравнения [3:2] TRNMODE — режим передачи [1] STICKYORUN — ошибка переполнения [0] ORUNDETECT — разрешить обнаружение ошибок переполнения | |
WCR Wire Control Register | R/W | 1 | [9:8] TURNROUND — число тактов смены направления линии [7:6] WIREMODE — режим работы SWD [2:0] PRESCALER — параметр семплирования линии в асинхронном режиме | ||
SELECT Select register | W | 0×08 | X | [31:24] APSEL — выбор AP (если больше чем один) [7:4] APBANKSEL — выбор банка AP [0] CTRLSEL — выбор банка DP | |
RESEND Read Resend Register | R | X | Восстановление данных на случай ошибки транзакции | ||
RDBUFF Read Buffer | R | 0×0C | X | Хранение данных последней операции чтения |
R — Read, R/W — Read/Write, W — Write
Карта регистров AP несколько сложнее. Будет уместно привести более детальную структуру взаимодействия блоков, взятую из описания архитектуры. Регистров больше и в этот раз для адресации дополнительно используются значения битов APBANKSEL (см. выше Таблицу 2).
Рисунок 7 — Схема подключения блока MEM-AP. Источник изображения [2], [4]
Представленный рисунок — общая архитектура MEM-AP для подключения в режиме SWD или JTAG. Это нужно учитывать при его изучении, т.к. назначения регистров для SWD и JTAG отличаются. В верхней части рисунка показаны регистры DPACC и APACC, в нашем случае это выбор DP или AP. Часть описанных в таблице 2 регистров вместе со словом управления показана в верхнем блоке DPACC. Блоки разделяет ещё одно условно выделенное управляющее слово — в этот раз адреса состоят из шести бит A[7:4], A[3:2]. За счет расширения адреса происходит обращение в разные банки AP. Последнее, что стоит отметить относится к условно показанному третьему управляющему слову, состоящему из Addr[31:2] и данных Data[31:0], при помощи которого мы получаем доступ ко всему адресному пространству микроконтроллера. Это как-раз то, ради чего все задумано. Карта регистров AP представлена в таблице 3.
Таблица 3 — Карта регистров MEM-AP
Имя | APnDP | W/R | Адрес A[3:2]A[7:4] | Описание |
CSW Control/Status Word Register | 1 — регистры AP | R/W | 0×00 | [31:23] DbgSwEnable, Prot, SPIDEN — управление доступом, зависит от реализации чипа [11:8] Mode — режим, всегда должен быть 1 [7] TrInProg — транзакция выполняется [6] DeviceEn — включение устройства [5:4] AddrInc — автоинкремент адреса [2:0] Size — размер адреса доступа к памяти |
TAR Transfer Address Register | R/W | 0×04 | [31:0] Address — адрес памяти, к которому происходит обращение | |
DRW Data Read/Write Register | RAZ/ WI | 0×0C | [31:0] Data — данные для записи или прочитанные по выбранному адресу | |
BD0 Banked Data Register 0 | R/W | 0×10 | [31:0] Banked Data — данные текущей транзакции. Регистры банков используются, чтобы организовать взаимодействие с каким-либо функциональным блоком микроконтроллера. Например, нулевой банк может соотноситься с ядром, а первый с периферией и т.д. | |
BD1 Banked Data Register 1 | R/W | 0×14 | ||
BD2 Banked Data Register 2 | R/W | 0×18 | ||
BD3 Banked Data Register 3 | R/W | 0×1C | ||
CFG Configuration Register | RO | 0xF4 | [0] Big-endian — порядок следования бит при доступе к памяти. Поля регистра зависят от реализации чипа | |
BASE Base Address Register | RO | 0xF8 | [31:12] BASEADDR — адрес регистра отладки или таблицы ROM [1] Format — формат регистра базового адреса, всегда 1 [0] Entry present — признак возможности отладки Поля регистра зависят от реализации чипа | |
IDR Identification Register | RO | 0xFC | [31:28] — ревизия AP [27:24] — код JEP-106 continuation [23:17] — код JEP-106 identity [16] — Класс AP [7:0] — Идентификационный код AP |
RAZ — Read As Zero, RO — Read Only, R/W — Read/Write
Модель ADI (ARM Debug Interface) получилась мудреная. Наверное, ARM снова взяли какого-нибудь студента, и он быстрёхонько разработал нужную архитектуру. А теперь народ голову ломает, как всем этим управлять. Вряд ли мы узнаем истинные причины достаточно высокой сложности ADI, скорее всего они скрыты в универсальности интерфейса. Противоречивые требования всегда накладывают свой отпечаток: ADI декларирует синхронный и асинхронный режим, работу как SWD или JTAG, дополнительный функционал, такой как операцию сравнения или верификацию; при этом достигаются высокие скорости обмена! Вместе с тем интерфейс очень широко распространён, чего не скажешь, например, про 2-wire JTAG. Не считая редких случаев, когда устройство находится в условиях сильных электромагнитных помех, прошивка и отладка программы через SWD работают великолепно.
Пример реализации программатора
Давайте вспомним, с чего все началось. Есть большая серия собранных печатных плат, которые нужно проверять и прошивать. Было решено делать технологическую оснастку, в состав которой входит автономный программатор SWD. Изучив протокол и архитектуру отладчика, мы дошли до примера его практической реализации. Если речь не в спортивном интересе, то лучшее что можно сделать при создании собственного программатора — воспользоваться готовым решением. DAPProg от XIVN1987 содержит все необходимое. Собственно, это уже готовый автономный программатор SWD для микроконтроллеров с ядром Cortex-M, и дело осталось за малым, понять, как это работает.
«CMSIS (Common Microcontroller Software Interface Standard) — это набор инструментов, API, фреймворков и рабочих процессов, которые помогают упростить повторное использование программного обеспечения, сократить время обучения разработчиков микроконтроллеров, ускорить сборку и отладку проектов» [6]. В данный стандарт входит и программная реализация CMSIS-DAP. Если вы скачаете CMSIS Version 5 [7], то без труда найдите нужные файлы DAP.c, SW_DP.c, JTAG_DP.c (см. CMSIS_5–5.9.0\CMSIS\DAP\Firmware). Как раз на их основе создан программатор DAPProg.
Работа программатора представляет собой «ножкодрыг», выбранные пины SWCLK, SWDAT и RESET переключаются в соответствии с протоколом SWD. Соответствующие функции и конфигурация программатора находятся в файле config.h и зависят от выбранного в качестве мастера микроконтроллера. Например, функции работы с ножкой SWDIO можно реализовать так:
static __inline uint32_t PIN_SWDIO_IN(void)
{
return (SWDIO_PIN_STATE) ? 1 : 0;
}
static __inline void PIN_SWDIO_OUT(uint32_t bit)
{
if(bit & 1) SWDIO_PIN_SET;
else SWDIO_PIN_RESET;
}
static __inline void PIN_SWDIO_OUT_ENABLE(void)
{
SWDIO_PIN_RESET;
GPIOA->MODER &= ~GPIO_MODER_MODE9_1;
GPIOA->MODER |= GPIO_MODER_MODE9_0; // output
}
static __inline void PIN_SWDIO_OUT_DISABLE(void)
{
GPIOA->MODER &= ~(GPIO_MODER_MODE9_0 | GPIO_MODER_MODE9_1) ; // input
Функции объявлены как inline, т.е. выполнение кода должно быть максимально быстрым. Реализация обмена SWD находится в файле SW_DP.c, речь идет о выдаче тактового сигнала, переключении ножки данных на вход или выход, получении подтверждения ACK. С использованием этого кода организован необходимый интерфейс взаимодействия с регистрами DP и AP, он находится в файле SWD_host.c.
Имеющихся знаний уже достаточно, чтобы записать что-нибудь во Flash целевого устройства. Во-первых, у нас есть набор регистров DP и AP, которые позволяют получить доступ к ресурсам целевого контроллера, во-вторых — функция, управляющая ножками согласно протоколу. Необходимо перезагрузить и остановить ядро, разблокировать Flash и писать по нужным адресам. Такая процедура была с успехом проделана моим коллегой, показав свою жизнеспособность. Её недостаток заключается в том, что необходимо учитывать алгоритм работы с памятью целевого контроллера. Нужно знать, каким образом выполняется инициализации памяти, стирание страницы или сектора, её программирование, т.е. все это аппаратно-зависимые вещи, которые документируются производителем конкретной микросхемы.
Именно поэтому вместе с DFP (Device Family Pack) на выбранную микросхему скачивается файл с расширением .FLM (например, для APM32F030 требуемый файл находится в папке: \Arm\Packs\Geehy\APM32F0xx_DFP\1.0.7\Flash), который содержит так называемый Flash Programming Algorithm. При помощи Keil такой алгоритм можно создать и самостоятельно, воспользовавшись шаблоном (\Keil_v5\ARM\Flash\_Template), но лучше воспользоваться готовым.
Непосредственно применять файл .FLM в проекте не представляется возможным, сперва его нужно перекодировать. Для этого в папке FlashAlgo проекта DAPProg находится скрипт flash_algo.py. Именно с его помощью удается получить подключаемый к проекту c-файл. Для работы скрипта, вероятно, потребуется установить Python Package pip3 install pyelftools
(не elftools). Прошивку в свою очередь можно приготовить из .bin при помощи скрипта bin2array.py (находится в папке bin2array) или .hex, воспользовавшись шестнадцатеричным редактором [8]. В последнем случае нажатием правой кнопкой мыши выберите пункт «Экспортировать выбранные байты как фрагмент кода».
Подготовленные файлы алгоритма и прошивки подключается к проекту и используются функциями SWD_flash.с. Обратите внимание, как например происходит стирание сектора. Адрес памяти передается программе flash_algo, которая работает в RAM микроконтроллера.
error_t target_flash_erase_sector(uint32_t addr)
{
if (0 == swd_flash_syscall_exec(&flash_algo.sys_call_s, flash_algo.erase_sector, addr, 0, 0, 0)) {
return ERROR_ERASE_SECTOR;
}
return ERROR_SUCCESS;
}
Даже с использованием готового проекта DAPProg от XIVN1987, запуск и отладка собственного программатора займут некоторое время. Трудовые будни разработчика РЭА неизбежно сопряжены с чёрной магией. Мой программатор, например, отказывался прошивать целевой контроллер без подключенного к тактовой линии щупа осциллографа. Поступало даже предложение включить щуп в комплект поставки тестовой оснастки. Но путем витиеватых размышлений удалось установить истинную причину проблемы — ножка SWCLK была настроена на Very High Speed, а частота линии составляла несколько сотен килогерц. После обнуления регистра OSPEEDR все заработало как надо, видно причина крылась в неких переходных процессах.
Среди читателей наверняка найдутся те, кто вместе с представленной темой уже собаку съел и кто заметит неточности или даже ошибки. Замечания и уточнения приветствуются, т.к. представленный материал является результатом относительно поверхностного изучения архитектуры отладчика DAPProg и стандарта ARM Debug Interface v5. Тем не менее собранной информации должно быть достаточного для практических целей. Помним, что это только вершина айсберга. За дальнейшими деталями предлагаю обратиться к технической документации, к счастью её много и написана она весьма добротно.
Ресурсы:
1. AN2606 STM32 microcontroller system memory boot mode
2. ARM Debug Interface v5. Architecture Specification
3. ARM DSTREAM System and Interface Design Reference Guide
4. A Deep Drive into ARM Debug Access Port
5. Проект Keil автономного SWD программатора DAPProg для микроконтроллеров с ядром Cortex-M
6. CMSIS — Common Microcontroller Software Interface Standard
7. CMSIS Version 5 Development Repository
8. Полноценный шестнадцатеричный редактор на основе HTML5/JavaScript
9. Flash programming algorithms