Как я уже писал в своём первом топике на Хабре про создание печатной платы, моя фирма занимается разработкой устройств для телефонии разного рода VoIP-, GSM-, PBX-шлюзов, розетки-ребутеры управляемые через GSM и прочее. Сегодня я опишу процесс разработки прошивки для устройства SimBank в его PCI версии, где я выступил в роли разработчика схемы для ПЛИС.

История постановки задачи от руководства + история работы устройства


Устройство SimBank предназначено для работы с SIM картами (Subscriber Identification Module — модуль идентификации абонента). Данные карты являются разновидностью ISO-7816 карт. Может использоваться для централизированного хранения Sim-карт или других видов smart-card, и последующего использования их в устройствах типа GSM-шлюз, тюнеры спутникового телевидения или в других устройствах использующих при работе smart-card. Совместно с программным обеспечением Sim-server предоставляет гибкую систему для контроля и учета сим-карт, используемых в ваших приложениях, широкие возможности для настройки и конфигурирования работы системы через удобный web-интерфейс. При этом сами карты хранятся в доступном для вас месте и соединяются с оконечными устройствами по TCP/IP протоколу.


На Хабре есть статьи описывающие устройстви и принцип работы со смарт картами:

Краткое введение в SIM-карты от OgreSwamp
Смарт-карты для самых маленьких от brake
Как устроена смарт-карта от rlepricon
Я их все с удовольствием прочитал перед началом разработки. И повторять написанное в них не буду.


Устройство SimBank

Само устройство было сделано на фирме уже давно и удачно использовалось в течении нескольких лет. Но особенностью всех PCI устройств на фирме было учебное PCI ядро, “которое поставлялось как есть и претензии не принимались”. Они и не выставлялись. С минимальными переделками это ядро ставилось на все без исключения платы. Это позволяло со знанием дела использовать доступное адресное пространство. С одним и тем же Vendor ID, Device ID, Class Code. Так как устройства обычно собираются на фирме и проверяются на работоспособность, то конфликтов не возникало. В условиях ограниченных временных рамок принцип “работает — не трогай” довольно уместен. Так продолжалось до каких-то пор пока на некоторых современных материнских платах не стали проявляться нестабильность в работе или вообще плата не хотела заводится.

Пришла пора более тщательно разобраться с ядром PCI. Учебное ядро доработанное под свои задачи было документировано комментариями, но их не всегда было достаточно. Ссылка на место, откуда оно было взято, датированная 2002 или 2004 годом — не работала.
Задача проста как день: “Устройство должно работать на всех стандартных платах”.
В качестве бонуса: “Даже если это будут не все 200 SIM карт, а 196 или 1хх”.
До этого момента я никогда не работал ни с SIM картами, ни с PCI шиной в качестве разработчика ПЛИС. Только схема и плата. Опыт разработки на VHDL и Verilog уже был.
Разборки решено было начать с PCI, так как часть относящаяся к SIM картам работала.
Первое прочтение спецификации “PCI Local Bus” не подействовало чудесным образом и не сильно пролило свет на то как всё нужно делать. Начитавшись статей о том, как делать и как не делать я так толком ничего еще и не поняв, решил: “нужно делать, по ходу дела разберусь”.
Для PCI у Altera в среде Quartus есть Мегафункция, которая позволяет генерировать ядро PCI с необходимыми нам параметрами. К ядру прилагается хорошая документация, на сайте есть описание PCI-кита, к которому прилагается PCI модель, для поведенческого моделирования в ModelsSim-Altera. Всё это вместе — здорово помогает освоится с нуля в этой теме.
Также Altera разрешает пользоваться своим Vendor ID (1172h).
В спецификации PCI выбран класс устройства не доступный в ранних версиях спецификаций: 07h — Simple Communication Controllers, 05h- Smart Card.
Далее всё должно работать в Linux. Так как у коллеги больше опыта работы и с SIM картами и с PCI, то с него технические требования. За что ему огромное спасибо. Грамотно составленный документ, это половина дела. Хоть и требует “лишней писанины”.

Изучение материала


Так как устройство разрабатывал не я, то мне также необходимо было ознакомится с проектом схемы и платы.
Схемная часть необходима для понимания, что откуда идёт и к чему присоединяется, а плата даёт понять куда в вставлять программатор, SIM карты и где находится та или иная служебная индикация. О том как это сыграло злую шутку со мной я опишу чуть позже.



