Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Являясь крупнейшим русскоязычным агрегатором различных статей технического характера, Хабр как-то не добирает «массы» в области разработки цифровой электроники — ПЛИС/FPGA, ASIC, HDL в целом. Хотя пласт технологий в данном случае просто огромный, и я бы сказал, что требуемые знания здесь — это настоящая full-stack разработка: схемотехника, электроника, топология, цифровая логика, синтез и цифровая трассировка, прототипирование в ПЛИС, затем — схемотехника тестовых плат, а если это что-то сложнее простого регистрового управления аналоговым трактом, то еще и низкоуровневое программирование, да и без написания прикладных приложений для управления всем этим тоже по итогу не обходится.
Говорят, критикуя — предлагай, поэтому вниманию читателей предоставляется небольшой экскурс в один из рабочих проектов, а именно — оцифровка и декодирование данных в сфере пассивных RFID-HF устройств.
Пара слов о том, для чего потребовалось всё описанное далее. Для заказчика нами был разработан ASIC-кристалл микроконтроллера с микроразмерами, микропотреблением, питанием от RF-поля и соответственно беспроводным интерфейсом для передачи данных по этому же полю. Это несущая в HF-диапазоне, PIE-модуляция в одну сторону и 0.1% передачи с BPSK-модуляцией поднесущей (по умолчанию с частотой F/16 и четырьмя периодами поднесущей на бит данных) в другую. Коэффициент передачи в 0.1% это, как можно догадаться, очень мало, да и поднесущая не так уж и далеко от основного синала.
Слегка выпрямленный отклик на антенне. Да, это 40 вольт. Слева — ASK-модулированный поток данных в устройство. Справа — требуемый нам сигнал ответа
ASIC был разработан, нарисован, изготовлен и корпусирован, после чего наконец добрался до фазы исследования результата и тестирования. Запитываться с помощью поля — запитывается. Принимать амплитудную модуляцию и интерпретировать данные — тоже справляется. И в ответ функционально корректно модулирует поле. Однако для полного ответа на вопрос «а соответствует ли устройство тем идеям, которые в него закладывали» всего этого недостаточно, потому что без двустороннего обмена данными в реальном времени ответить на него невозможно, так как большая доля функционала по сути зависит как от внешнего «обвеса» устройства (главным образом — катушки-антенны), так и от того, кто это поле включает, параметров этого поля и управления им — ведущего устройства-мастера. Вследствие чего требуется фактически создать прототип такого управляющего устройства, которое будет воспроизводить реальные условия работы всей системы.
Так как в перспективе для управляющего устройства нужна обработка данных и управление «по воздуху» в более-менее автономном варианте, требуется контроллер/процессор. Другая задача — реализовать управление тестовым стендом, а также некоторые интерфейсы общения с ASIC, например отладочный интерфейс и scan-chain тестирование; для всего этого очень подходит реализация ПЛИС, так как большая часть кода под это уже была написана ранее в ходе разработки самого ASIC. Итоговым решением был выбран продукт на основе семейства Xilinx Zynq, представляющий собой интеграцию реконфигурируемого ARM-ядра и программируемой логики в одном корпусе.
Снова возвращаемся к изначальной задаче приёма отклика в RF-поле. Теоретически, демодулировать сигнал до удобоваримого уровня можно и до цифрового домена. Однако это нетривиальная задача даже для фиксированных параметров в условии выше. А если учесть, что частота поднесущей по спецификации может меняться в довольно широком диапазоне, а мощность принимаемого сигнала — плавать в зависимости от таких факторов как расстояние между антенной и катушкой, питание ASIC и выбранные параметры его компонентов, задача становится совсем уж невыполнимой. Поэтомугланды вырежем сейчас, жабры вставим через неделю основная обработка сигнала будет выполняться в «цифре», с небольшой предфильтрацией на входе.
После фильтрации всё уже не так плохо
Конечно, прежде чем работать с сигналом в цифровой области, необходимо собственно его сначала оцифровать. Здесь появляется множество вариантов исполнения АЦП, большинство из которых не подходят по тем или иным причинам.
Во-первых, в Zynq (как и в Artix, и в UltraScale, и в некоторых других FPGA от Xilinx; у конкурентов также имеются подобные коммерческие решения) уже есть многоканальные встроенные АЦП. Только вот частота семплирования у них зачастую не высокая: в выбранном чипе это всего лишь 1 MSPS, то есть полоса в 500 кГц, что для нашего обрабатываемого сигнала фактически граничное значение. А с учётом более широкого диапазона настройки частоты поднесущей и вовсе недостаточное.
Далее идёт широкий спектр компонентных АЦП. Тут на любую частоту и разрешение подобрать подходящее устройство уже несложно. Но подобное решение увеличивает стоимость тестовой платы и сборки, плюс увеличивается либо сложность разработки цифрового описания (если это АЦП с SPI- или I2C-интерфейсом), либо сложность разработки платы (для трассировки параллельного интерфейса). Да и количество цифровых входов Zynq ограничено — в проекте используется не просто Xilinx Zynq, а готовый модуль на его основе, устанавливаемый на плату-носитель.
Поэтому в качестве эффектного и, как оказалось в последствии, эффективного решения (а во многом просто ради ! НАУКИ!) с помощью компаратора, пассивного фильтра и D-триггера был выполнен однобитный дельта-сигма АЦП. Частота семплирования в данном случае совпадает с основной частотой работы цифровой логики — 100 МГц.
Наш АЦП. В роли компаратора — ADCMP600. В роли D-триггера — D-триггер
Работать напрямую с таким сигналом, разумеется, не очень удобно, поэтому далее нужно увеличить разрядность, для чего можно частично пожертвовать частотой. Увеличением разрядности до приемлемых значений, децимацией и фильтрацией сигнала заниматься будет CIC-фильтр, он же — каскадный интегрально-гребенчатый фильтр Хогенауэра. Подробнее можно узнать например здесь, а вкратце — из однобитного сигнала (на самом деле — двухбитного, т.к. проще работать со знаковым представлением сигнала как +1/-1; при однобитном беззнаковом сигнале 0/+1 получаем смещение 0.5 и некоторые другие проблемы работы блоков, в частности фильтров) получаем 12-битный поток с частотой семплирования 12.5 МГц, пропущенный через ФНЧ с частотой среза около 600 кГц.
Выход ДСАЦП и результат работы CIC-фильтра; видна задержка работы ФНЧ — КИХ 80-ого порядка
Таким образом, мы перешли в «цифровую плоскость» решения задачи. Ответный сигнал различим и при должных условиях достаточно красив, чтобы в нём визуально прочитать нужные нам данные. Однако до того момента, как это сможет автоматически выполнять цифровой демодулятор — еще достаточно далеко.
В первую очередь необходимо преобразовать оцифрованный сигнал в оригинальную однобитную последовательность модулированной поднесущей. Самый простой и прямолинейный вариант — обычный компаратор с нулём. В некоторых случаях (узкая полоса, резкие полосовые фильтры, высокое соотношение сигнал-шум) такое решение вполне работоспособно. В нашем случае одного взгляда на сигнал хватает, чтобы понять, почему компаратор не будет работать.
Один из примеров разбираемого сигнала; в данном случае частота поднесущей не кратна опорной частоте, так как схема работала от внутреннего осциллятора, поэтому присутствуют еще и интермодуляции
Во-первых, в аналоговом тракте присутствует разделительная ёмкость, и даже не одна. В том числе она используется для отсечения постоянной составляющей, что приводит к смещению сигнала к нулю. Однако такое смещение происходит недостаточно быстро относительно частоты поднесущей, из-за чего первые несколько периодов выпадают из последовательности. Увеличение же частоты среза получаемого пассивного фильтра высоких частот приводит к задавливанию полезного сигнала.
Во-вторых, шум и интермодуляции в сигнале не позволяют задать такое значение на компараторе, чтобы это удовлетворяло корректной работе втечение даже одной транзакции.
В-третьих, так как мы планируем в последствии еще и демодулировать поднесущую, простого «среза» наверняка будет недостаточно; в идеале хотелось бы иметь саму поднесущую в как можно более чётком виде.
Исходя из всего этого, получаем несколько критериев для будущего алгоритма: детектирование фронтов/пиков, динамическое слежение за «нулём», работа с изменяемой амплитудой, отфильтровывание шумов. Ну и разумеется всё это в реальном времени.
Значительное влияние на результат оказала данная статья из блога fail0verflow, в которой по сути разбирается алгоритм решения идентичной проблемы. В нашем случае было сделано несколько допущений и модификаций: мы работаем с синусоидальным сигналом, а не с импульсным, вследствие чего была изменена логика получения выходного значения на основе обнаруженных пиков. Также добавлена возможность динамического изменения некоторых константных в описанном алгоритме величин (минимальный размах сигнала, пороги различия пиков и т.п.) для исследования работы схемы и поиска оптимальных значений в зависимости от внешних параметров.
Оцифрованный сигнал после фильтра и соответствующий ему результат работы детектора
Где-то на этом этапе садовые гномы из South Park уже получали прибыль; нам же еще остаётся по сути самое главное — демодулировать сигнал и получить из него полезные данные.
Если представить идеальный вариант входного сигнала и работы системы — отсутствие шумов и неточностей аналогового тракта, отсутствие преобразований данных, полностью синхронизированные между собой частотные домены, — имеем на входе меандр с известной частотой и 50% скважностью, а также изменение фазы меандра относительно модулирующих данных. Получить из такого сигнала данные достаточно просто — требуется лишь выполнить операцию исключающего «ИЛИ» (XOR) с меандром поднесущей без модуляции; при совпадении фаз получим «0», при противоположных фазах — «1». Остаётся только сформировать стробирующий сигнал появления нового бита — это будет, допустим, ниспадающий фронт немодулированного меандра. Готово!
Теперь возвращаемся в реальность. Внесение искажений в сигнал приведёт к фазовым ошибкам, и после XOR мы будем получать осечки в местах изменения значения одного из сигналов. До какого-то предела их можно игнорировать — например «забирать» результат между фронтами опорного сигнала там, где значение стабильно. Более стабильным вариантом будет фильтрация, в первом приближении — реализация счётчиков «единиц» и «нулей» XOR-выхода: чего насчитали больше втечение периода поднесущей, такой результат и выдаём.
Такой приём работал бы, если бы наш приёмник был синхронизирован с частотой тракта и частотой работы передатчика. Но даже с таким условием точность будет относительно невысокой и зависимой от частоты поднесущей, которая по умолчанию всего в 16 раз ниже основной частоты. В нашем же случае передатчик может формировать (и по умолчанию формирует) поднесущую относительно своего внутреннего генератора, частота которого с достаточной точностью неизвестна. Это приводит к тому, что сформировать опорную немодулированную поднесущую для XOR уже не получится просто зная делители — через какое-то количество периодов такой сигнал разойдётся по фазе более чем на 180° с принимаемым, и это количество периодов заведомо меньше чем длина пакета, т.е. обычный метод решения проблемы в UART (передискретизация на умноженной частоте и завершение декодирования всех данных — байта — до того, как накапливаемая ошибка превысит 50%) нас не устроит.
Требуется формировать опорный сигнал на основе входящего. Для этого мы замеряем несколько стартовых периодов входящего сигнала и формируем новый меандр с посчитанным количеством «нулей» и «единиц». Начальная часть пакета представляет собой т.н. пилотный сигнал — немодулированная поднесущая с последующим переключением фазы; всё это как раз предназначено для настройки приёмника на декодирование данных. В данном случае нам необходимо отбросить некоторую стартовую часть этого сигнала, ожидая стабилизации постоянной составляющей (вспоминаем предыдущую главу). Также на основе фазовой ошибки между входным сигналом и опорой можно выполнять коррекцию периода и формы опоры. Т.е. реализовывать обратную связь в схеме для фазочастотной подстройки.
Еще одной из модификаций относительно сказанного выше является формирование выходных значений. Вместо того, чтобы фильтровать результат исключающего «ИЛИ», принимаем начальную фазу за «0» и далее с помощью реализованных уже счётчиков детектируем изменение фазы входного сигнала. При нескольких периодах поднесущей на бит сделать это проще и точнее, как показала практика.
По итогу получаем приёмник, при условии хорошего согласования в RF-части стабильно принимающий короткие пакеты данных и достаточно стабильно — большие объёмы вплоть до максимума. Возникновение ошибок в демодуляции далее детектируется за счёт битов чётности и контрольной суммы на уровне протокола, и затем возможно данные запрашиваются повторно. Однако это уже другая история.
Успешно декодированный пакет в 7 бит: «1110110»
Говорят, критикуя — предлагай, поэтому вниманию читателей предоставляется небольшой экскурс в один из рабочих проектов, а именно — оцифровка и декодирование данных в сфере пассивных RFID-HF устройств.
Предисловие и постановка задачи
Пара слов о том, для чего потребовалось всё описанное далее. Для заказчика нами был разработан ASIC-кристалл микроконтроллера с микроразмерами, микропотреблением, питанием от RF-поля и соответственно беспроводным интерфейсом для передачи данных по этому же полю. Это несущая в HF-диапазоне, PIE-модуляция в одну сторону и 0.1% передачи с BPSK-модуляцией поднесущей (по умолчанию с частотой F/16 и четырьмя периодами поднесущей на бит данных) в другую. Коэффициент передачи в 0.1% это, как можно догадаться, очень мало, да и поднесущая не так уж и далеко от основного синала.
Слегка выпрямленный отклик на антенне. Да, это 40 вольт. Слева — ASK-модулированный поток данных в устройство. Справа — требуемый нам сигнал ответа
ASIC был разработан, нарисован, изготовлен и корпусирован, после чего наконец добрался до фазы исследования результата и тестирования. Запитываться с помощью поля — запитывается. Принимать амплитудную модуляцию и интерпретировать данные — тоже справляется. И в ответ функционально корректно модулирует поле. Однако для полного ответа на вопрос «а соответствует ли устройство тем идеям, которые в него закладывали» всего этого недостаточно, потому что без двустороннего обмена данными в реальном времени ответить на него невозможно, так как большая доля функционала по сути зависит как от внешнего «обвеса» устройства (главным образом — катушки-антенны), так и от того, кто это поле включает, параметров этого поля и управления им — ведущего устройства-мастера. Вследствие чего требуется фактически создать прототип такого управляющего устройства, которое будет воспроизводить реальные условия работы всей системы.
Так как в перспективе для управляющего устройства нужна обработка данных и управление «по воздуху» в более-менее автономном варианте, требуется контроллер/процессор. Другая задача — реализовать управление тестовым стендом, а также некоторые интерфейсы общения с ASIC, например отладочный интерфейс и scan-chain тестирование; для всего этого очень подходит реализация ПЛИС, так как большая часть кода под это уже была написана ранее в ходе разработки самого ASIC. Итоговым решением был выбран продукт на основе семейства Xilinx Zynq, представляющий собой интеграцию реконфигурируемого ARM-ядра и программируемой логики в одном корпусе.
Снова возвращаемся к изначальной задаче приёма отклика в RF-поле. Теоретически, демодулировать сигнал до удобоваримого уровня можно и до цифрового домена. Однако это нетривиальная задача даже для фиксированных параметров в условии выше. А если учесть, что частота поднесущей по спецификации может меняться в довольно широком диапазоне, а мощность принимаемого сигнала — плавать в зависимости от таких факторов как расстояние между антенной и катушкой, питание ASIC и выбранные параметры его компонентов, задача становится совсем уж невыполнимой. Поэтому
После фильтрации всё уже не так плохо
Оцифровываем сигнал
Конечно, прежде чем работать с сигналом в цифровой области, необходимо собственно его сначала оцифровать. Здесь появляется множество вариантов исполнения АЦП, большинство из которых не подходят по тем или иным причинам.
Во-первых, в Zynq (как и в Artix, и в UltraScale, и в некоторых других FPGA от Xilinx; у конкурентов также имеются подобные коммерческие решения) уже есть многоканальные встроенные АЦП. Только вот частота семплирования у них зачастую не высокая: в выбранном чипе это всего лишь 1 MSPS, то есть полоса в 500 кГц, что для нашего обрабатываемого сигнала фактически граничное значение. А с учётом более широкого диапазона настройки частоты поднесущей и вовсе недостаточное.
Далее идёт широкий спектр компонентных АЦП. Тут на любую частоту и разрешение подобрать подходящее устройство уже несложно. Но подобное решение увеличивает стоимость тестовой платы и сборки, плюс увеличивается либо сложность разработки цифрового описания (если это АЦП с SPI- или I2C-интерфейсом), либо сложность разработки платы (для трассировки параллельного интерфейса). Да и количество цифровых входов Zynq ограничено — в проекте используется не просто Xilinx Zynq, а готовый модуль на его основе, устанавливаемый на плату-носитель.
Поэтому в качестве эффектного и, как оказалось в последствии, эффективного решения (а во многом просто ради ! НАУКИ!) с помощью компаратора, пассивного фильтра и D-триггера был выполнен однобитный дельта-сигма АЦП. Частота семплирования в данном случае совпадает с основной частотой работы цифровой логики — 100 МГц.
Наш АЦП. В роли компаратора — ADCMP600. В роли D-триггера — D-триггер
Работать напрямую с таким сигналом, разумеется, не очень удобно, поэтому далее нужно увеличить разрядность, для чего можно частично пожертвовать частотой. Увеличением разрядности до приемлемых значений, децимацией и фильтрацией сигнала заниматься будет CIC-фильтр, он же — каскадный интегрально-гребенчатый фильтр Хогенауэра. Подробнее можно узнать например здесь, а вкратце — из однобитного сигнала (на самом деле — двухбитного, т.к. проще работать со знаковым представлением сигнала как +1/-1; при однобитном беззнаковом сигнале 0/+1 получаем смещение 0.5 и некоторые другие проблемы работы блоков, в частности фильтров) получаем 12-битный поток с частотой семплирования 12.5 МГц, пропущенный через ФНЧ с частотой среза около 600 кГц.
Выход ДСАЦП и результат работы CIC-фильтра; видна задержка работы ФНЧ — КИХ 80-ого порядка
От оцифрованного сигнала к цифровому сигналу
Таким образом, мы перешли в «цифровую плоскость» решения задачи. Ответный сигнал различим и при должных условиях достаточно красив, чтобы в нём визуально прочитать нужные нам данные. Однако до того момента, как это сможет автоматически выполнять цифровой демодулятор — еще достаточно далеко.
В первую очередь необходимо преобразовать оцифрованный сигнал в оригинальную однобитную последовательность модулированной поднесущей. Самый простой и прямолинейный вариант — обычный компаратор с нулём. В некоторых случаях (узкая полоса, резкие полосовые фильтры, высокое соотношение сигнал-шум) такое решение вполне работоспособно. В нашем случае одного взгляда на сигнал хватает, чтобы понять, почему компаратор не будет работать.
Один из примеров разбираемого сигнала; в данном случае частота поднесущей не кратна опорной частоте, так как схема работала от внутреннего осциллятора, поэтому присутствуют еще и интермодуляции
Во-первых, в аналоговом тракте присутствует разделительная ёмкость, и даже не одна. В том числе она используется для отсечения постоянной составляющей, что приводит к смещению сигнала к нулю. Однако такое смещение происходит недостаточно быстро относительно частоты поднесущей, из-за чего первые несколько периодов выпадают из последовательности. Увеличение же частоты среза получаемого пассивного фильтра высоких частот приводит к задавливанию полезного сигнала.
Во-вторых, шум и интермодуляции в сигнале не позволяют задать такое значение на компараторе, чтобы это удовлетворяло корректной работе втечение даже одной транзакции.
В-третьих, так как мы планируем в последствии еще и демодулировать поднесущую, простого «среза» наверняка будет недостаточно; в идеале хотелось бы иметь саму поднесущую в как можно более чётком виде.
Исходя из всего этого, получаем несколько критериев для будущего алгоритма: детектирование фронтов/пиков, динамическое слежение за «нулём», работа с изменяемой амплитудой, отфильтровывание шумов. Ну и разумеется всё это в реальном времени.
Значительное влияние на результат оказала данная статья из блога fail0verflow, в которой по сути разбирается алгоритм решения идентичной проблемы. В нашем случае было сделано несколько допущений и модификаций: мы работаем с синусоидальным сигналом, а не с импульсным, вследствие чего была изменена логика получения выходного значения на основе обнаруженных пиков. Также добавлена возможность динамического изменения некоторых константных в описанном алгоритме величин (минимальный размах сигнала, пороги различия пиков и т.п.) для исследования работы схемы и поиска оптимальных значений в зависимости от внешних параметров.
Оцифрованный сигнал после фильтра и соответствующий ему результат работы детектора
Окончательная демодуляция
Где-то на этом этапе садовые гномы из South Park уже получали прибыль; нам же еще остаётся по сути самое главное — демодулировать сигнал и получить из него полезные данные.
Если представить идеальный вариант входного сигнала и работы системы — отсутствие шумов и неточностей аналогового тракта, отсутствие преобразований данных, полностью синхронизированные между собой частотные домены, — имеем на входе меандр с известной частотой и 50% скважностью, а также изменение фазы меандра относительно модулирующих данных. Получить из такого сигнала данные достаточно просто — требуется лишь выполнить операцию исключающего «ИЛИ» (XOR) с меандром поднесущей без модуляции; при совпадении фаз получим «0», при противоположных фазах — «1». Остаётся только сформировать стробирующий сигнал появления нового бита — это будет, допустим, ниспадающий фронт немодулированного меандра. Готово!
Теперь возвращаемся в реальность. Внесение искажений в сигнал приведёт к фазовым ошибкам, и после XOR мы будем получать осечки в местах изменения значения одного из сигналов. До какого-то предела их можно игнорировать — например «забирать» результат между фронтами опорного сигнала там, где значение стабильно. Более стабильным вариантом будет фильтрация, в первом приближении — реализация счётчиков «единиц» и «нулей» XOR-выхода: чего насчитали больше втечение периода поднесущей, такой результат и выдаём.
Такой приём работал бы, если бы наш приёмник был синхронизирован с частотой тракта и частотой работы передатчика. Но даже с таким условием точность будет относительно невысокой и зависимой от частоты поднесущей, которая по умолчанию всего в 16 раз ниже основной частоты. В нашем же случае передатчик может формировать (и по умолчанию формирует) поднесущую относительно своего внутреннего генератора, частота которого с достаточной точностью неизвестна. Это приводит к тому, что сформировать опорную немодулированную поднесущую для XOR уже не получится просто зная делители — через какое-то количество периодов такой сигнал разойдётся по фазе более чем на 180° с принимаемым, и это количество периодов заведомо меньше чем длина пакета, т.е. обычный метод решения проблемы в UART (передискретизация на умноженной частоте и завершение декодирования всех данных — байта — до того, как накапливаемая ошибка превысит 50%) нас не устроит.
Требуется формировать опорный сигнал на основе входящего. Для этого мы замеряем несколько стартовых периодов входящего сигнала и формируем новый меандр с посчитанным количеством «нулей» и «единиц». Начальная часть пакета представляет собой т.н. пилотный сигнал — немодулированная поднесущая с последующим переключением фазы; всё это как раз предназначено для настройки приёмника на декодирование данных. В данном случае нам необходимо отбросить некоторую стартовую часть этого сигнала, ожидая стабилизации постоянной составляющей (вспоминаем предыдущую главу). Также на основе фазовой ошибки между входным сигналом и опорой можно выполнять коррекцию периода и формы опоры. Т.е. реализовывать обратную связь в схеме для фазочастотной подстройки.
Еще одной из модификаций относительно сказанного выше является формирование выходных значений. Вместо того, чтобы фильтровать результат исключающего «ИЛИ», принимаем начальную фазу за «0» и далее с помощью реализованных уже счётчиков детектируем изменение фазы входного сигнала. При нескольких периодах поднесущей на бит сделать это проще и точнее, как показала практика.
По итогу получаем приёмник, при условии хорошего согласования в RF-части стабильно принимающий короткие пакеты данных и достаточно стабильно — большие объёмы вплоть до максимума. Возникновение ошибок в демодуляции далее детектируется за счёт битов чётности и контрольной суммы на уровне протокола, и затем возможно данные запрашиваются повторно. Однако это уже другая история.
Успешно декодированный пакет в 7 бит: «1110110»