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

В процессе серийного производства электронных устройств следующим этапом после монтажа печатных плат является стенд серийной прошивки и тестирования. На нем производится прошивка микроконтроллера/микропроцессора, запуск платы и ее проверка в том или ином объеме. Естественно, как и монтаж плат, работа стенда также должна быть мах автоматизирована. В крайнем случае, ручные операции, от которых не удалось избавиться, должны быть предельно просты, интуитивно понятны и монотонны. В статье описывается процесс разработки подобного стенда под микропроцессор 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. При работе с отладочными платами вы почти наверняка встречали нечто подобное:

856a002471a2ba9fbc9366e3e9361bb5.jpg

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

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

92f4a5885d6ab74522b8cab4b8ad382e.PNG

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

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

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

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

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

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

Стенд

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

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

5c99e27146ac6af2019df912d1e3ca69.jpg

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

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

b6a10ca3bdbee34db19c7cb9358dd8be.jpgea44ecca4e16a6260c0c0b0a16d289d7.jpgca3672ad0bbf9ed42129e57f625389b1.jpg

С аппаратной точки зрения, все довольно тривиально: по центру набор пружинных контактов под тестируемый 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.

Habrahabr.ru прочитано 21358 раз