По схеме мы имеем плату с двумя ПЛИС Altera Cyclone II EP2C35672C8 в корпусе с 672 контактами. Одна ПЛИС присоединена к PCI шине и она же управляет второй ПЛИС. К каждой ПЛИС присоединены по 100 держателей SIM карт. К каждой SIM карте подведен свой сигнал сброса и данных, сигнал CLK уходит на на группу из 10 карт.



Каждая микросхема EP2C35 содержит 105 M4K блоков внутренней памяти. Всего в блоке RAM (включая бит чётности) 4,608 бит.

Блоки поддерживают разные конфигурации от 4K × 1 до 128 × 32(36).

После этого уже мной был написан документ с дополнительными вопросами, что за чем будет подаваться, кто следит за длительностью тех или иных сигналов. Кто считает правильность прихода данных от SIM и прочее.

Разобравшись с контактами первой от шины PCI микросхемы, я сделал пробный проект ядра PCI в Quartus, который должен был заработать с новыми параметрами на шине. Без какой либо логики — просто ядро.
Получив ответ на команду “lspci -vv” моему счастью не было предела.



Обсудив с разработчиком драйвера и прикладной программы дальнейший ход работы, мы сделали пробный проект в которым драйвер записывал в память данные, а потом их считывал и сверял. Изначально проверка делалась только по первой в схеме микросхеме.
Далее был разбор со второй микросхемой.
Между двумя микросхемами на схеме предусмотрено 36 сигнальных линий + 2 линии между выходами PLL_OUT и CLK. В предыдущем проекте использовались 19 линий адреса и два байта на обмен данными (по одному в каждую сторону) сигналы чтения и сигнал записи. Это было сделано исходя из специфики работы имевшегося ядра. И для получения универсальности драйвера и программ. Так например есть устоявшаяся часть адресов откуда можно вычитать служебную информацию.
В ARM весриях хранится информация типа



Elgato G4_1 — тип устройства (для PCI плат “K16” или “К32”, для Симбанка — “SimBank”)
SIM51215 (SIM900 или другой) — тип установленного GSM или 3G модуля (необходим для выбора набора AT-команд). К описываемому устройству имеет отношение в плане какую SIM карту мы можем через данный модуль вывести в сеть оператора.
2014 — год разработки
ver.14.144 — номер версии прошивки
SN: 0123

И если перед прошивкой её не забыли изменить на правильную, то по этим данным можно узнать много интересного из “амбарной книги”, где записано под кого делался проект и какие отличия от стандарта пожелал заказчик.
Для Симбанка достаточно серийного номера. Так как конструктивно плата Симбанка всегда одна в устройстве. И в PCI версии такая плата одна на фирме.

Болезненное решение


В предыдущей версии ядра использовались два BAR. BAR1 c отображением в память размером 1М, BAR0 как регистры ввода вывода размером 4К. Вcё адресное пространство отдавалось под обе ПЛИС одновременно.
От применения IO решено было отказаться по разным причинам. Во первых разработчику драйвера работать с памятью гораздо проще. Во вторых в сети есть множество рекомендаций почему при возможности стоит отказаться от IO в новых разработках. В третьих, количество IO ресурсов доступное для настройки в ядре от Altera ограничено 256. Ресурсов памяти можно выделить гораздо больше. Но этот переход повлек за собой изменение работы с модулями SIM карт. И означал почти полную переделку модуля обмена с SIM. К тому отход от привычной адресации означал, что мы не сможем прочитать любой своей программой в привычном месте нужные нам битики.
Можно было не делать на два базовых адреса и обойтись одним. Не экономить один тактовый сигнал при обмене с картами первой микросхемы. Это часть обмена данными не занимается передачей голоса и нам не нужно экономить милисекунды для того чтоб получить задержку не более 20 мс. Но такое мне показалось правильным.

Структура


