Разбираем протокол 2-wire JTAG

5eda06036f374b0d98989ac1ca674646.png2-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): d8e42e7a36564e4aa759346bf5f48cbf.pngНа самом деле, это только один из возможных форматов, описанных в стандарте — он называется 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 было обеспечить совместимость с существующим «железом», которое во все времена выглядело примерно так:

a6d1300b36c94dd18e79f00a3cad5b44.png Вместо того, чтобы требовать от разработчиков микросхем переделать всю JTAG-логику, стандарт предлагает добавить адаптер, преобразующий двухпроводной интерфейс в обычный четырехпроводной: adedff927d0c459f9519f0e5e9f14264.png Этот адаптер содержит свой собственный 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:

838df114c5ca4374a356db98780c4b9b.png Очевидно, что при попытке передать Oscan1-пакет в таком режиме «nTDI» будет воспринят контроллером TAP.1 просто как первый бит, переданный по линии TMS, «TMS» — как второй бит, и т.д. Именно поэтому перед тем, как передавать пакеты, нужно перевести контроллер TAP.7 в «продвинутый режим». Это делается путем записи в один из внутренних регистров TAP.7. На время конфигурирования TCK и TMS отключаются (а TDI и TDO и раньше были отключены): 63c75c51de5d4a5a824749692a97b9fd.png После того, как конфигурирование закончено, TCK, TMS, TDI и TDO управляются внутренней логикой контроллера: 42ce54b13a374208ac3de0e5e45c3470.png Таким образом, процесс переключения в «продвинутый режим» и последующее чтение 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 оно должно быть больше семи: 4627752485984dd3b9d326d3eb6eab16.png Также Escape Reset переводит TAP.7 в состояние Test Logic Reset. Вся прелесть эскейп-последовательностей в том, что они абсолютно никак не влияют на TAP.1, который их просто игнорирует. TAP.1 считывает значения TMS только по переднему фронту TCK, поэтому ему абсолютно все равно, что происходит с TMS в остальное время.Вот так на экране осциллографа выглядит Escape Reset, который передает USB-JTAG адаптер Digilent HS2: eed23b7b56c24a648f714022f685f474.jpg Как видно, все восемь фронтов на месте, однако расстояние между шестым и седьмым выглядит подозрительно. Я уже пожаловался в Digilent — обещали исправить.Инициализация TAP.1 Чтобы сбросить TAP.1 вслед за TAP.7, стандарт рекомендует послать пять единиц по TMSC: они гарантированно переводят TAP-контроллер в Test-Logic-Reset из абсолютно любого другого состояния (можете проверить сами): 7f0b2946feb44ba2b3dc417c3da2e422.png Поскольку больше в Test-Logic-Reset возвращаться мы не планируем, то имеет смысл вслед за пятью единицами послать нолик и тем самым перевести автомат в состояние Run-Test/Idle. Напомню, что контроллер считывает значения TMS в момент переднего фронта TCK и сразу же изменяет состояние автомата в соответствии с этими значениями: a0cfa150d0464a46bf8fb12a7f8bf3fb.png Отключение 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 выглядит так:

8a75eb662bf840c2816ab087c9d6cb30.png Как видно, ZBS начинается и заканчивается в состоянии Run-Test/Idle, поэтому важно не забыть после Escape Reset и Test-Logic-Reset перейти в Run-Test/Idle до того, как посылать первый ZBS.Когда нужный режим выбран, его нужно зафиксировать (LOCK), после чего последующие ZBS-ы уже не смогут влиять на выбор режима. LOCK — это почти то же самое, что ZBS, только с заходом в Shift-DR:

5959febf4bee473983615ad11ccf42af.png Заметьте, что состояние 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 не показано, так как оно не меняется):

2e0f050c651b4d039769bfc3827272cf.png Потом записываем данные (девять тактов в состоянии Shift-DR): 1014cd40fe674aa3b11f2d3682e351bc.png Запись в регистр SCNFMT сама по себе формат не меняет. Чтобы контроллер переключил формат, нужно послать специальный проверочный пакет (check packet).Check Packet Проверочный пакет проверяет корректность настроек, и если все в порядке, то контроллер переключает формат и, при необходимости, переходит в «продвинутый режим». Как у всего остального в IEEE 1149.7, у проверочного пакета куча разных вариантов. Вам придется поверить мне на слово, что нам нужен пакет с END-директивой (и не спрашивайте, что это значит), который выглядит вот так: 287ced3fadb449c5a8a80e7cc3f8bd91.png Ура, теперь мы в «продвинутом режиме» и можем наконец-то использовать 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 всегда первым передается младший бит: a14f2a5336864382bbe01f1e401c3020.png Красными точками отмечены места, в которых контроллер считывает биты 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 перезаписывается числом 0×1 (вне зависимости от длины Instruction Register его младший бит устанавливается в единицу, а все остальные в ноль) для того, чтобы можно было обнаружить обрыв цепи при тестировании. Так как мы заходили в Capture-IR на предыдущем этапе, то адрес IDCODE уже затерт, поэтому сначала нужно перезаписать его.

Для записи адреса регистра надо войти в состояние Shift-IR и задвинуть адрес (0xC) по TDI младшим битом вперед. При этом на TDO появится значение, находившееся в Instruction Register до этого, т.е. 0×1 (опять же, младшим битом вперед). В случае обычного четырехпроводного JTAG IEEE 1149.1 это выглядело бы так:

f728dd8f1da249bc941e2003b8bc21c5.png А вот так это выглядит при использовании пакетов OScan1: 0a744377a67b411b9592a81fae797f7f.png Красными точками отмечены места, в которых контроллер считывает биты 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.

© Habrahabr.ru