======Немного о Jtag, ISC и 5576РС1У====== Подвернулась небольшая подработка по программированию микросхемы конфигурирования ПЛИС 5576РС1У. Здесь собраны тезисы того, что удалось узнать при решении данной задачи. =====Про 5576РС1У ===== Микросхема 5576РС1У - это микросхема флеш памяти, которая выдает сохраненные в ней данные через последовательный интерфейс в микросхему ПЛИС, тем самым ее конфигурируя. Диаграммы режима PassivSerial уже были представлены тут - [[https://startmilandr.ru/doku.php/doc:mk:5576rs1u|Конфигурация в режиме Passive Serial]] 5576РС1У является аналогом микросхемы EPC4QC100N от Altera, поэтому алгоритм работы с 5576РС1У можно найти в BSDL файле для EPC4QC100N ([[https://github.com/StartMilandr/MDR_Pack_v6/blob/master/PACK_Gen/Files/Examples/All_Boards/5576RC1U_Test/epc4q100_1532.bsd|epc4q100_1532.bsd]]) и в векторном файле [[https://github.com/StartMilandr/MDR_Pack_v6/blob/master/PACK_Gen/Files/Examples/All_Boards/5576RC1U_Test/test_epc.svf|test_epc.svf]]. * BSDL файл - это файл описания внутренних регистров микросхемы для реализации периферийного сканирования (Boundary Scan Description Language). * SVF файл - это файл описания воздействий на микросхему, которым проверяется ее функционал (Serial Vector Format ). Доступ к внутренним регистрам микросхемы и подача тестовых воздействий происходят по интерфейсу JTAG. Под него эти файлы и написаны. ====BSDL файл==== В BSDL файле нам интересен в первую очередь раздел с описанием регистров и инструкций. Вот содержимое раздела: -- ********************************************************************* -- * INSTRUCTIONS AND REGISTER ACCESS * -- ********************************************************************* attribute INSTRUCTION_LENGTH of EPC4Q100 : entity is 10; attribute INSTRUCTION_OPCODE of EPC4Q100 : entity is "BYPASS (1111111111), "& "EXTEST (0000000000), "& "SAMPLE (0001010101), "& "IDCODE (0001011001), "& "USERCODE (0001111001), "& -- Following 10 instructions are IEEE 1532 instructions "ISC_ENABLE (0001000100), "& "ISC_DISABLE (0001001010), "& "ISC_PROGRAM (0110010110), "& "ISC_ERASE (0110010010), "& "ISC_ADDRESS_SHIFT (0100001110), "& "ISC_READ_INFO (0001000010), "& "ISC_READ (0110100110), "& "ISC_NOOP (0011111111), "& "ISC_STAT (0000111110)"; По данному отрывку мы узнаем перечень доступных инструкций и то, что все они являются 10-битными. Инструкции с префиксом **ISC** относятся к интерфейсу **In-System Configuring**, который регламентируется стандартом **IEEE 1532**. Который является в свою очередь расширением стандарта JTAG (IEEE 1149.1 Standard Test Access Port and Boundary-Scan Architecture - [[https://ru.wikipedia.org/wiki/JTAG|Wikipedia]]). Ранее каждый производитель предлагал свои способы прошить то или иное устройство, что затрудняло работу с микросхемами разных производителей (например в составе одного устройства). Требовалось изучение каждого отдельного способа программирования и необходим был свой программатор. Чтобы это дело упростить придумали отдельный стандарт на внутрисхемное конфигурирование (ISC) и теперь каждый производитель внутри микросхемы реализует поддержку команд данного стандарта. Найти в сети описание стандарта ISC (IEEE 1532) не удалось, поэтому остается только предположить назначение инструкций исходя из их названия. Некоторые уточнения: * IDCODE - Позволяет считать идентификатор микросхемы, в данном случае это 0x0100A0DD. * USERCODE - Позволяет записать / считать ID который можно использовать в своих целях. Например версия прошивки. * ISC_ENABLE / ISC_DISABLE - вход в режим ISC и выход. * ISC_NOOP - вероятно No Operation. * ISC_STAT - статус исполнения инструкции. * ISC_ADDRESS_SHIFT - установка текущего адреса перед ISC_READ / ISC_PROGRAM / ISC_ERASE. * ISC_PROGRAM / ISC_READ - вход в режимы программирования / чтения. Адрес инкрементируется автоматически с каждым новым словом данных * ISC_ERASE - стирание сектора данных. Стертые данные соответствуют логическим 1-цам, соответственно при программировании записываются только нули. Стирается сектор целиком. Далее в файле представлены доступные регистры: attribute REGISTER_ACCESS of EPC4Q100 : entity is "DEVICE_ID (IDCODE), "& -- Following 7 registers are IEEE 1532 registers "ISC_Default[2] (ISC_DISABLE, ISC_NOOP)," & "ISC_PData[16] (ISC_PROGRAM)," & "ISC_RData[18] (ISC_READ, ISC_STAT)," & "ISC_Sector[23] (ISC_ERASE)," & "ISC_Address[23] (ISC_ADDRESS_SHIFT)," & "ISC_Config[2] (ISC_ENABLE)," & "ISC_Info[5] (ISC_READ_INFO)"; По этому описанию мы узнаем разрядность внутренних регистров. Чтобы записать или считать тот или иной регистр необходимо сначала подать инструкцию, а затем считать или записать значение в данный регистр. Грубо говоря, записывая по jtag инструкцию мы выбираем внутренний регистр с которым дальше будем обмениваться данными. Основные интересующие нас регистры: * ISC_PData - 16-битный регистр в который записываются слова для записи в память. Слова пишутся по одному, необходимо выждать лишь требуемую задержку. * ISC_RData - 18-битный регистр чтения значений из памяти. Используется для верификации данных, которые были запрограммированы. Два младших бита являются значениями CRC для 16-битного слова. * ISC_Sector - 23-битный адрес сектора, который будет стерт. * ISC_Address - 23-битный адрес с которого будут читаться слова или куда слова будут программироваться. В этом же файле BDSL описано как применять эти инструкции и регистры. Возьмем к примеру функцию программирования памяти: "FLOW_PROGRAM (array) " & "INITIALIZE " & "(ISC_ADDRESS_SHIFT 23:000000 WAIT TCK 1)" & "(ISC_PROGRAM 16:? WAIT 3.0e-4 )" & "REPEAT 249999 " & "(ISC_PROGRAM 16:? WAIT 3.0e-4 )"; Я трактую это следующим образом: * **"ISC_ADDRESS_SHIFT 23:000000"** - Подать инструкцию ISC_ADDRESS_SHIFT и 23-битный адрес равный 0x000000. * **"WAIT TCK 1"** - После этого следует подать один тактовый сигнал на вывод TCK интерфейса JTAG. * **"ISC_PROGRAM 16:?"** - Записать инструкцию ISC_PROGRAM и 16-ное значение данных. (? - означает не конкретное значение как в случае с адресом, а значение из массива array). * **"WAIT 3.0e-4"** - выждать задержку 300мкс пока слова программируется. * **"REPEAT 249999"** - повторить все это еще 249999 раз. * В обрывках литературы который выдал поиск в Google встречается различие между задержками //"WAIT TCK 1"// и //"WAIT 3.0e-4//". Суть его в том, что когда указано //"WAIT TCK 1"//, то это требование именно подать тактовый сигнал. Т.е. если бы было написано //"WAIT TCK 100"//, то необходимо было бы подать 100 тактовых периодов на вывод TCK интерфейса JTAG. В отличие от этого, задержка //"WAIT 3.0e-4//" - это всего лишь временной интервал. В машине состояний JTAG, которую мы рассмотрим дальше, наличие лишнего такта TCK не имеет никакого смысла. Возможно требование к TCK это нечто из расширения ISC, поскольку большинство инструкций ISC в BSDL файле заканчивается так. Поэтому при реализации обмена мы этот такт оставим. Аналогично расшифровывается чтение памяти, например верификация того, что память стерта: "FLOW_BLANK_CHECK " & "INITIALIZE" & "(ISC_READ WAIT TCK 1 16:DFFF*0000, 2:2*0) "& "(ISC_ADDRESS_SHIFT 23:000000 WAIT TCK 1)" & "(ISC_READ WAIT TCK 1 18:3FFFE*FFFFF)" & "REPEAT 249999 " & "(ISC_READ WAIT TCK 1 18:3FFFE*FFFFF)," & * **"ISC_READ WAIT TCK 1"** - Подается команда с лишним тактом * **16:DFFF*0000, 2:2*0** - Читается 16-битный ответ и еще 2 бита CRC. Используется маска //AND 0000//, что говорит о том, что чтение служебное (пустое) - данные никак не проверяются. * **"ISC_ADDRESS_SHIFT 23:000000 WAIT TCK 1"** - Выставляется начальный адрес. * **"ISC_READ WAIT TCK 1"** - Запись инструкции чтения. * **18:3FFFE*FFFFF** - Читается 18-битное слово памяти, которое должно быть равно 3FFFE (стертое значение 0xFFFF с CRC). Маска на считанное значение FFFFF. * **"REPEAT 249999"** - Повторить 249999 раз. Остальные функции можно посмотреть в самом BSDL файле. Под описанием функций представлены операции, которые выполняются с микросхемой программатором. Каждая операция состоит из нескольких функций. Мы рассмотрели сейчас только функции PROC_PROGRAM(array) и PROC_BLANK_CHECK: attribute ISC_ACTION of EPC4Q100 : entity is "VERIFY_IDCODE = (TEST_VERIFY_IDCODE)," & "PROGRAM = (TEST_VERIFY_IDCODE RECOMMENDED," & "PROC_ENABLE," & "PROC_ERASE," & "PROC_BLANK_CHECK," & "PROC_PROGRAM(array)," & "PROC_VERIFY(array)," & "PROC_PROGRAM_DONE," & "PROC_VERIFY(donebit)," & "PROC_DISABLE)," & "VERIFY = (TEST_VERIFY_IDCODE RECOMMENDED," & "PROC_ENABLE," & "PROC_VERIFY(array)," & "PROC_VERIFY(donebit)," & "PROC_DISABLE)," & "ERASE = (TEST_VERIFY_IDCODE RECOMMENDED," & "PROC_ENABLE," & "PROC_ERASE," & "PROC_BLANK_CHECK," & "PROC_DISABLE)," & "BLANK_CHECK = (TEST_VERIFY_IDCODE RECOMMENDED," & "PROC_ENABLE," & "PROC_BLANK_CHECK," & "PROC_DISABLE)," & "VERIFY_DONEBIT = (TEST_VERIFY_IDCODE RECOMMENDED," & "PROC_ENABLE," & "PROC_VERIFY(donebit)," & "PROC_DISABLE)," & "PROGRAM_DONE = (PROC_ENABLE," & "PROC_PROGRAM_DONE," & "PROC_DISABLE)"; ====Интерфейс JTAG==== Из BSDL файла мы узнали что в 5576РС1У есть внутренние регистры и для доступа к ним есть набор инструкций. Встает вопрос - что с этим делать, куда и как это все подавать? Для этого необходимо рассмотреть машину состояний JTAG и понять как она работает. В общем-то моя многолетняя фобия перед JTAG оказалась напрасной. Это достаточно простой последовательный интерфейс для записи инструкций и значений во внутренние регистры. Что там дальше делать с этими инструкциями и регистрами решает внутреннее устройство микросхемы. Назначение JTAG осуществить транзакцию, к дальнейшему он уже отношения не имеет. Основным регистром является регистр BoudaryScan который бывает длиной в сотни бит и позволяет тестировать выводы микросхемы в готовом устройстве. Есть так-же регистр ByPass длиной в один бит, который позволяет пропускать транзакции сквозь микросхему в следующие микросхемы объединенные в одну Jtag цепь (DaisyChain). Бывают всякие специализированные регистры. Например, как мы уже знаем, в 5576РС1У есть регистры ISC через которые происходит программирование микросхемы. **Линии JTAG:** * TCK - тактовый сигнал, весь "экшн" происходит по возрастающему фронту этого сигнала. * TDI - по фронту TCK уровень на данном выводе записывается в текущий сдвиговый регистр внутри микросхемы. * TDO - одновременно с защелкиванием бита TDI в сдвиговый регистр, из этого регистра выдвигается бит на вывод TDO. * TMS - сигнал перемещения по машине состояний JTAG. * TRST - опциональный сигнал сброса подключенного устройства, в микроконтроллерах обычно подключается к выводу Reset. {{doc:doclist:jtagregupdate.png}} Про TDI и TDO полагаю понятно, что они подключаются с двух сторон внутреннего сдвигового регистра. Данные от TDI вдвигаются в регистр, а из регистра синхронно данные выдвигаются в линию TDO. Но поскольку регистров внутри устройства может быть несколько, то необходим способ выбора того регистра с которым будут работать линии TDI и TDO. Для этого предназначен автомат состояний и перемещение по этому автомату происходит с помощью сигнала TMS. Сразу стоит сказать, что TDI и TDO подключаются к внутреннему регистру только в состояниях **Shift-DR** и **Shift-IR**. Поэтому чтобы записать / считать регистры необходимо в автомате состояний дошагать до Shift-DR или ShiftIR соответственно. **IR** - это регистр инструкций. Он один и он выбирает который внутренний регистр будет подключен к линиям TDI и TDO в состоянии Shift-DR. Например записав в IR инструкцию ISC_ADDRESS_SHIFT, а в DR записав значение адреса мы переключим в 5576РС1У текущий активный адрес. Посмотрим как это осуществить по машине состояний JTAG. {{doc:doclist:jtagstates.png}} //(Картинка из шикарного видео, где наглядно расказывается про Jtag - [[https://www.youtube.com/watch?v=PhaqHKyAvR4|YouTube - TechSharpen, "JTAG TAP Controller Tutorial"]])// При работе по JTAG обычно выбирают "парковочное" состояние - это то состояние в которое возвращается автомат между транзакциями. Обычно это либо состояние Idle, либо Select-DR. Select-DR подходит, потому что из него можно попасть и в ветвь DR и в ветвь IR. Но состояние Idle лучше тем, что в нем можно подать импульс TCK при TMS=0, который ничего не изменит, т.к. автомат останется в том-же Idle. А нам как раз необходим один лишний такт TCK для реализации выражения //"WAIT TCK 1"// из файла BSDL. Состояние Reset не подходит под парковочное, потому что при выходе в reset к линиям TDI и TDO подключается регистр по умолчанию, а это обычно ByPass или BoundaryScan. Выставление внутреннего адреса происходит в два этапа - запись инструкции ISC_ADDRESS_SHIFT в IR и запись самого адреса через регистр DR. Запись инструкции по автомату состояний происходит так: //допустим мы находимся в состоянии idle, тогда подаем TMS Idle 1: -> Select-DR 1: -> Select-IR // Дошли до ветки инструкций 0: -> Caprute-IR 0: -> Shift-IR // Перешли в состояние сдвига бит IR 0: TDI[0] -> reg ->TD0[0] // Начинаем побитно сдвигать инструкцию в регистр IR 0: ... // ISC_ADDRESS_SHIFT (0100001110), 10 bit 0: TDI[9] -> reg ->TDO[9] // На TDO выдвигаются биты предыдущего значения IR 1: -> Exit-IR 1: -> Update-IR // Значение сдвигового регистра записалось в регистр инструкций. // Теперь к линиям TDI и TDO подключен регистр ISC_Address[23] 0: -> Idle // Возвращаемся в Idle 0: -> Idle // В Idle делаем дополнительный такт TCK - "WAIT TCK 1" two beer... **Диаграмма программирования слова** (кликабельно): {{doc:jtag:flowprogram.png}} * Красными линиями на фронтах TCK выделены служебные такты на перемещение по машине состояний JTAG. * Синими линиями выделены биты данных задвигаемые через линию TDI во внутренние регистры. * Сдвиг последнего бита совмещен с переходом на состояние Exit1, поэтому линия покрашена разноцветно. * Фиолетовым цветом выделен служебный такт "WAIT TCK 1". ====Диаграмма чтения IDCODE из 1986ВЕ1Т==== {{doc:jtag:jtag_ve1_idcode.png}} ====SVF файл====