Содержание статьи
На рынке доступно множество микросхем памяти SPI Flash 25-й серии, которые совместимы между собой по принципу работы и электрическим параметрам. Но, как оказалось, не все они одинаково хорошо работают с самодельным программатором, собранным на основе клона Arduino UNO.
В этой статье мы разберемся, почему такой программатор отказывается работать с чипом MX25L6406E производства Macronix. По ходу дела разработаем и обоснуем метод оценки качества работы программатора, проведем множество экспериментов, выдвинем и опровергнем несколько гипотез, построим теоретические модели соединительных проводов.
Еще мы выполним имитацию их работы в программе Quite Universal Circuit Simulator и в конце концов найдем и устраним «слабое звено». Ты увидишь, что длина соединительных проводов имеет значение, но отнюдь не решающее.
От полного непонимания к началу диалога
В предыдущей статье «UNO-программатор для SPI Flash. Прошиваем маршрутизатор подручными средствами» я рассказал о том, как собрал программатор, записав скетч frser-duino в клон Arduino UNO, и успешно применил его для записи дампа прошивки маршрутизатора в микросхему Winbond 25Q128FVSG с помощью утилиты flashrom. К сожалению, маршрутизатор с новой прошивкой не завелся, и возникла мысль записать прошивку в микросхему Macronix MX25L6406E, объем памяти которой в точности соответствовал размеру файла с дампом. В спецификации на этот чип указано, что он работает на напряжениях от 2,7 до 3,6 В и все восемь его контактов имеют те же назначения, что и у предыдущего «пациента».
www
Спецификация микросхемы Macronix MX25L6406E доступна на сайте производителя.
Я не испытывал тревоги: ну что тут может пойти не так? Разве что емкость микросхемы составляет не 16 Мбайт, а только 8 Мбайт, но это и к лучшему — процесс займет меньше времени. О, как же я ошибался!
Мне так понравилось пользоваться «картриджем», изготовленным из микросхемы SPI Flash, что изделие MX25L6406E я тоже поместил на переходнике SOP8 — DIP8 с контактами и установил на панельку DIP8 платы программатора с резистивными делителями для согласования уровней сигналов. Конструкцию платы тебе напомнит этот рисунок.

Не ожидая никакого подвоха, я набрал команду, чтобы узнать идентификатор микросхемы в контексте UNO-программатора:
flashrom -p serprog:dev=/dev/ttyUSB0:115200
Но вместо ожидаемых сведений на экране появилось сообщение об ошибке:
No EEPROM/flash device found
Что такое? Программатор не видит микросхему? Наверное, контакты окислились. Отключил программатор, достал картридж, протер его ножки, установил обратно, повторил команду. Опять не видит! Проверил соединительные провода и не обнаружил ошибок коммутации. Но программатор продолжал упорствовать: никакой микросхемы не подключено. И спорить с ним было бесполезно.
Я перечитал спецификацию MX25L6406E и не нашел ничего нового для себя. Результаты поиска в интернете тоже не дали готового решения. Те, у кого возникла такая же проблема, в основном сетовали на большое количество поддельных чипов и отказывались от дальнейших попыток. Но встретилось несколько упоминаний о том, что иногда программирование проходит с большей стабильностью при пониженном напряжении. Это уже кое‑что!
Почему резистивный делитель не подходит для питания
Ты можешь спросить: а почему бы не понизить напряжение питания с помощью резистивного делителя так, как мы делали для сигнальных линий? Но это плохая идея, и вот почему. Во‑первых, на параметры резистивного делителя большое влияние оказывает сопротивление входа нагрузки, которое в нашем случае не так велико.
Во‑вторых, ток питания микросхемы в режиме записи VCC Program Current составляет, если верить спецификации, 20 мА, что на много порядков превышает ток сигналов. Это не позволяет использовать высокоомные резисторы в плечах делителя — они просто не обеспечат прохождения необходимого тока.
А использование низкоомных приведет к значительному току через резистивный делитель на общую шину, что может вызвать перегрузку источника. Обычно для понижения напряжения питания используют преобразователи DC/DC на требуемое значение.
Немного понизить напряжение питания проще всего последовательным включением на линии VCC диода в прямом направлении. Я подключил диод 1N4148, и напряжение с 3,3 В упало до 2,9 В. При этом оно все еще находится в допустимом диапазоне, который указан в спецификации.
В ответ на повторный ввод команды я увидел такие строки:
...
Found Macronix flash chip "MX25L6405" (8192 kB, SPI) on serprog.
Found Macronix flash chip "MX25L6406E/MX25L6408E" (8192 kB, SPI) on serprog.
Found Macronix flash chip "MX25L6436E/MX25L6445E/MX25L6465E/MX25L6473E" (8192 kB, SPI) on serprog.
Multiple flash chip definition match the detected chip(s): "MX25L6405","MX25L6406E/MX25L6408E","MX25L6436E/MX25L6445E/MX25L6465E/MX25L6473E".
Please specify which chip definition to use with the -c
Ну вот, дело сдвинулось с мертвой точки! Прочитанному программатором идентификатору микросхемы удовлетворяют несколько моделей чипов, и нам предлагается указать конкретный модельный ряд с помощью параметра -c. В нашем случае подходит второй из предложенных вариантов, поэтому команду записи файла zte_h118n.
, размер которого совпадает с емкостью микросхемы, можно сформулировать так:
flashrom -p serprog:dev=/dev/ttyUSB0:115200 -c "MX25L6406E/MX25L6408E" -w zte_h118n.bin
На экране побежали информационные сообщения выполняемого процесса, но завершился он неудачей:
...
Verifying flash... FAILED at 0x00000002!
Expected = 0x00, Found = 0x01, failed byte count from 0x00000000-0x007fffff: 0x15a53
Your flash chip is in an unknown state.
Вот так, при сверке было обнаружено 88 659 расхождений, а микросхема перешла в неопределенное состояние.
Как слышно? Прием!
Я пробовал повторить операцию, но без особых успехов. Надо было что‑то менять в устройстве этой экспериментальной установки. Попробовал подключить еще один диод по линии питания, но после этого микросхема снова перестала определяться.
Может быть, понизить напряжение не питания, а информационных сигналов? Давай попробуем! Тем более что для этого нам не придется вносить существенных изменений в конструкцию. У нас уже есть три «понизителя» напряжения на резистивных делителях. Для ясности схему одного из них я показал на рисунке.
Если увеличивать сопротивление резистора R1, то напряжение сигнала на выходе будет уменьшаться. А для увеличения сопротивления достаточно последовательно к R1 подключить резистор R3. Это можно сделать даже без пайки, просто скрутив один вывод резистора R3 c концом соединительного провода, а другой вывод использовать в качестве штекера.

Чтобы снизить риск повреждения микросхемы памяти во время экспериментов, я решил сначала добиться устойчивого чтения, а к записи перейти позже. И вот, убрав диод с линии питания и подключив на линии SCK (вывод 13 UNO-программатора) и MOSI (вывод 11 UNO-программатора) по одному добавочному резистору сопротивлением 2000 Ом, я выполнил чтение содержимого микросхемы памяти.
info
Считывание содержимого микросхемы памяти было возможно только в том случае, если добавочные резисторы размещались на конце провода, подключенного к UNO-программатору. Если перевернуть провод и поместить резистор на стороне платы SPI Flash, то программатор волшебным образом перестает обнаруживать микросхему.
Но как узнать, было ли это чтение безошибочным, если данные в микросхему были записаны с ошибкой? Сравнение с исходным файлом в этом случае ничего не даст. Тогда я решил выполнить чтение еще раз и сравнить между собой полученные результаты. Если они совпадут, то цель достигнута. А если нет, то за оценку количества ошибок чтения можно принять величину d/2, где количество расхождений d значительно меньше размера файла (посмотри на рисунок).

У меня получилось d = 399 858 расхождений, что соответствует 199 929 ошибкам, или ошибкам в (199 929 / 8 388 608) × 100 = 2,38% байт. Насколько можно доверять такой оценке и насколько она стабильна? Может быть, в следующий раз будет 25%, а потом 0,01%?
Обратимся к статистике
Чтобы не гадать на кофейной гуще, я решил прибегнуть к статистике. Для этого написал на Python программу compare.
, которая побайтово сравнивает два файла и подсчитывает количество обнаруженных различий.
Вот листинг программы compare.
для побайтового сравнения двух файлов.
import sysif len (sys.argv) != 3: print ("Binary files comparison.") print ("Usage: %s file1 file2" % sys.argv[0]) quit ()print ("Files comparison: %s <---> %s" % (sys.argv[1], sys.argv[2]))bufsize = 4096diffs = 0size = 0f1 = open (sys.argv[1], 'rb')f2 = open (sys.argv[2], 'rb')n = 1while n > 0: b1 = f1.read (bufsize) b2 = f2.read (bufsize) n = min (len (b1), len (b2)) if n > 0: size += n for i in range (0, n): if b1[i] != b2[i]: diffs += 1print ("Compared size : %d" % size)print ("Differences : %d (%.2f%%)" % (diffs, round(diffs / size * 100, 2)))f2.close ()f1.close ()
Продолжение доступно только участникам
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее