Что такое utmi usb
Общее описание: USB3300 высокоскоростной USB2.0 трансивер позволяет работать в индустриальном диапазоне температур. USB3300 использует интерфейс с малым количеством выводов (ULPI интерфейс). ULPI интерфейс сокращает количество выводов с 54 до 12, использую метод передачи сигналов внутри основной полосы и передачу байта состояния между физическим уровнем приемопередатчика и уровнем линка.
Физический уровень изначально разработан с ULPI интерфейсом. Такая структура окружения UTMI к ULPI позволяет получить плавный переход от ULPI к линк интерфейсу. В результате физический уровень трансивера имеет низкое время ожиданияя приема-передачи. ULPI интерфейс позволяет USB3300 использовать как хост или как равноправное устройство (OTG).
Позже планируется использовать USB3300 как устройство, к которому можно добавить хост или OTG без дополнительных выводов. ULPI интерфейс в комбинации с технологиями компании SNSC дает хорошую возможность добавить Hi-Speed USB в новую разработку. И наконец в USB3300 интегрированны DP и DM согласующие резисторы, что минимизирует количество внешних элементов.
Типовая схема подключения
ULPI интерфейс содержит 12 выводов. 8 двунаправленных выводов для передачи данных, 3 вывода контроля и один вывод clockout на 60 MHz. Использую все 12 выводов ULPI интерфейс поддерживает весь диапазон уровней UTMI+, как показано на рисунке 1.2. Это позволяет работать USB3300 как HS, FS перефирийные устройства и как HS, FS, LS хост. Также USB3300, как опция, полностью поддерживает OTG (One-The-Go) протокол.
One-The-Go позволяет функционировать USB3300 как хост или перефирийное устройство, управляемое софтом. Например мобильный телефон может соединяться с компьютером для обмена информацией как перефирийное устройство, или соединяться с принтером как хост для печатанья фотографий. И наконец OTG может действовать как устройство, соединенное с другим OTG для обмена информацией.
Все эти соединентия поддерживаются низкопрофильным мини-AB USB коннектором. Ко всему вышесказаному USB3300 позволяет минимизировать энергопотери, а как следствие увеличить продолжительность службы батарей в переносных устройствах.
Особенности ULPI интерфейса при взаимодействии с UTMI+ (рис. 1.2)
USB3300 высокоинтегрированное устройство на физическом уровне, включает в себя Hi-Speed USB2.0 PHY с ULPI интерфейсом. Ниже приведена общая блок-схема устройства. Более подробное описание архитектуры устройства и кажного функционального узла можно узнать из документации в формате PDF на сайте производителя.
Описание выводов: USB3300 выпускается в 32-pin QFN корпусе размером 5 x 5 x 0.9 mm. Ниже приведена схема расположения выводов.
Динамические характеристики аналоговых I/O выводов: Время нарастания FS сигнала (TFSR) и время спада FS сигнала (TFFF) при емкости нагрузки 50 pF находится в пределах от 4 до 20 ns. Минимальное время нарастания (THSR) и спада (THSF) HS сигнала составляет 500 ps. Форма сигнала при этом должна соответствовать USB2.0 спецификации.
OTG электрические характеристики | ||||||
Параметр | Символ | Условия | min | typ | max | Ед. изм. |
Граничное значение SessEnd | VSessEnd | 0.2 | 0.5 | 0.8 | V | |
Граничное значение SessVld | VSessVld | 0.8 | 1.4 | 2.0 | V | |
Граничное значение VBUSVld | VVBUSVld | 4.4 | 4.58 | 4.75 | V | |
VBUS Pull-up | RVBUSPu | VBUS to VDD3.3 | 281 | 340 | Ù | |
VBUS Pull-down | RVBUSPd | VBUS to GND | 656 | 850 | Ù | |
VBUS импеданс | RVBUS | VBUS to GND | 40 | 75 | 100 | kΩ |
ID pull-up сопротивление | RIDpull-up | IDpull-up=1 | 80 | 100 | 120 | kΩ |
RID | IDpull-up=0 | 1 | MΩ | |||
STP pull-up сопротивление | RSTP | Защита интерфейса выключена | 240 | 330 | 600 | kΩ |
Электрические характеристики регулятора напряжения: Напряжение на выводе VDDA1.8 во включенном состоянии (SuspendM=1) имеет типовое значение 1.8 V (1.6V - минимальное, 2.0V - максимальное). В выключенном сосоянии (SuspendM=0) VDDA1.8=0. Номинальное напряжение VDD1.8 составляет 1.8 V (1.6V и 2.0V соответственно минимальное и максимальное)
Архитектура устройства (ниже представлена упрощенная блок-схема). Подробное описание каждого функционального узла доступно на на сайте производителя
Руководство по применению. На рисунке ниже приведена стандартная схема применения.
ULPI это интерфейс для организации высокоскоростных IP-систем USB 2.0. Он определяет взаимодействие между контроллерами USB и микросхемами PHY или трансиверами, которые соединяются с реальной физической шиной USB. Аббревиатура ULPI означает UTMI+ low pin interface (UTMI+ с уменьшенным количеством выводов), и он был специально разработан для уменьшения количества выводов корпусов микросхем high-speed USB PHY. Уменьшение количества выводов минимизирует стоимость и место на плате, необходимое для установки и разводки чипа PCB, и уменьшает количество выводов, назначенных для связи с высокоуровневым контроллером USB. В результате этих свойств ULPI быстро стал новым стандартом интерфейса среди разработчиков систем и чипов (SMSC, NXP Semiconductors, Mentor Graphics).
Стандарты UTMI+ и ULPI. ULPI это расширение стандарта UTMI+ PHY. Оба стандарта определяют интерфейсы между высокоуровневым контроллером USB и микросхемами PHY для организации физического соединения с шиной USB, однако ULPI специально предназначен для микросхем PHY. Стандарт UTMI (расшифровывается как USB transceiver macrocell interface) был разработан компанией Intel® для высокоскоростных периферийных устройств (USB v2.0). UTMI позволяет периферийным устройствам подключаться к компьютеру либо на высокой high-speed, либо на полной (full-speed) скорости (для совместимости со старыми PC). Стандарт UTMI+ это расширение оригинального UTMI, которое поддерживает контроллеры OTG и контроллеры хоста на всех скоростях.
Наподобие UTMI+ для встроенного в чип PHY, стандарт ULPI предоставляет основную выгоду для разработчиков решений USB при использовании отдельных микросхем PHY. Оба стандарта поддерживаются организацией ULPI Working Group. Хотя ULPI это не полностью открытая спецификация, любой может свободно присоединиться и стать пользователем ULPI. Зарегистрируйтесь на сайте ULPI Working Group, чтобы получить бесплатную копию документации стандарта.
В прошлой паре статей мы рассмотрели пример «прошивки» для комплекса Redd, делающей его ПЛИСовую часть логическим анализатором общего применения. Дальше у меня было желание сделать следующий шаг и превратить его в шинный USB-анализатор. Дело в том, что фирменные анализаторы такого вида очень дорогие, а мне необходимо провести проверку, почему одна и та же USB поделка, если её подключить к машине, работает, а если включить машину, когда всё уже воткнуто в разъём, не работает. То есть, программные анализаторы тут не справятся. По мере написания я как-то увлёкся и написал блок из пяти статей. Теперь можно сказать, что в них показан не только сам анализатор, но и типовой процесс его создания в режиме «на скорую руку». В статье будет показано, как сделать такой анализатор не только на базе Redd, но и на готовых макетных платах, которые можно приобрести на Ali Express.
Пожалуй, я сегодня даже нарушу традицию и буду отлаживать проект не на комплексе Redd, а на обычной макетке. Во-первых, я отдаю себе отчёт, что у подавляющего большинства читателей нет доступа к такому комплексу, но есть доступ к Ali Express. Ну, а во-вторых, мне просто лень городить огород с подключением пары из USB-устройства и хоста, а также бороться с возникающими наводками.
В далёком 2017-м году я искал в сети готовые решения и нашёл вот такую замечательную вещь, вернее, её предка. Сейчас у них всё уже на специализированной плате, а тогда везде были фотографии простой макетки от Xilinx, к которой была подключена плата от WaveShare (узнать про неё можно тут). Давайте посмотрим на фотографию этой платы.
Что должна уметь делать голова
В случае с логическим анализатором всё было легко и просто. Есть данные. Мы к ним подключились и начали паковать, да слать в шину AVALON_ST. Здесь всё сложнее. Спецификацию ULPI можно найти тут . Девяносто три листа занудного текста. Лично меня такое вгоняет в уныние. Чуть более простым выглядит описание на микросхему USB3300, которая стоит в плате от WaveShare. Его можно взять тут . Хотя я всё равно копил смелость с того самого декабря 2017-го года, иногда почитывая документ и сразу закрывая его, как чувствовал приближение депрессии.
Из описания ясно, что у ULPI имеется набор регистров, которые следует заполнить перед началом работы. В первую очередь, это связано с подтягивающими и терминирующими резисторами. Вот рисунок, поясняющий суть:
В зависимости от роли (хост или устройство), а также выбранной скорости, надо включать разные резисторы. Но мы не являемся ни хостом, ни устройством! Мы должны все резисторы отключить, чтобы не мешать основным устройствам на шине! Это делается через запись в регистры.
Ну, и скорость. Надо выбрать рабочую скорость. Для этого также надо произвести запись в регистры.
Когда мы всё настроили, можно приступать к ловле данных. Но в названии ULPI буквы «LP» означают «Low Pins». И вот это самое уменьшение числа ножек привело к такому зубодробительному протоколу, что только держись! Давайте рассмотрим протокол подробнее.
Временная диаграмма записи в регистр ULPI
Для записи в регистр используется следующая времянка (я специально перешёл на жаргон, так как чувствую, что меня клонит в сторону ГОСТ 2.105, а это – скучно, так что отойду от него):
Перво-наперво, мы должны дождаться состояние DIR=0. На такте T0, мы должны выставить на шину данных константу TXD CMD. Что это значит? Сразу и не разберёшь, но если чуть покопаться по документам, выясняется, что нужное значение можно найти тут:
То есть, в старшие биты данных следует положить значение «10» (для всего байта получится маска 0x80), а в младшие — номер регистра.
Далее, следует дождаться взлёта сигнала NXT. Этим сигналом микросхема подтверждает, что услышала нас. На рисунке выше мы дождались его на такте T2 и выставили на следующем такте (T3) данные. На такте T4 ULPI примет данные и снимет NXT. А мы отметим конец цикла обмена единицей в STP. На также T5 данные будут защёлкнуты во внутренний регистр. Процесс завершён. Вот такая расплата за малое число выводов. Но нам надо будет записать данные только при старте, так что помучиться с разработкой, конечно, придётся, но на работу особо всё это влиять не будет.
Порт состояния (+0x0C)
Доступен только на чтение.
Бит 0 – WRITE_BUSY. Если равен единице – идёт процесс записи в регистр ULPI.
Бит 1 – READ_BUSY. Если равен единице – идёт процесс чтения из регистра ULPI.
Остальные биты зарезервированы.
Ⅲ USB-передача
1 формат передачи USB
Передача данных по шине USB основана на пакетах в качестве базового блока, и для передачи данных необходимо объединять различные пакеты в транзакции.
Протокол USB определяет четыре типа передачи: массовая передача, синхронная передача, передача с прерыванием и передача управления. Среди них пакетная передача, синхронная передача и передача с прерыванием представляют собой одну транзакцию при каждой передаче данных. Управляющая передача включает в себя три процесса. Процесс установления и процесс состояния представляют собой одну транзакцию, и процесс данных может включать в себя несколько транзакций.
2 пакета
Пакет делится на разные домены, а домены, содержащиеся в разных типах пакетов, различны. Но он должен начинаться с домена синхронизации SYNC, за которым следует PID идентификатора пакета, и, наконец, заканчивать пакет концом EOP пакета.
PID домен
PID используется для определения типа пакета. Он имеет всего 8 бит и использует только 4 бита (PID0PID3), остальные 4 бита являются PID0Отрицание PID3 используется для проверки PID.
PID определяет четыре типа пакетов: пакет токенов, пакет данных, пакет рукопожатия и специальный пакет. Подобные пакеты делятся на четыре конкретных пакета.
Адресный домен
Адрес занимает всего 11 битов, из которых нижние 7 битов являются адресом устройства, а верхние 4 бита являются адресом конечной точки.
Поле номера кадра
Номер кадра занимает 11 битов. Каждый раз, когда хост отправляет кадр, номер кадра увеличивается на 1. Когда номер кадра достигает 0x7FF, он сбрасывается в ноль и возобновляет счет.
Поле данных
В зависимости от типа передачи длина данных поля данных варьируется от 0 до 1024 байтов.
A. Пакет с токенами
Существует четыре типа пакетов токенов:
- OUT: уведомить устройство о выводе пакета данных
- IN: уведомить устройство о возврате пакета данных
- SETUP: используется только при передаче управления. Он также информирует устройство о том, что пакет данных должен быть выведен. Отличие от маркера OUT состоит в том, что используется только пакет данных DATA0, и он может быть отправлен только на конечную точку управления устройства.
- SOF: отправляется в форме широковещательной передачи в начале каждого кадра. Для полноскоростных USB-устройств хост генерирует кадр каждые 1 мсек. USB-хост подсчитывает текущий номер кадра и отправляет номер кадра через пакет SOF в начале каждого кадра.
Пакет токена OUT / IN / SETUP не имеет поля номера кадра и поля данных.
Пакет токенов SOF не имеет поля адреса и поля данных.
B. Пакет данных
Пакет данных не имеет поля адреса и поля номера кадра. Максимальная длина пакета данных зависит от типа передачи.
C. рукопожатие пакет
Существует четыре варианта пакета рукопожатия:
- ACK: передача завершена правильно
- NAK: устройство временно не готово к приему данных или не готово к отправке данных
- STALL: устройство не может быть использовано для передачи
- NYET / ERR: используется только для высокоскоростной передачи, устройство не готово или ошибка
Пакет рукопожатия имеет только поле PID.
3 транзакции
Транзакции можно разделить на три категории
- Настройка транзакции: хост используется для отправки управляющих команд на устройство
- Транзакция ввода данных: используется хостом для чтения данных с устройства
- Транзакция вывода данных: используется хостом для отправки данных на устройство
Состав транзакции: пакет токена + пакет данных + дополнительный пакет рукопожатия
4 передачи
Протокол USB определяет четыре типа передачи: передача управления, массовая передача, изохронная передача и передача прерывания.
Тип передачи | Характеристики | Сценарий приложения |
---|---|---|
Передача управления | Непериодический, взрыв | Передача команд и статуса |
Массовая передача | Непериодический, взрыв | Данные могут занимать любую полосу пропускания и выдерживать задержку |
Синхронная передача | периодический | Непрерывная передача, передача информации о времени и сохранение информации о метках времени в данных |
Синхронная передача | Периодическая, низкая частота | Разрешить отложенное общение |
А. Массовая передача
На рисунке поле представляет пакет, серый пакет представляет пакет, отправленный хостом, а белый пакет представляет пакет, отправленный устройством. Массовая передача является надежной передачей и требует пакета квитирования для указания результата передачи. Если объем данных относительно велик, для завершения передачи всех данных будет использоваться передача нескольких пакетных транзакций.Во время передачи PID пакета данных переворачивается способом DATA0-DATA1-DATA0 -. чтобы обеспечить синхронизацию между отправителем и получателем.
Пакетный перевод (Transfer) состоит из нескольких переносов пакетных транзакций (Transaction).
Б. Прерывание передачи Прерывание передачи
Процесс передачи прерывания такой же, как и пакетной передачи, за исключением того, что он не поддерживает PING. Разница между ними заключается только в том, что конечные точки, в которых происходит передача транзакции, различны, максимальная поддерживаемая длина пакета различна, приоритет различен и другие вещи, которые прозрачны для пользователя.
Упомянутое здесь прерывание не совпадает с прерыванием на аппаратном уровне, это не запрос на прерывание, отправленный устройством, но хост гарантирует, что передача будет запланирована в течение определенного интервала времени.
Когда хост планирует задачу передачи прерывания, он инициирует передачу прерывания в соответствии с интервалом запроса, указанным в соответствующем дескрипторе конечной точки прерывания. Прерывание передачи имеет более высокий приоритет, уступая только синхронной передаче.
C. изохронный перевод
Синхронная передача является ненадежной передачей, поэтому она не имеет пакета квитирования и не поддерживает инверсию PID. Когда хост планирует передачу транзакции, синхронная передача имеет наивысший приоритет.
D. передача управления
Управляющая передача делится на три (или две) фазы: настройка, данные (может не быть) и состояние. Каждый этап состоит из одного или нескольких переводов транзакций.
-
Этап настройки процесса настройки
Используйте транзакцию сборки следующим образом:
В процессе перечисления устройств получение различных дескрипторов, установка адреса и конфигурация конфигурации все осуществляются посредством передачи управления.
Интерфейс шины USB
1 стандарт USB интерфейса
Стандартный разъем USB имеет тип A, тип B и тип Mini B, каждый тип разделен на вилку и розетку.
Стандартный интерфейс USB
USB использует режим дифференциальной передачи, две линии передачи данных D + и D-.
- J государство и K государство
На низкой скорости: D + это «0», D- это «1», это состояние «J», состояние «K» противоположно;
На полной скорости: D + это «1», D- это «0», это состояние «J», состояние «K» противоположно;
Высокая скорость равна полной скорости. - Статус SE0
D + это "0", D- это "0" - Статус IDLE
Состояние холостого хода на низкой скорости является состоянием "K";
Состояние холостого хода на полной скорости - состояние "J";
Состояние холостого хода на высокой скорости - состояние «SE0».
Для режима полной скорости есть несколько важных сигналов:
- Сбросить сигнал
Хост отправит сигнал сброса перед установкой связи с устройством для настройки устройства в ненастроенное состояние по умолчанию. То есть состояние SE0 сохраняется в течение 10 мс. - Возобновить сигнал
20 мс К состояние + EOP низкой скорости - Сигнал приостановки
J состояние более 3 мс - Сигнал СОП
Переключиться из состояния IDLE в состояние K - Сигнал EOP
Сигнал SE0 длится 2 бита, затем следует состояние J в течение 1 бита - Сигнал SYNC
3 повторных переключения состояния K и J, затем состояние K в течение 2 бит
3 USB кодирование и декодирование данных и вставка битов
Битовая вставка должна гарантировать, что в переданной последовательности данных достаточно изменений уровня. Введите данные при заполнении объектов, то есть сначала заполните, а затем закодируйте. Для каждых 6 последовательных «1» в потоке данных, один «0» должен быть вставлен.
Приемник декодирует поток кода NRZI, затем распознает биты вставки и отбрасывает их.
4 Топология USB
USB - это система со структурой «ведущий-ведомый», узел называется «Хост», а ведомый - «Устройство». Устройство включает в себя функцию USB и USB HUB.
Шина USB основана на иерархической звездной топологии с центром в качестве концентратора, соединяющего окружающие устройства. К шине можно подключить максимум 127 устройств. Максимальное количество концентраторов в серии - 5.
5 USB логических компонентов
Руководство программиста
Порт управления ULPI (+8)
На чтение всегда равен нулю. На запись назначение битов следующее:
Бит 0 – При записи единичного значения, инициирует процесс чтения регистра ULPI, адрес которого задан в порту адреса регистра ULPI.
Бит 31 – При записи единицы подаёт сигнал RESET на микросхему ULPI.
Остальные биты зарезервированы.
Порт данных регистра ULPI (+4)
При записи в данный порт: автоматически начинается процесс записи в регистр ULPI, адрес которого был задан в порту адреса регистра. Запрещается писать в данный порт, пока не завершился процесс предыдущей записи.
При чтении: из данного порта будет возвращено значение, полученное в результате последней операции чтения регистра ULPI.
Заключение
Мы познакомились с методикой физической организации головы USB-анализатора, спроектировали базовый автомат для работы с микросхемой ULPI и реализовали черновой SystemVerilog-модуль этой головы. В последующих статьях мы рассмотрим процесс моделирования, проведём моделирование этого модуля, а затем проведём практические опыты с ним, по результатам которых начисто доработаем код. То есть, до конца нам предстоит ещё минимум четыре статьи.
Иллюстрированная проекция модели сетевого взаимодействия OSI на универсальную последовательную шину.
Три «замечательных» уровня стека USB
Меня не устроил вид стека USB, который можно встретить чаще всего на просторах сети:
Уровень шины, логический, функциональный… Это, конечно, замечательные абстракции, но они скорее для тех, кто собирается делать драйвер или прикладной софт для хоста. На стороне же микроконтроллера я ожидаю шаблонный конечный автомат, в узлы которого мы обычно встраиваем свой полезный код, и он сперва будет по всем законам жанра глючить. Или же глючить будет софт на хосте. Или драйвер. В любом случае кто-то будет глючить. В библиотеках МК тоже с наскока не разобраться. И вот я смотрю на трафик по шине USB анализатором, где происходящие события на незнакомом языке с тремя замечательными уровнями вообще не вяжутся. Интересно, это у меня от гриппозной лихорадки в голове такой диссонанс?
Если у читателя бывали сходные ощущения, предлагаю альтернативное, явившееся мне неожиданно ясно в перегретом мозгу видение стека USB, по мотивам любимой 7-уровневой модели OSI. Я ограничился пятью уровнями:
Я не хочу сказать, что весь софт и библиотеки уже сделаны или должны проектироваться, исходя из этой модели. Из инженерных соображений код c уровнями будет сильно перемешан. Но я хочу помочь тем, кто начинает своё знакомство с шиной USB, кто хочет понять протоколы обмена устройств и терминологию предметной области, подобраться поближе к готовым примерам, библиотекам и лучше ориентироваться в них. Эта модель не для загрузки в МК, но в ваши блестящие умы, дорогие друзья. А ваши золотые руки потом всё сами сделают, я не сомневаюсь:)
Итак, поехали, поправляйте, если увидите косяки. Это draft-версия, и если уже такое где-то было нарисовано, прошу простить, я не нашёл и потому скрутил сам. Думаю, картинка никуда не убежит, а я пока объясню почтенной публике, зачем вообще взялся за эту публикацию.
Свой первый баг из чужого кода я вытряхнул в конце девяностых, будучи студентом на подработках. Это был pppd под FreeBSD, который мы тогда прикрутили на модемный пул. Мотороловские модемы залипали в отбое, дозвониться никто не мог, линия пропадала зазря, и единственный оставшийся способ через PPP keep-alive почему-то глючил. Вот тогда я и выяснил, что pppd зачем-то ждёт шесть ответных байтов LCP вместо положенных четырёх. Почувствовал я себя тогда эдаким лихим жукотрясом из девяностых:-) При чём тут PPP? Просто он на USB похож: пакетный и двухточечный. Правда, в отличие от USB 2.0, полнодуплексный.
Как пройти в библиотеку?
Получив на GitHub любезно выложенный автором проект RHIDDemo для Em::Blocks, я начал портировать его в Keil (мой отладчик CoLink на базе FTDI; кто-нибудь, подскажите плагин от Coocox для Em::Blocks). Но никак не мог понять: где, чёрт возьми, автор раздобыл SPL 3.6.1 выпуска 2012г, если на сайте выложен 3.5.0 от 2011г? Я прошёл довольно скучный квест, который к моему удивлению привёл… прямо на готовый проект Custom HID для Keil в составе библиотеки USB FS 4.0.0. Лежит у всех на виду, как мышь под веником. Ну и ладно. Зато я раскурил, наконец, релизы STMicroelectronics, нашёл описание библиотеки USB FS STSW-STM32121 (UM0424) и пресёк попытки разработчика свести меня с ума. Вот скажите, это нормально подкладывать винтажный CMSIS 1.30 образца 2009г в набор SPL 3.5.0 выпуска 2011г, новый SPL 3.6.1 релиза 2012г прятать в USB-FS 4.0.0 релиза 2013г (подложив туда же и CMSIS 3.0.1 от 2012г), при том, что у них же выложена актуальная версия CMSIS 3.30 релиза 2014г? Кстати, в SPL 3.6.x для STM32F10X исправили пару багов с USART, касающихся сигналов о переполнении буфера. Спасибо, хоть release notes оставили…
HID vs SNMP
Итак, взявшись за STM32F103C8T6, я тоже решил слегка задвинуться по теме USB HID, уж больно хорошо абстракция USB HID укладывается в концепцию всяческих датчиков, сенсоров и прочих ШИМ-управляемых драйверов питания. Чем-то напомнило мне SNMP, только в сильно упрощённом виде: дескрипторы HID играют роль SNMP MIB. Когда устройство инициализируется хостом: «Привет, хост! Я кофеварка. У меня есть кнопка [старт], регуляторы [сливки], [сахар], датчики [остаток кофе], [остаток воды], [остаток сахара], [остаток сливок]. Подтягивай драйвера, дави на кнопку, кофейку попьём». Ничего не напоминает? Пример диалога по SNMP: «Ну, привет, управляющая станция с софтом за $100000. А я шасси коммутатора за $200000, и на мне сидят ещё 4 модуля по $100000 за штуку; в каждом ещё по 16 портов с неприличной скоростью, и всех функций тут просто не перечислить… спрашивай отдельно по каждому пункту; ах, да загрузка процессора такая-то, памяти столько-то…». И ещё на дюжину страниц в таком же духе.
Ещё одна замечательная схема
На просторах сети встретил ещё такую иллюстрацию (лежало в формате BMP, без шуток):
Сперва выглядит оптимистично. Наконец-то, стек в разобранном виде. Кадры, правда, обозначены неудачно: я бы нарисовал их вертикальными пунктирными линиями, а EOF — это просто пауза, реально данные не передаются. Но начинаем читать контекст и теряем понимаем истинный замысел автора (запутать нас):
Хост-контроллер интерфейса шины USB формирует кадры;
Кадры передаются последовательной передачей бит по методу NRZI.
каждый кадр состоит из наиболее приоритетных посылок, состав которых формирует драйвер хоста;
каждая передача состоит из одной или нескольких транзакций;
каждая транзакция состоит из пакетов;
каждый пакет состоит из идентификатора пакета, данных (если они есть) и контрольной суммы.
Вроде бы и нарисовано всё правильно, но по мере прочтения вопросов становится всё больше. Минимальная передаваемая структура данных по шине — это кадр или пакет? Вообще, это сверху вниз надо смотреть или наоборот? И что кодируется по методу NRZI — кадры, пакеты или просто весь битовый поток по шине? Из транзакций состоит посылка, передача, или, может быть, ценная бандероль какая?
Почему нельзя просто: хост группирует пакеты в транзакции и распределяет их по временным квантам, именуемым кадрами, чтобы давать приоритет критичным по времени данным (видео, аудио) исходя из текущей пропускной способности шины? Да, в USB есть нюансы с планированием передачи пакетов, я их пока не затрагиваю.
Моё видение стека USB
Хорошей документацией считаю упоминавшийся тут на хабре USB in a NutShell (ура, перевод), а также USB Made Simple. По ним я и собрал свою версию стека USB, нарисую её ещё раз.
Физический уровень
На физическом уровне используется набор электрических режимов дифференциальной пары проводников (вместе с землёй) для обозначения состояний, с помощью которых кодируется битовый поток по методу NRZI со вставкой битов (bit stuffing): здесь после шести идущих подряд «1» (ну захотелось передать, скажем, 0xffff) вставляется «0», чтобы приёмник подолгу не залипал в одном состоянии; приёмник узнает вставленный «0» и как данные не засчитает, это довольно распространённый приём в кодировании для лучшей автоподстройки частот. Пара проводов вместе с землёй даёт возможность сформировать, как минимум, четыре статических состояния (они обозначаются J, K, SE0, SE1). В USB 2.0 SE1 не используется, а три оставшихся дополнительно разыгрываются в динамике (с часами и переходами) для передачи ещё нескольких управляющих символов (границы пакетов, сброс, подключение/отключение, энергосбережение/выход). Хорошие иллюстрации есть в USB Made Simple, Part 3 — Data Flow.
Т.е. в итоге передаются данные в виде ноликов и единичек, плюс всякие управляющие символы, чтобы можно было из всей этой электродинамической кухни готовить нормальные пакеты данных.
(дополнено по просьбе читателей)
Пакетный уровень
На пакетном уровне между хостом и устройством передаются безадресные пакеты (пара устройств на полудуплексной линии может обойтись и без адресации). Пакет состоит из маркера SYNC для синхронизации тактов приёмника, последовательности байт и символа EOP. Длина пакета переменная, но оговаривается через верхние уровни стека. Первый байт называется Packet Identifier (PID), имеет простой избыточный формат для помехоустойчивости и пригоден для скармливания автомату следующего уровня (для сборки транзакций из пакетов). Пакеты с начинкой (длиннее одного байта PID) снабжаются контрольной суммой (короткой CRC5 или длинной CRC16, в зависимости от типа пакета). Анализатор протоколов должен, как минимум, показывать нам пакеты.
Уровень транзакций
На следующем уровне из пакетов собираются транзакции. Транзакция — это малый набор пакетов (в Full Speed USB 1, 2 или 3), следующих строго друг за другом, которыми (в полудуплексном режиме) хост обменивается с оконечной точкой (endpoint), и только с одной. Очень важно, что транзакцию открывает только хост, это специфика USB (нам в прошивке МК меньше мороки). На уровне транзакций можно говорить о канале (pipe) между хостом и одной из оконечных точек устройства, но я намеренно избегаю термина «канальный уровень» (Data Link) из модели OSI. Анализатор протоколов должен хотя бы декодировать транзакции.
Уровень передач
Поверх транзакций расположим уровень передач (transfers). Их в USB используется четыре типа: контрольные с оконечной точкой №0 (control transfers), передачи с прерываниями (interrupt transfers), изохронные (isochronous transfers) и крупноблочные передачи (bulk transfers). Последние три являются вариантами потоковых каналов (stream pipe), про которые я ещё скажу несколько слов. Этот уровень тоже должен отобразить хороший анализатор протоколов.
Прикладной уровень
Венчает стек, как обычно, прикладной уровень. Здесь происходят: установка адреса устройству хостом, рассказ устройства о себе на языке дескрипторов, команды хоста на выбор конфигурации (контрольные передачи), обмен данными с HID-устройствами (в примерах пока нашёл передачу с прерываниями, хочу попробовать контрольную), печать на принтере и сканирование, доступ к накопителю USB (крупноблочные), общение через гарнитуры и веб-камеры (изохронные) и многие другие замечательные вещи.
Последний штрих
Смотрим на трафик по USB
Итак, транзакция всегда инициируется хостом в отношении одной выбранной оконечной точки на устройстве (помимо специальной точки с номером 0, их может быть ещё до 15 штук на одном устройстве, например, комбинированная клавиатура с мышью, термометром, флэшкой, кофеваркой и кнопкой вызова сантехника заказа пиццы).
В случае приёма хостом данных с устройства последнее не может само открыть транзакцию, но может только дождаться нужного момента и поучаствовать в ней. Хост открывает транзакцию устройству пакетом с PID = IN (группа Token) и гарантирует на нужное время свободу шины, устройство вбрасывает пакет из группы Data, в зависимости от типа транзакции хост может подтвердить успех третьим пакетом из группы Handshake (ACK, NAK, STALL, NYET), транзакция закрыта.
При отправке данных на устройство (PID = OUT, группа Token) хост открывает транзакцию, отправляет пакет с данными (Data), также в зависимости от режима может принять пакет Handshake с подтверждением успешности транзакции.
По окончании транзакции всё вернётся на круги своя, устройство снова будет ждать управляющих пакетов от хоста.
Режимы передачи USB в примерах STM32 USB FS
DISCLAIMER
Дальше будут упоминаться примеры из той самой библиотеки UM0424 для работы с Full Speed USB от STMicroelectronics, но они рассчитаны под их родные демоплаты. Берите пример с автора Raja, проявляйте инженерную смекалку в адаптации проектов под свою демоплату.
По софту всё понятно: это примеры не для промышленного использования, там могут быть баги, некоторые части (типа таблицы ссылок в примере Mass storage) защищены патентом, и вы не имеете прав их использовать в коммерческом проекте. Но это ещё ничего, китайцы ухитряются потом продавать на рынке USB-изделия, у которых даже библиотечные VID и PID не удосужились поменять.
По железу, как я понял, надо начинать с кварца. У меня челябинский PinBoard II с кварцем 12Мгц (все библиотеки заточены под 8МГц), я менял умножитель ФАПЧ с 9 на 6 (ссылка с разъяснениями), иначе МК разгонится до 108МГц вместо 72МГц, а USB на 72МГц вместо положенных 48МГц вообще не поедет. Можно ещё сбавить обороты МК до 48МГц, поменяв делитель шины USB с полутора до единицы. Использовать внутренний генератор МК HSI спецы не любят: частота может слегка уплыть от нагрева, последствия для USB предсказать затрудняюсь. Ну и не забываем о периферии, конечно. Без флэш-памяти SPI/SDIO из примера Mass storage можно сделать разве что аналог /dev/null, но его ведь хрен отформатируешь:-)
Передачи с прерываниями
Эта разновидность (interrupt transfer) предназначена для обмена небольшими транзакциями, сходными с контрольными. Нет, устройство не может прерывать хоста, оно ждёт опроса, их частота и размеры пакетов оговариваются заранее в дескрипторе устройства. Хорошо подходят для всевозможных пультов, датчиков, сенсоров, мышек, светодиодов и прочих HID-кофеварок. Канал с прерываниями каждой точки однонаправленный.
Примеры: Custom HID, Joystick mouse, Virtual COM port
Передачи изохронные
Передачи крупноблочные
Что осталось нераскрытым
Я не имею цель сделать ещё один учебник по USB, их и без меня хватает, и там хорошо описаны: электрическая часть, подробности протоколов, работа с концентраторами, дескрипторный язык и уровень абстракции HID, проблемы с уникальностью VID/PID, USB 3.0 и многие другие замечательные возможности шины USB, как полезные нам, так и не очень. Айтишникам особо рекомендую экскурсию на тёмную сторону с обзором вражеских девайсов (флэшка с замаскированной HID-клавиатурой, которая будет делать страшные вещи).
Ссылки
P.S.
Читая публикации на хабре, посвящённые в той или иной степени микроэлектронике, я разглядел две инженерных касты, назовём их условно: Промэлектронщики и Айтишники. Это своего рода инженерный Инь и Ян, в каждом из нас есть доля того и другого.
Промэлектронщики имеют блестящие знания и навыки по железу, паяют радиодетали толщиной с волос левой рукой с закрытыми глазами (причём потом это работает). Взглянув на электронную схему, почти физически начинают ощущать все её токи с потенциалами, работают также и с силовыми схемами, и с (большими, быстрыми, опасными) промышленными изделиями. Подход к программированию МК соответствующий: он просто должен выдать нужные логические уровни на нужные ножки в нужное время, не столь важно каким способом. Консервативны в технологиях (не влезай — работает), тяжёлую периферию МК не особо жалуют. При обсуждении объектно-ориентированного программирования, информационной безопасности, гигантских проектов в миллион строк кода и всяких навороченных графических интерфейсов скучнеют. Вместо пакетно-ориентированной шины USB предпочитают потоковый режим USART, усиленный либо привычным RS-232, либо более брутальным RS-485 (последовательная шина для промышленных применений, до 10Мбит/с на 15м, до 100кБит/с на 1200м, до 32 устройств).
Айтишники воспитаны на понимании операционных систем, сетевой инфраструктуры и сложных взаимодействий, элита хорошо подкована в информационной безопасности и разбирается во всяких незримых способах проникновения в чужую систему. Некоторые при этом очень любят котиков (ну как их можно не любить? я, правда, не держу, не развожу и не готовлю:-). Многие любят свободу информации, ругать корпорации/правительства и побеждать силы природы усилием мысли. Паталогически ленивы, но обожают новые технологии и закрученные инженерные ребусы с дорогими игрушками (желательно решаемые на уровне софта или, в крайнем случае, перемычек). Отношения с паяльником настороженные: не спрашивайте у айтишника, любит ли он паяльник, может неправильно понять; лучше спросите, любит ли он паять электронные схемы.
К чему я? Мы просто видим этот мир по-разному… Ведь ядро Linux кроили такие же ребята, из модулей на С и ассемблерных вставок для конкретных платформ, и без холиваров вроде обошлись. По-настоящему серьёзный проект я вижу как многоядерную систему, сочетающую современнейшие МК с тяжёлой периферией, но не исключаю связки с классическими моделями типа AVR: ими можно обвесить какие-нибудь критичные быстровращающиеся острия технического прогресса. Если код проверенный годами, то почему нет?
USB - это аббревиатура от универсальной последовательной шины. В версиях USB1.0 и USB1.1 поддерживаются только режим низкой скорости 1,5 Мбит / с и режим полной скорости 12 Мбит / с. В USB2.0 добавлено 480 Мбит / с. Скоростной режим.
Порт адреса регистра ULPI(+0)
В порт со смещением +0 следует помещать адрес регистра ULPI шины, с которым будет идти работа
Особенности реализации автомата со стороны AVALON_MM
При работе с шиной AVALON_MM, можно пойти двумя путями. Первый — создавать задержки доступа к шине. Мы этот механизм исследовали в одной из предыдущих статей, и я предупреждал, что он чреват проблемами. Второй путь — классика. Ввести регистр состояния. При начале транзакции взводить сигнал BSY, при её завершении — сбрасывать. И возложить ответственность за всё на логику мастера шины (процессор Nios II или мост JTAG). Каждый из вариантов имеет свои достоинства и свои недостатки. Раз мы уже делали варианты с задержками шины, давайте сегодня, для разнообразия, сделаем всё через регистр состояния.
Проектируем основной автомат
Первое, на что хотелось бы обратить внимание — мои любимые RS-триггеры. У нас есть два автомата. Первый обслуживает шину AVALON_MM, второй — интерфейс ULPI. Мы выяснили, что связь между ними идёт через пару флагов. В каждый флаг может писать только один процесс. Каждый автомат реализуется своим процессом. Как быть? С некоторых пор я просто стал добавлять RS-триггер. У нас два бита, значит их должны вырабатывать два RS-триггера. Вот они:
Один процесс взводит reg_served, второй — have_reg. А RS-триггер в своём собственном процессе на их основе формирует сигнал write_busy. Аналогично, из read_finished и reg_request формируется read_busy. Можно это делать и иначе, но на данном этапе творческого пути, мне нравится именно такой метод.
Как мы видели выше, одного такта достаточно, чтобы соответствующий RS-триггер установился в единицу. И с этого момента из регистра статуса начинает читаться установленный сигнал BSY:
Собственно, так непринуждённо мы познакомились с процессами, обслуживающими работу с шиной AVALON_MM.
Давайте я также напомню про принципы работы с шиной ulpi_data. Эта шина двунаправленная. Поэтому следует применять стандартный приём для работы с ней. Вот так объявлен соответствующий порт:
Читать из этой шины мы можем, а вот писать напрямую – нельзя. Вместо этого, мы заводим копию для записи
И подключаем эту копию к основной шине через такой мультиплексор:
Логику работы основного автомата я постарался по максимуму прокомментировать внутри Verilog кода. Как я и предполагал по ходу разработки графа переходов, при реальной реализации, логика в несколько изменилась. Часть состояний была выкинута. Тем не менее, сравнивая граф и исходный текст, надеюсь, вы поймёте всё, что там сделано. Поэтому я не буду рассказывать про этот автомат. Лучше приведу для справки полный текст модуля, актуальный на момент до модификации по результатам практических опытов.
Ⅱ USB-устройство
1 режим питания устройства USB
Существует два способа питания для USB-устройств:
1) Автономное оборудование: оборудование получает рабочее напряжение от внешнего источника питания.
2) Оборудование с питанием от шины: оборудование получает очки от VBUS (5v)
Для устройств с питанием от шины различают маломощные и мощные USB-устройства.
Для устройств с низким энергопотреблением максимальная потребляемая мощность не превышает 100 мА.
Для устройств большой мощности максимальное потребление энергии при перечислении не превышает 100 мА, а потребление энергии не превышает 500 мА после того, как перечисление завершает настройку.
Оборудование находится в счетном проекте, черезДескриптор конфигурацииСообщить хосту о режиме питания и потребляемой мощности.
2 механизм обнаружения вставки USB-устройства
Когда никакое устройство не подключено к хосту, D + и D- хоста находятся на низком уровне (состояние SE0) .Если состояние SE0 длится в течение некоторого времени, хост считает его отключенным.
Когда устройство подключено к хосту, хост обнаруживает, что уровень определенной линии данных повышается на некоторый период времени, и считается, что устройство подключено. Хост должен немедленно проверить состояние шины, чтобы определить скорость устройства перед его перезагрузкой.
3 Состояние устройства USB
Устройство USB имеет шесть состояний: подключение, питание, инициализация, распределение адресов, конфигурация и приостановка. Схема перехода состояний следующая.
4 Процесс перечисления USB-устройств
В соответствии со статусом устройства USB, хост будет выполнять следующие действия на устройстве USB:
В процессе перечисления используется передача управления.
- Сбросить фазу
После того, как USB-хост обнаружит, что USB-устройство вставлено, оно перезагрузит устройство. Адрес устройства USB после сброса шины равен 0, так что хост может связываться с вновь вставленными устройствами через адрес 0. Хост USB отправляет стандартный запрос на получение дескриптора устройства в конечную точку 0 устройства, адрес которого равен 0. Устройство вернет дескриптор устройства хосту. После того, как хост успешно получит пакет данных, он вернет пакет подтверждения устройству, тем самым перейдя к следующему этапу распределения адресов. - Этап распределения адресов
Хост снова сбрасывает устройство и переходит к этапу распределения адресов. Хост отправляет запрос на установку адреса в конечную точку 0 устройства с адресом 0, и адрес нового устройства включается в пакет данных процесса установления. Конкретный адрес управляется хостом, и хост назначит уникальный адрес вновь подключенному устройству. После того, как устройство получит этот процесс установления, оно войдет в процесс состояния. Устройство ожидает возвращения состояния запроса хоста. После получения запроса состояния устройство возвращает пакет состояния 0 длины. Если хост подтверждает, что пакет состояния был принят правильно, он отправит ответный пакет ACK на устройство. После того, как устройство получит этот ACK, он активирует новый адрес устройства. Таким образом, устройству присваивается уникальный адрес устройства. - Получить этап дескриптора
Дескриптор получения может быть визуально выражен следующим образом:
Device:12 01 0100. Device Descriptor
Ведущий: Сколько у вас функций?
Device:09 02 09. Configuration Descriptor
Host: Сколько интерфейсов имеет каждая функция?
Device:09 04 00. Interface Descriptor
Host: Какие конечные точки используются для каждого интерфейса?
Device:06 05 82. Endpoint Descriptor
Ведущий: Хорошо, я знаю, кто вы, давайте начнем передачу оборудования!
Device:OK,Read Go!
5 USB дескриптор
Существует 11 типов дескрипторов USB:
Первый байт каждого дескриптора описывает количество байтов, содержащихся в дескрипторе, а второй раздел описывает тип описания.
Структурная схема головы
Как видно из описания выше, голова должна быть подключена сразу к двум шинам: AVALON_MM для доступа к регистрам и AVALON_ST для выдачи данных на сохранение в ОЗУ. Главное в голове — это мозг. И вот им должен стать конечный автомат, который будет формировать временные диаграммы, рассмотренные нами ранее.
Начнём его разработку с функции приёма данных. Здесь следует учитывать, что мы никак не можем повлиять на поток из шины ULPI. Данные оттуда если начали идти, то будут идти. Им всё равно, есть готовность у шины AVALON_ST или её нет. Поэтому мы просто будем игнорировать неготовность шины. В реальный анализатор можно будет добавить индикацию аварии в случае выдачи данных без готовности. В рамках статьи всё должно быть просто, поэтому просто запомним это на будущее. А обеспечивать наличие готовности шины, как в логическом анализаторе, нам будет внешний блок FIFO. Итого, граф переходов автомата для приёма потока данных получается таким:
Взлетел DIR – начали приём. Один такт повисели в wait1, затем – принимаем, пока DIR равен единице. Упал в ноль – через такт (правда, не факт, что он нужен, но пока заложим состояние wait2) вернулись в idle.
Пока всё просто. Не забываем, что в шину AVALON_ST должны уходить не только линии D0_D7, но и линия NXT, так как она определяет, что сейчас передаётся: команда или данные.
Ну, и осталось добавить ветку чтения регистра ULPI. Здесь же — всё наоборот. Автомат, обслуживающий шину, шлёт нам запрос и ждёт нашего ответа. Когда данные получены, он сможет обработать их. А будет он работать с приостановкой шины или по опросу, это уже проблемы того автомата. Конкретно сегодня я решил работать по опросу. Запросили данные – появился BSY. Как BSY пропал – можно принимать считанные данные. Итого, граф принимает вид:
Возможно, по ходу разработки будут какие-то коррективы, но пока — будем придерживаться этого графа. В конце концов, это же не отчёт, а инструкция по методике разработки. А методика такова, что сначала надо нарисовать граф переходов, а затем – делать логику, согласно этому рисунку с поправкой на всплывающие подробности.
Протокол ULPI
Протокол ULPI несколько непривычен для простого человека. Но если посидеть с документом и помедитировать, то начинают проявляться некоторые более-менее понятные черты. Становится ясно, что разработчики приложили все усилия, чтобы действительно уменьшить число используемых контактов.
Я не буду здесь перепечатывать полную документацию. Ограничимся самыми важными вещами. Наиважнейшая из них — направление сигналов. Его невозможно запомнить, лучше каждый раз смотреть на рисунок:
ULPI LINK — это наша ПЛИС.
На каком железе можно работать
Ответ на вопрос из заголовка прост: на любом, где есть ПЛИС и внешняя память. Разумеется, в этом цикле мы рассматриавем только ПЛИС Altera (Intel). Правда, имейте в виду, что данные из микросхемы ULPI (именно она стоит на той платочке) идут на частоте 60 МГц. Длинные провода тут неприемлемы. Ещё важно подключать линию CLK к входу ПЛИС из группы GCK, иначе всё будет то работать, то сбоить. Лучше не рисковать. Программно пробрасывать не советую. Я пробовал. Кончилось всё проводом к ноге из группы GCK.
Для сегодняшних опытов по моей просьбе знакомый спаял мне вот такую систему:
Микромодуль с ПЛИС и SDRAM (ищите его на АЛИ экспресс по фразе FPGA AC608) и та самая плата ULPI от WaveShare. Вот так выглядит модуль на фотографиях от одного из продавцов. Просто мне лень его отвинчивать от корпуса:
Кстати, вентиляционные отверстия, как на фото моего корпуса, делаются очень интересно. На модели рисуем сплошной слой, а в слайсере ставим заполнение, скажем, 40% и говорим, что снизу и сверху надо сделать ноль сплошных слоёв. В итоге, 3D принтер сам рисует эту вентиляцию. Очень удобно.
В общем, подход к поиску железа понятен. Теперь начинаем проектировать анализатор. Вернее, сам анализатор мы уже сделали в прошлых двух статьях (тут работали с железом, а тут — с доступом к нему), сейчас же просто спроектируем проблемно-ориентированную голову, которая ловит данные, приходящие из микросхемы ULPI.
Временная диаграмма чтения из регистра ULPI
Честно говоря, для практических задач чтение регистров не так и важно, но давайте рассмотрим и его. Чтение будет полезно хотя бы для того, чтобы убедиться, что мы верно реализовали запись.
Мы видим, что перед нами гремучая смесь из предыдущих двух времянок. Адрес мы задаём так, как это делали для записи в регистр, а данные забираем по правилам чтения данных.
Ну, что? Приступаем к проектированию автомата, который будет нам всё это формировать?
Временная диаграмма приёма данных
В состоянии покоя мы должны выдавать на шину данных константу 0x00, что соответствует команде IDLE. Если из шины USB приходят данные, протокол обмена будет выглядеть так:
Цикл начнётся с того, что в единицу взлетит сигнал DIR. Сначала, он там будет находиться один такт, чтобы система успела переключить направление шины данных. Дальше — начинаются чудеса экономии. Видите имя сигнала NXT? Это он при передаче от нас значит NEXT. А здесь — это совсем другой сигнал. Когда DIR равен единице, NXT я бы назвал C/D. Низкий уровень — перед нами команда. Высокий — данные.
То есть, мы должны фиксировать 9 бит (шину DATA и сигнал NXT) либо всегда при высоком DIR (затем программно отфильтровывая первый такт), либо начиная со второго такта после взлёта DIR. Если линия DIR упала в ноль — переключаем шину данных на запись и снова начинаем вещать команду IDLE.
С приёмом данных — понятно. Теперь разбираем работу с регистрами.
Читайте также: