Разработка стенда серийной прошивки и первоначального тестирования

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

В процессе серийного производства электронных устройств следующим этапом после монтажа печатных плат является стенд серийной прошивки и тестирования. На нем производится прошивка микроконтроллера/микропроцессора, запуск платы и ее проверка в том или ином объеме. Естественно, как и монтаж плат, работа стенда также должна быть мах автоматизирована. В крайнем случае, ручные операции, от которых не удалось избавиться, должны быть предельно просты, интуитивно понятны и монотонны. В статье описывается процесс разработки подобного стенда под микропроцессор IMX6ULL.

В конце приводится ссылка на исходный код стенда, который может быть адаптирован под другие семейства микропроцессоров с архитектурой ARM.

Итак, в процессе работы с отладочной платой мы более-менее убедились в том, что выбранный микропроцессор нас более-менее устраивает, - нужные интерфейсы поддерживаются, быстродействия хватает и появилось понимание примерного размера финальной прошивки. Останавливаем свой выбор на данном процессоре. Пора приступать к разработке печатной платы.

Среди прочих требований к плате нужно продумать решение трех вопросов:

  1. где будет храниться образ ядра Linux (прошивка, image): SPI флеш, NAND флеш, microSD карта или еще что-либо;

  2. как эта прошивка будет поставляться в серийно выпускаемые изделия;

  3. как будет задаваться загрузочный интерфейс процессора;

Варианты ответов на каждый вопрос:

  1. Проще всего использовать microSD карт. В этом случае вам нужно лишь подготовить N-ное количество microSD карт с образом ядра вставить их во все изготовленные устройства. Но такой способ может не подойти, если:

  • необходимо разработать устройство в индустриальном исполнении (рабочие температуры от -40 С);

  • вы хотите осложнить доступ к прошивке устройства;

  • устройство может подвергаться вибрациям;

  • у вас есть строгие ограничения по себестоимости конечного изделия и каждый цент на счету (microSD карта + разъем заведомо дороже, чем микросхема флеш-памяти распаянная на плате);

  • у вас есть строгие ограничения по габаритам конечного изделия;

Если microSD-карта не подходит, - остается выбор между NOR и NAND памятью. В целом, при выборе типа памяти нужно отталкиваться от размера финальной прошивки. Если до 32 Мб, - то лучше выбрать микросхему памяти NOR (цена - меньше, ресурс - выше), если больше 32 Мб - то остается NAND.

  1. Если выбор пал на микросхему памяти, распаянную на плате, то есть два варианта:

  • образ ядра прошивается в микросхему до распайки на плате. В этом случае микросхемы по-очередно извлекаются из катушки, устанавливаются в программатор (наподобие такого), затем упаковываются обратно в катушку. Очень трудоемкий и не технологичный процесс.

  • образ ядра прошивается в микросхему после распайки на плате с помощью микропроцессора. Процессор нужно каким-либо образом запустить (при этом загрузочная микросхема флеш памяти пуста!), доставить ему образ прошивки, которую процессор и загрузит в м/сх памяти. Звучит как нечто, что очень сложное в реализации, зато это снижает количество ручных операций в процессе производства. Забегая вперед скажу, что мы выбрали именно этот вариант и нам удалось его автоматизировать.

  1. При работе с отладочными платами вы почти наверняка встречали нечто подобное:

переключатель, задающий загрузочный режим. Дело в том, что процессоры, как правило, поддерживают несколько различных вариантов загрузки, таких как SPI, QSPI, SDHC, UART и пр. Какой-именно вариант загрузки использовать определяется фьюз-битами. Процессор может вычитать фьюз-биты из регистров, либо определив состояние определенных GPIO. Какой именно способ процессору использовать, в свою очередь, задается состоянием двух других ножек (BOOT_MODE0 и BOOT_MODE1).

Также можно встретить вариант одного-единственного варианта загрузки, заданного резисторами:

Если габариты позволяют и не жалко несколько ножек процессора, то лучше выбрать один из этих вариантов (переключатель или резисторы).

В случае же, если вы ограничены в габаритах (наш случай) или нужно высвободить ножки процессора под другие задачи, то остается только третий вариант, - "прожигать" фьюз-биты.

Итак, в нашем случае, ответы на три озвученных ранее вопроса были такими:

  1. Образ хранится в микросхеме памяти. SPI NOR флешка.

  2. Флешка прошивается уже будучи распаянной на плате.

  3. "Прожиг" фьюз битов.

Стенд

Решением задачи при такой постановке вопроса стал разработанный стенд серийной прошивки и первоначального тестирования.

Устройство, под которое был разработан стенд:

SoM-модуль, на борту стандартный для SoM-модулей набор: Процессор, память RAM, память flash, физика ethernet, физика wi-fi. По всем четырем граням расположены контактные площадки в форме полу-отверстий с шагом 1.5мм.

А вот и собственно сам стенд:

С аппаратной точки зрения, все довольно тривиально: по центру набор пружинных контактов под тестируемый SoM, который прижимается сверху вертикальным зажимом. Из подведенной к модулю периферии: UART, JTAG, Ethernet. Ну и питание.

Алгоритм работы стенда

В ручную, процедура прошивки и прожига фьюз-битов выглядит следующим образом:

  1. Тестируемое устройство устанавливается в стенд

  2. подключается usb-uart переходник, программатор JLink, Ethernet-кабель, подается питание.

  3. Запускается openocd сервер (openocd.exe);

  4. Производится подключение к openocd серверу по telnet (openocd клиент);

  5. Открывается COM-порт, назначенный USB-UART переходнику;

  6. Сброс процессора (через консоль openocd-клиента):

reset init; arm core_state arm; halt;
  1. Грузим образ u-boot (через консоль openocd-клиента):

load_image u-boot.imx 0x877ff400
  1. Грузим образ ядра linux (через консоль openocd-клиента):

load_image openwrt-imx6ull-cortexa7-video-squashfs.mtd-factory.bin 0x82000000
  1. Запускаем работу процессора (через консоль openocd-клиента):

resume 0x87800000

В это время должен стартануть u-boot и в консоли com-порта появятся логи загрузки. Нажимаем enter, чтобы остаться в консоли u-boot.

  1. Определите флешку (консоль COM-порта):

=> sf probe 
  1. И прошейте флешку (консоль COM-порта):

=> sf update 0x82000000 0x1000 0x8000
  1. Прожгите фьюз-биты (консоль COM-порта):

=> fuse prog -y 0 5 0x0a000030
=> fuse prog -y 0 6 0x00000010
  1. Ребутните процессор (консоль COM-порта):

=> reset

После этого процессор должен ребутнуться и загрузить ядро линукс с флешки без помощи программатора.

Автоматизируй это

Естественно, шаги с 3-его по 13-й можно автоматизировать. По-питонячьи это выглядит так:

# 1. Init OpenOCD server
device_state = Queue()
p_openocd = Process(target=run_openocd_server, args=(device_state,))
p_openocd.start()
check_state(device_state, STATE_JTAG_DEVICE_DETECTED)

# 2. Init. Connect to OpenOCD server via Telnet
telnet_socket_write_queue = Queue()
p_telnet = Process(target=run_telnet_client, args=(telnet_socket_write_queue,))
p_telnet.start()
check_state(device_state, STATE_TELNET_CLIENT_CONNECTED)

# 3. Init. Open COM port.
device_state_com = Queue()
com_port_write_queue = Queue()
p_com_port = Process(target=run_com_port_handler, args=(device_state_com,com_port_write_queue,CFG_COM_PORT,))
p_com_port.start()

# 4. Reset CPU
telnet_socket_write_queue.put((TELNET_CMD_RESTART_CPU + "\r\n").encode())
check_state(device_state, STATE_CPU_RESET_COMPLETED)

# 5. Load u-boot image
telnet_socket_write_queue.put((TELNET_CMD_LOAD_U_BOOT_IMAGE + "\r\n").encode())
check_state(device_state, STATE_IMAGE_LOADED, 30)

# 6. Load kernel image
telnet_socket_write_queue.put((TELNET_CMD_LOAD_KERNEL_IMAGE + "\r\n").encode())
check_state(device_state, STATE_IMAGE_LOADED, 600)

# 7. Start U-boot execution
telnet_socket_write_queue.put((TELNET_CMD_START_U_BOOT + "\r\n").encode())
check_state(device_state_com, STATE_U_BOOT_CONSOLE_ACCESSED)

# 8. Check flash IC available
com_port_write_queue.put(COM_CMD_CHECK_FLASH + "\r")
check_state(device_state_com, STATE_FLASH_IC_DETECTED)

# 9. Flash firmware to IC
com_port_write_queue.put(COM_CMD_WRITE_FIRMWARE_TO_FLASH + "\r")
check_state(device_state_com, STATE_FIRMWARE_FLASHED_TO_IC, 300)

# 10. Burn fuse bit (ECSPI3 programming 0x450 = 0x0a000030)
com_port_write_queue.put(COM_CMD_BURN_FUSE_BITS_1 + "\r")
check_state(device_state_com, STATE_FUSE_BITS_BURNED)

# 11. Burn fuse bit (BT_FUSE_SEL programming 0x460 = 0x00000010)
com_port_write_queue.put(COM_CMD_BURN_FUSE_BITS_2 + "\r")
check_state(device_state_com, STATE_FUSE_BITS_BURNED)

# 12. Reset device
com_port_write_queue.put(COM_CMD_RESET_DEVICE + "\r")

Исходники можно найти тут. Скрипт может быть относительно легко адаптирован под другие семейства процессоров с архитектурой ARM (семейства IMX компании NXP, по крайней мере).

TO-DO

В планах - замена шага №6 (загрузка factory образа по JTAG занимает очень много времени) на загрузку с TFTP-сервера с помощью u-boot утилиты tftp.

Источник: https://habr.com/ru/post/647487/


Интересные статьи

Интересные статьи

Продолжаем серию материалов про создание системы заметок. В этой части мы спроектируем и разработаем RESTful API Service на Golang cо Swagger и авторизацией. Будет много кода, ещё больше рефакторинга ...
Выгрузка пользователей из 1C ЗУП в Битрикс24 или правдивая история о том как настроить интеграцию 1С-Битрикс24 с ЗУП без 1С-ника.В жизни так бывает, причём бывает чаще чем хотелось бы, хоть в целом и ...
История сегодня пойдёт про автосервис в Москве и его продвижении в течении 8 месяцев. Первое знакомство было ещё пару лет назад при странных обстоятельствах. Пришёл автосервис за заявками,...
После того, как вы прочитали базовые шаги по написанию Hello World ядра из цикла имеющихся на Хабре статей, самое время приступить к серьезной разработке самых базовых инструментов: аллокатора ку...
Несколько лет назад я изготовил на микроконтроллере ATmega8 часы с будильником, где реализовал однотональный (одноголосный) простейший синтезатор мелодий. В Интернете немало статей для начинающих...