После первых успехов и проверки записи в память тестовых данных пришло время выстроить структуру обмена данными с SIM картами. Примерное представление как нужно будет делать у меня было давно, но после тестов я готов был предположить, что оно будет работать. И тесты проделаны были продуманны и проделаны не зря.
Существующих 36 + 2 сигнальных линий не хватало для того, чтобы транслировать служебные сигналы PCI шины на вторую микросхему без изменений, а ограничивать приём — передачу одним или двумя байтами мне не хотелось. Даже если удастся разнести шину адреса и данных.
Поэтому 32 из 36 сигнальных линий сразу же были отданы на шину данных (и адреса), 1 сигнальная линия управляла общим сбросом системы, а на остальные три гуляем вешаем всё управление. Для передачи сигнала CLK у нас была отдельная линия.
Режим молчания, режим передачи адреса, режим записи одиночных данных, потоковых данных (Burst), режимы записи по маске, чтение и потоковое чтение. Всего восемь состояний шины которые уложились в три сигнальных линии.
Осталось только передать четыре сигнала BE для записи по маске. Их было решено запрятать в старшие четыре бита на шине данных если у нас используется такой режим.

Для каждой SIM карты в ПЛИС был выделен максимально возможный буфер. Так как со Smart картой осуществляется однопроводный полудуплексный интерфейс, карт у нас 100, а ячеек памяти в микросхеме всего 105, то и буфер решено было сделать один на приём и передачу. И обнулять адресацию при начале передачи или приёма. Мне повезло и согласно стандарту от SIM карты не может придти больше 256 байт данных за одну команду. А значит доступный буфер не переполнится пока мы его не вычитаем.
Термин “команда” тут немного отличается от стандарта ISO-7816. Там под командой понимается обращение к карте, затем подтверждение от карты, а затем данные. Но меня интересует количество данных от обнуления до обнуления буфера.

Буфер для обмена данными со второй микросхемой решено было не делать, а сделать непосредственную запись в память для SIM карт во второй микросхемы. Так как у меня уже было занято 102 блока памяти из 105. Выбор первой и второй микросхемы выбирается через обращение к BAR0 или BAR1 соответственно.
Для BAR0 и BAR1 сделаны разные контроллеры. Обращение ко второй микросхеме происходит на такт позже, но это регулируется задержкой выставления сигнала TRDY на стороне PCI шины.

Для каждой микросхемы выделено 64К памяти чего достаточно для обслуживания 128 Smart карт. У нас 100. Остается еще 28 блоков. В двух блоках разместим служебную информацию где будет храниться информация о состоянии сброса карты, скорости обмена. Обратно нам нужно возвращать количество принятых данных от SIMки.

Дальше всё это нужно заставить работать и отладить. Так как проект для 100 SIM карт компилируется очень долго, да и 100 SIM карт спалить гораздо дороже чем 1 или 10 то решено отладить обмен сразу на 1 — 5 картах, а потом уже добавлять все остальные. Причем сделать отладку только первой половины, а уже когда решим, что тут всё получилось, идём во вторую микросхему.
И тут-то меня ждал сюрприз.
Есть схема, на которой контакты карт пронумерованы от 1 до 100. Есть проект платы из которого удачно импортированы настройки контактов ПЛИС (PCI работает, тестовая запись в память работает, обмен между микросхемами также работает). Проект собран для 5 карт, чтоб если ошибусь с началом нумерации от 0 или 1, то вторая карта бы заработала. Дудки.
Ну ничего, у нас есть 10 светодиодов на плате. И хоть ими я собирался заниматься потом, пришлось потом пододвинуть чуточку поближе к сейчас. Добавляем простую вещь на включение/выключение светодиода, а лампочка не горит светодиод не мигает.
Берем осциллограф. CLOCK есть, а сброса нет. Данных тоже нет.
Импорт данных подкачал? Сверяем схему с платой, плату и схему с Pin Planner в Quartus. Совпадают. А сброса нет.
И вот тут мне на помощь приходит старший товарищ. И говорит, что “принцесса в другом замке” в программе “SIM0” находится с другой стороны коробки. И что всегда так было.
Сюда можно заскриншотить кусок из PCB проекта в P-CAD. Там есть подписи сигналов

На плате это SIM 199, а в проекте SIM0. Проект компилируется всего для 5 карточек. (Для 100 он просто ещё не влезает). И поэтому остальные 95 карт просто не обслуживаются. И для них даже буфер памяти в ПЛИС не формируется.

Для того чтобы сэкономить ресурсы решено было тактировать блоки памяти частотой на которой работают SIM карты. Так меньше нужно будет делителей. А с меньшей частотой легче будет влезть в скоростные ресурсы микросхемы. При этом планировалось в буфер для SIM карты писать на скорости шины PCI (33 МГц), а считывать на скорости работы с SIM картами. Из-за ограничений на реализацию двухпортовой памяти в Cyclone II пришлось искать другие решения. В итоге самым простым и правильным оказалось тактировать все блоки частотой 33 МГц от PCI_CLK. А уменьшение количества многоразрядных счётчиков пришлось сделать путём сигналов разрешения для тактирования регистров. К тому же такой подход позволил проще писать временные ограничения для TimingAnalyzer используя multycycle.

