2-wire JTAG (он же двухпроводной JTAG, он же CompactJTAG, он же cJTAG) – это новомодный интерфейс, являющийся частью стандарта IEEE 1149.7-2009. Он обеспечивает ту же и даже большую функциональность, что и обычный JTAG (IEEE 1149.1), но использует всего два сигнала вместо четырех.
К сожалению, ни в русскоязычном, ни в англоязычном сегментах Интернета нет никакой информации об этом стандарте, кроме нескольких статей, написанных маркетологами. Тем не менее некоторое время назад мне по долгу службы пришлось с этим стандартом разобраться, и теперь у вас есть уникальный шанс ознакомится с результатами моих изысканий.
Я предполагаю, что вы знакомы с обычным JTAG-ом. Освежить память можно здесь: habrahabr.ru/post/190012/
Часто считают, что IEEE 1149.7 и 2-wire JTAG – это синонимы. На самом деле это не так, потому что IEEE 1149.7 включает в себя режимы, использующие как два, так и четыре сигнала. Так что 2-wire JTAG – это подмножество IEEE 1149.7.
Официально IEEE 1149.7 называется IEEE Standard for Reduced-Pin and Enhanced-Functionality. Несмотря на то, что помимо двухпроводности IEEE 1149.7 предлагает еще стопицот новых фич, по моему опыту именно возможность сэкономить две ножки на корпусе микросхемы привлекает разработчиков больше всего.
К сожалению, создатели IEEE 1149.7 употребляли тяжелые наркотики безо всякой меры, поэтому родили без преуменьшения чудовищный документ, который твердо занял первое место в моем личном хит-параде самых уродски написанных стандартов. Помимо того, что он вырос почти в десять раз (1037 страниц против 139 у старого JTAG-а), он еще и написан так, что абсолютно невозможно ничего понять.
Тем не менее, если вы однажды окажетесь лицом к лицу с микросхемой, у которой вместо четырех JTAG-ног (TCK, TMS, TDI и TDO) всего две (TCKC и TMSC – “C” на конце означает “Compact”), а готового софта для работы с ней не будет, то вам придется что-то делать.
Что обычно делает инженер, когда видит перед собой микросхему с неопознанным JTAG-портом. Конечно же пытается выяснить, какие устройства к нему подключены. Узнать это можно, прочитав их IDCODE. Для обычного JTAG процедура проста и выглядит так, как описано по ссылке www.fpga4fun.com/JTAG3.html:
- Сначала определяем число устройств в цепочке:
- Переходим в Test Mode Reset
- Переходим в Shift-IR
- Задвигаем через TDI кучу единиц (сколько не жалко, например, 1000) во все IR-ы в цепочке. Теперь все устройства в BYPASS
- Переходим из Shift-IR в Shift-DR
- Задвигаем через TDI кучу нулей (сколько не жалко, например, 1000) во все DR-ы в цепочке, чтобы обнулить их
- Начинаем задвигать через TDI единицы в DR. Как только получили единицу из TDO, прекращаем. Число устройств в цепочке равно числу задвинутых единиц.
- Потом для каждого устройства читаем IDCODE:
- Переходим в Test Mode Reset – в результате во все IR-ы записывается адрес регистра IDCODE.
- Идем в Shift-DR
- Читаем из TDO 32 бита для каждого устройства (кстати, для этого придется задвинуть что-нибудь в TDI). Первые 32 бита, прочитанные из TDO – IDCODE последнего устройства в цепочке, следующие 32 бита – IDCODE предпоследнего устройства, и т.д.
Изначально я хотел показать, как сделать то же самое, используя двухпроводной JTAG, но оказалось, что эта процедура длинновата для примера. Поэтому я решил упростить ее, предположив, что в JTAG-цепочке только одно устройство, и адрес его регистра IDCODE известен. Таким образом, выглядеть чтение IDCODE будет так:
- Записываем в Instruction Register (IR) адрес IDCODE:
- Идем в Shift-IR
- Находясь в Shift-IR, задвигаем в TDI адрес регистра IDCODE
- Читаем данные из IDCODE:
- Идем в Shift-DR
- Находясь в Shift-DR, задвигаем в TDI 32 произвольных бита и одновременно получаем содержимое IDCODE через TDO
Очевидно, что умея выполнять упрощенную процедуру, можно без проблем реализовать и полную, потому что они используют одни и те же команды. Однако без TDI и TDO, которых у 2-wire JTAG нет, ничего из вышеперечисленного работать не будет. Как же предлагается обойтись без них? Очень просто: по TMSC будут передаваться Scan-пакеты, содержащие TMS, TDI и TDO. Вот так выглядят два идущих друг за другом пакета (nTDI означает, что передается инвертированный TDI):
На самом деле, это только один из возможных форматов, описанных в стандарте – он называется OScan1. Есть еще JScan, MScan, SScan, а OScan-ов аж восемь штук. Правда, обязательны для 2-wire JTAG только JScan0-3, MScan, Oscan0 и Oscan1.
Тут нужно заметить, что сигнал TMSC, в отличие от обычного TMS, двунаправленный, потому что в каждом пакете TMS и TDI передаются в одну сторону, а TDO – в другую.
Однако если вы попробуете просто начать передавать OScan-пакеты, то вас ждет неприятный сюрприз. Короче говоря, ничего работать не будет. А все потому, что по умолчанию любое устройство с поддержкой IEEE 1149.7 находится в режиме совместимости со стандартом 1149.1 (так называемом «стандартном режиме» – standard mode) и никакие такие пакеты не воспринимает. Поэтому прежде чем слать всякие OSсan-ы, необходимо такое устройство сконфигурировать для работы в нужном формате и перевести его в «продвинутый режим» (advanced mode).
Прежде чем рассказать, как это сделать, требуется небольшое лирическое отступление. Одной из главных целей при разработке IEEE 1149.7 было обеспечить совместимость с существующим «железом», которое во все времена выглядело примерно так:
Вместо того, чтобы требовать от разработчиков микросхем переделать всю JTAG-логику, стандарт предлагает добавить адаптер, преобразующий двухпроводной интерфейс в обычный четырехпроводной:
Этот адаптер содержит свой собственный Test Access Port (TAP), называемый TAP.7, чтобы можно было отличить его от обычного TAP (он же TAP.1). У контроллера TAP.7 внутри точно такой же автомат, как у TAP.1, но есть и некоторые отличия, например, у него нет Instruction Register, зато есть много других регистров, которых нет у TAP.1.
По умолчанию TAP.7 «прозрачен», т.е. сигнал TCKC просто напрямую подключен к TCK, а TMSC – к TMS. Поэтому все сигналы, посылаемые по TCKC и TMSC передаются безо всяких изменений на TCK и TMS:
Очевидно, что при попытке передать Oscan1-пакет в таком режиме «nTDI» будет воспринят контроллером TAP.1 просто как первый бит, переданный по линии TMS, «TMS» — как второй бит, и т.д. Именно поэтому перед тем, как передавать пакеты, нужно перевести контроллер TAP.7 в «продвинутый режим». Это делается путем записи в один из внутренних регистров TAP.7. На время конфигурирования TCK и TMS отключаются (а TDI и TDO и раньше были отключены):
После того, как конфигурирование закончено, TCK, TMS, TDI и TDO управляются внутренней логикой контроллера:
Таким образом, процесс переключения в «продвинутый режим» и последующее чтение IDCODE выглядит так:
- Инициализировать контроллеры TAP.7 и TAP.1
- Отключить TCK и TMS
- Выбрать нужный формат передачи
- Если выбран формат OScan, MScan или SScan, то перейти в «продвинутый режим»
- Подключить TCK, TMS, TDI и TDO
- Записать в Instruction Register адрес IDCODE
- Прочитать данные из IDCODE
Инициализация TAP.7. Escape Reset
Поскольку никогда нельзя знать, в каком состоянии находятся автоматы TAP.7 и TAP.1, то перед началом работы необходимо сбросить их. Для сброса контроллера TAP.7 применяется Escape Reset – один из видов эскейп-последовательностей, определенных стандартом. От всех остальных JTAG-команд эскейп-последовательности отличаются тем, что значения TMS считываются не по переднему фронту TCK, а асинхронно. Контроллер TAP.7 считает количество фронтов сигнала TMS, пришедших за то время, пока TCKC неизменен. Количество фронтов определяет вид эскейп-последовательности. Для Escape Reset оно должно быть больше семи:
Также Escape Reset переводит TAP.7 в состояние Test Logic Reset. Вся прелесть эскейп-последовательностей в том, что они абсолютно никак не влияют на TAP.1, который их просто игнорирует. TAP.1 считывает значения TMS только по переднему фронту TCK, поэтому ему абсолютно все равно, что происходит с TMS в остальное время.
Вот так на экране осциллографа выглядит Escape Reset, который передает USB-JTAG адаптер Digilent HS2:
Как видно, все восемь фронтов на месте, однако расстояние между шестым и седьмым выглядит подозрительно. Я уже пожаловался в Digilent – обещали исправить.
Инициализация TAP.1
Чтобы сбросить TAP.1 вслед за TAP.7, стандарт рекомендует послать пять единиц по TMSC: они гарантированно переводят TAP-контроллер в Test-Logic-Reset из абсолютно любого другого состояния (можете проверить сами):
Поскольку больше в Test-Logic-Reset возвращаться мы не планируем, то имеет смысл вслед за пятью единицами послать нолик и тем самым перевести автомат в состояние Run-Test/Idle. Напомню, что контроллер считывает значения TMS в момент переднего фронта TCK и сразу же изменяет состояние автомата в соответствии с этими значениями:
Отключение TCK и TMS
Чтобы отключить TCM и TMS, нужно перевести контроллер TAP.7 в режим Control Mode 2. Всего контроллер поддерживает восемь разных Control Mode-ов, пронумерованных от нуля до семи. Нас интересует именно Control Mode 2, так как только в нем появляется доступ к служебным регистрам контроллера, один из которых нам и нужен, чтобы изменить формат пакетов на OScan1.
Переключение между Control Mode-ами осуществляется последовательно путем выполнения так называемого Zero-bit DR Shift (ZBS). Каждый выполненный ZBS увеличивает Control Mode на единицу. В отличие от эскейп-последовательностей, в теории ZBS может повлиять на контроллер TAP.1, однако на практике это крайне маловероятно. С точки зрения стандарта IEEE 1149.1 Zero-bit DR Shift не имеет абсолютно никакого смысла, поэтому разработчики понадеялись, что ни «железо», ни «софт», разработанные до появления 2-wire JTAG, не должны были использовать ZBS ни для каких своих нужд. На временной диаграмме ZBS выглядит так:
Как видно, ZBS начинается и заканчивается в состоянии Run-Test/Idle, поэтому важно не забыть после Escape Reset и Test-Logic-Reset перейти в Run-Test/Idle до того, как посылать первый ZBS.
Когда нужный режим выбран, его нужно зафиксировать (LOCK), после чего последующие ZBS-ы уже не смогут влиять на выбор режима. LOCK – это почти то же самое, что ZBS, только с заходом в Shift-DR:
Заметьте, что состояние TAP.1, в отличие от TAP.7, уже не меняется. Это потому, что TCK и TMS отключаются сразу, как только номер Control Mode-а становится равен двум, даже если он еще не зафиксирован.
Таким образом, чтобы попасть в Control Mode 2, нужно:
- Послать два ZBS
- Послать LOCK
Выбор формата
Теперь, когда мы находимся в режиме Control Mode 2, можно писать в служебные регистры. Стандарт описывает множество регистров, запись в которые производится специальными командами. Команды делятся на две группы – двухсекционные (two-part command) и трехсекционные (three-part command), которые я рассматривать не буду.
У двухсекционных команд в первой секции передается номер команды, а во второй – данные. Обе секции передаются одинаковым образом: из состояния Run-Test/Idle надо перейти в Shift-DR, после чего вернуться в Run-Test/Idle. Передаваемое значение равно количеству тактов, проведенных в Shift-DR.
Нас интересует команда под номером три — STFMT (Store Scan Format), которая записывает номер желаемого формата (для OScan1 он равен девяти), передаваемый во второй секции, в регистр SCNFMT (Scan Format).
Таким образом, в первой секции нам надо передать тройку, а во второй – девятку. Сначала записываем номер команды, для чего ждем три такта в состоянии Shift-DR (состояние TAP.1 не показано, так как оно не меняется):
Потом записываем данные (девять тактов в состоянии Shift-DR):
Запись в регистр SCNFMT сама по себе формат не меняет. Чтобы контроллер переключил формат, нужно послать специальный проверочный пакет (check packet).
Check Packet
Проверочный пакет проверяет корректность настроек, и если все в порядке, то контроллер переключает формат и, при необходимости, переходит в «продвинутый режим». Как у всего остального в IEEE 1149.7, у проверочного пакета куча разных вариантов. Вам придется поверить мне на слово, что нам нужен пакет с END-директивой (и не спрашивайте, что это значит), который выглядит вот так:
Ура, теперь мы в «продвинутом режиме» и можем наконец-то использовать OScan1! Чтобы вернутся в «стандартный режим», достаточно перейти в Test-Logic-Reset.
Подключение TCK, TMS, TDI и TDO
Итак, мы в «продвинутом режиме», но все еще в Control Mode 2, а это значит, что TAP.1 до сих пор отключен, и прочитать из него IDCODE мы не можем. Стандарт описывает четыре способа выхода из Control Mode:
- Перейти в Test-Logic-Reset
- Перейти в Select-IR-Scan
- Записать единичку в регистр ECL (Exit Control Level), используя двухсекционную команду STMC
- Сделать что-то неведомое, связанное с синхронизацией TAP.7-контроллеров
В Test-Logic-Reset нам нельзя, а то контроллер вернется в «стандартный режим». Двухсекционную команду слать долго. А вот пойти в Select-IR-Scan и вернуться в Run-Test/Idle, не проходя через Test-Logic-Reset, довольно просто – нужно всего лишь послать последовательность 011011 по TMS (заходить в Shift-IR не обязательно). Помните, что в JTAG всегда первым передается младший бит:
Красными точками отмечены места, в которых контроллер считывает биты TMS со входа TMSC. По TDI передаются нули (т.е. во всех пакетах биты nTDI равны единице), хотя они все равно игнорируются, так как мы не заходим в состояние Shift-IR.
Кстати, в отличие от «стандартного режима», состояние TAP.7 меняется не сразу же после получения нового бита TMS, а по третьему (последнему) переднему фронту TCKC в OScan1-пакете (на рисунке эти моменты показаны черными стрелочками) – это сделано для того, чтобы контроллер успел вставить в OScan1-пакет бит TDO и отправить его на хост (в этом конкретном случае контроллер возвращает нули по TDO).
Чтение IDCODE
Теперь, когда TAP.1 снова подключен к TAP.7, мы можем наконец прочитать содержимое регистра IDCODE.
Адрес регистра IDCODE не описан в стандарте, поэтому во всех контроллерах он разный (в моем случае он четырехбитный и равен 0xC). Чтобы упростить жизнь пользователям, Instruction Register инициализируется адресом IDCODE в состоянии Test-Logic-Reset. Однако в состоянии Capture-IR содержимое Instruction Register перезаписывается числом 0x1 (вне зависимости от длины Instruction Register его младший бит устанавливается в единицу, а все остальные в ноль) для того, чтобы можно было обнаружить обрыв цепи при тестировании. Так как мы заходили в Capture-IR на предыдущем этапе, то адрес IDCODE уже затерт, поэтому сначала нужно перезаписать его.
Для записи адреса регистра надо войти в состояние Shift-IR и задвинуть адрес (0xC) по TDI младшим битом вперед. При этом на TDO появится значение, находившееся в Instruction Register до этого, т.е. 0x1 (опять же, младшим битом вперед). В случае обычного четырехпроводного JTAG IEEE 1149.1 это выглядело бы так:
А вот так это выглядит при использовании пакетов OScan1:
Красными точками отмечены места, в которых контроллер считывает биты TMS, а зелеными – биты nTDI со входа TMSC. Фиолетовыми точками отмечены места, в которых контроллер считывает биты TDO перед тем, как вставить их в OScan1-пакеты.
Стрелочками показаны некоторые зависимости, например, сигналы TMS и TDI меняются по соответствующим фронтам TCKC, а сигнал TDO меняется по заднему фронту TCK.
Теперь остается только пойти в состояние Shift-DR, задвинуть 32 произвольных бита по TDI и прочесть 32 бита с TDO, которые и будут содержать IDCODE. С вашего позволения временную диаграмму чтения IDCODE приводить не буду.
Заключение
Я описал примерно 5% возможностей IEEE 1149.7. Тем не менее, я надеюсь, что у вас появилось какое-никакое понимание принципов, лежащих в его основе.
Все диаграммы в этой статье проверены на реальном железе с использованием USB-JTAG адаптера Digilent HS2, коммерческого контроллера 2-wire JTAG, к исходным кодам которого у меня есть доступ, отладочной платы с FPGA и логического анализатора ChipScope от Xilinx.
Программа для генерации последовательностей была написана с использованием API для HS2 на основе примера, который можно скачать вместе с Digilent Adept SDK и отлично работала как под Linux, так и под Windows.
Автор: valeriyk