Настройка модели. Для успешного завершения всех изобретений нам нужна модель. Для PCI ядра модель предоставляет Altera. Для Smart карт пришлось писать модель самостоятельно.
Минус такого решения, если я неправильно понимаю как должна работать SIM карта, то я так же неправильно напишу модель. Но всё обошлось. Правда не сразу.
Моделирование позволило на первых парах не сильно отвлекать коллегу, хотя потом на этапе отладки в железе нам пришлось хорошенько разбираться. Что не так в модели, и как оно должно быть.

T0/T1 или не нужно делать как лучше, делайте как правильно


Изначально в автомате работы с SIM картой отлаживалась работа по сбросу и чтению с SIM карты ATR, смена скорости и передача простой команды «A0 A4 00 00 02» и на всё это нужно было получить ответ «A4». Мне хотелось сделать автомат который бы по включению питания сам бы всё считывал и к моменту загрузки ОС работал с SIM картой на максимальной для неё скорости. Это бы позволило разгрузить программу от инициализации двухсот(200!) SIM карт во время старта.
С некоторыми SIM картами всё замечательно прошло, а некоторые упорно отдавали h’3B и молчали. В итоге срабатывал таймер в автомате и я сбрасывал SIM карту.Так как она не сказала мне на какой скорости с ней можно поговорить. Если же я получал три или больше трёх байт, то я считал что ATR считан и можно работать дальше. Хотя на модели всё было замечательно, но как оказалось у разных SIM карт по разному выдаются ATR. Одни выдают всю информацию сразу, а у других после отправки заголовка h’3B проходит пауза, а затем выдаётся вся информация. Увеличение паузы решило эту проблему. До поры до времени. Некоторые SIM карты выдают завершающий бит с такой же большой паузой как и в начале после байта h’3B.
Решено. Никаких умных автоматов, самопроизвольных переключений скорости и прочего. Пусть об этом думает процессор. Принимает решение когда выдать сброс, убрать его, и пусть считает когда порция данных от карты завершена.
Но есть проблема. Мы еще не настроили все плюшки, а у нас влазит всего 80-85 SIM карт из необходимых 100 в каждой микросхеме. Пусть это делает драйвер операционной системы. Ему не сложно, а нам просторнее.
Дальше пошла отладка работы с другими командами уже получаемыми от драйвера из ОС. Отладка на большом количестве SIM карт. И новые “открытия”.

197 из 200


Как решить если 197 из 200 карт работают, а три нет то это точная работа или нет? Отработка велась на заблокированных оператором SIM картах, которые еще отвечали на сброс и делали вид, что записывают в себя SMS. Может три карты заблокированы не зря и это они ошибаются? Как я был наивен.
Первая же работа в реальном режиме показала, что с их стороны всё верно. Ищи.
И я нашёл. Не сразу. Помог мой коллега который организовал логи обмена по всем SIM картам. оказалось, что иногда при записи больших команд у меня перескакивал счётчик. И я два раза писал один байт в середине длинной передачи. Так как поведенческое моделирование дело долгое, то длинные команды в модели я не использовал. Ограничивался короткими транзакциями. Делал усеченные размеры счётчиков. Сделал шаг, смоделировал, проверил, заработало. Маленько “Ура!” и дальше продолжим. Записали байт, слово, двойное слово. Прошли пару адресов — значит и дальше всё будет хорошо.
И вот, на маленькой модели всё прекрасно, а в жизни не работает. При этом до момента ошибки SIM карта успевает отдать свой номер (если он записан в карте), ICCID, IMSI и что-то ещё. А при имитации записи/чтения в SIM карту. Не всегда, лишь с тремя из двухсот карточек происходят ошибки.
Ручное сравнивание лога программы и логического анализатора показало, что ошибка происходит в теле SMS сообщения. При этом 197 карт пропускают эту ошибку сквозь пальцы подсчёт CRC. Зачем напрягаться для каких-то SMS, а три карты дождались таки своего звёздного часа.
Я доработал модель запустил, а на ней снова всё в порядке. Как так?
Запускаю модель с полной проверкой счётчиков (Делим 33 МГц на 6, потом на 372, а потом еще и ещё, вместо условно поделим на 4, а потом еще на 16). Запускам вечером модель, в надежде утром всё увидеть…
И утром застаём свою ОС спящей, детским сном. У неё выключался экран в режим энергосбережения. Но до спать у нас с ней не доходило. После 4+ часов моделирования более 300 сигналов и шин, она уснула “не разуваясь”. И утром завелась с третьего раза. Какая это радость, что мне не пришлось восстанавливать всю систему.
День работы, отключение сна. И уже на следующее утро я смог найти ошибку. Правда перед этим целый день у меня ушёл на разборки со всякими другими премудростями. И утром счастливого дня я уже догадывался где ошибку искать.
Она не была последней, были и другие более явные и лёгкие в плане добычи ошибки.

Собираем мозайку


Сделав предположение, о том что работа со Smart Card стабильна, я подключил вторую микросхему. Сначала в проекте. Потом на модели. Потом в железе. Не всё сразу, но и эту часть мы побороли. Как проверить, что все 200 мест работают одновременно и не мешают соседям? Где взять столько SIM карт? Нашлись по сусекам 200 штук. Запустились.
Запустились, отдали банк на проверку. И вот еще один сюрприз. Если в SimBank заняты все места, то всё хорошо. Карты работают стабильно, сбрасываются не часто. А если часть карт не установлена, то на их места приходит периодический сброс. Но иногда сброс приходит на симметричные карты соседней ПЛИС. Почему?
Ответ на этот вопрос я до сих пор толком не знаю. Но зато я теперь знаю, что такое Signal Tap II Logic Analyser в Quartus от Altera. С помощью него получилось через JTAG вытащить состояния сигналов на шине PCI. Изменяя проект и создавая новые сигналы для старта отсчёта или стопа удалось просмотреть некоторые части внутри ПЛИС. Так я например с удивлением узнал, что при чтении или записи больше восьми байт у меня почти никогда не включается Burst режим. По записи вообще никогда, а при чтении не более двух тактов PCI. Хотя из программы приходит команда “считать 40 байт”, а считывание идёт по четыре байта. С выставления адреса на PCI



Проблему сбросов частично удалось решить программно со стороны драйвера, Частично ограничением не использовать нечётное количество SIM карт в банке до лучших времён.

Вопрос к аудитории: есть ли в Linux (Centos 6 32-бит) возможность писать все логи обращения устройству? Желательно при этом не нарушать работы PCI шины. Так чтоб можно было задать Vendor ID/Device ID и сохранять его логи обращений R/W на шине?

Итог


В итоге к окончанию всех улучшений полный проект в ПЛИС расходует 89% логических элементов и 85% доступной внутренней памяти. Всё влезло, а это означает, что элементарная база и структура проекта выбраны верно.
По схеме сэкономлено два 50 МГц генератора, так как в проекте берется только тактовый сигнал PCI 33 МГц, они не используются и в дальнейшем могут не устанавливаться на плату. Проект работает со всеми “подсунутыми” на сегодняшний день материнскими платами.
Устройство может регистрировать все карты в сети одновременно, лишь бы хватило GSM каналов.

Сейчас помимо обновления прошивок и драйверов у заказчиков несколько устройств трудятся у нас. Одни отправляют SMS для проекта SprintSMS. Другие находятся “под охраной” нашей технической поддержки и трудятся на благо заказчика. Это позволило понять суть работы заказчика, и что ему в действительности нужно. Как в плане эргономики работы так и в плане дополнительного функционала программного обеспечения. К тому же стабильно работающее устройство (тук-тук-тук чтоб не сглазить) позволило более качественно отлаживать программное обеспечение.

Но это уже другая история.

Всем спасибо.
Читателям за проявленный интерес к статье. Авторам других статей — за полезные уроки. Создателям Хабра за Хабр. Коллегам за помощь в трудные минуты и «предоставленный» опыт. Руководителю — за понимание.

P.S. В качестве бонуса картинка с web-интерфейса визуального контроля за работой SimBankа.
Отдельное спасибо за него Максиму.


Здесь SimBank запущен в режиме на 100 карт.


Список карт с вычитанной информацией из них. На картах которые требуют ввода PIN-кода часть информации не доступна.


Web-интерфейс доступен и со смартофна.
Жёлтым выделенны карты которые сейчас в работе.