Перейти к основному содержимому

ЛР №8 Jtag

Использование интерфейса JTAG для взаимодействия процессорной системы с инструментальным компьютером

Для кого этот документ

  • Студентам курса "Организация ЭВМ"
  • Необходимое ПО: Quartus II

Контекст

Данная лабораторная работа посвящена изучению интерфейса JTAG UART и его использованию для взаимодействия процессорной системы NIOS II с инструментальным компьютером. Студенты научатся передавать данные между платой DE2-70 и терминалом, работать с буферами FIFO, а также реализовывать обработку ввода-вывода с использованием прерываний и таймеров.

Цель работы

Изучение порта JTAG UART и практическое его использование для взаимодействия процессорной системы, реализованной на стенде, с оператором за инструментальным компьютером.

Планируемые результаты обучения

После выполнения этой работы студенты должны уметь:

  • Вводить информацию в процессорную систему, используя клавиатуру инструментального компьютера;
  • Выводить информацию из процессорной системы в терминальное окно инструментального компьютера.
  • Выполнять ввод и вывод информации с использованием прерывания текущей программы
  • Использовать вложенные прерывания от таймера

Исходные файлы лабораторной работы

Программа, демонстрирующая использование JTAG UART порта в процессорной системе, является составной частью приложения AMP. Она доступна в разделе Sample program под именем JTAG UART.s.

Программные заготовки

Для выполнения лабораторной работы доступны программные заготовки, содержащие исходные файлы для всех задач:

Подготовка к лабораторной работе

Для выполнения лабораторной работы необходимо изучить по материалам лекций и предложенной литературе:

  1. Организацию обмена данными между стендом и инструментальным компьютером.
  2. Принцип работы и назначение регистров и отдельных полей JTAG UART порта. Эта информация содержится в описании процессорной системы «DE2-70 Media Computer» [9] и в материалах JTAG порт. Включите ее в отчет.
  3. Реализацию прерываний в процессорной системе с процессором NIOSII[8]. Используемые для этого регистры процессора и портов ввода – вывода.
  4. Особенности использования вложенных прерываний.
  5. Уясните принцип действия программы, работающей с интерфейсом JTAG из файла JTAG UART.s. Включите ее в отчет.
  6. Уясните содержимое фрагмента программы TEST_DE2_70_Media_Computer из листинга 9 приложения и включите его в отчет.
  7. Уясните пункты задания, выполняемого в текущей лабораторной работе, и подготовьте программные заготовки для их выполнения.

В лабораторной работе ASCII-коды символов, введенных в терминальном окне AMP, отправляются в процессорную систему, реализованную на стенде с использованием UART JTAG интерфейса, отображаются на светодиодах и 7-сегментных индикаторах, затем отправляются обратно в инструментальный компьютер, и отображаются в терминальном окне AMP.

Материалы для подготовки

Перед выполнением лабораторной работы рекомендуется изучить теоретические материалы:

Эти материалы содержат подробную информацию о принципах работы JTAG интерфейса и программном интерфейсе JTAG UART порта, необходимую для выполнения лабораторной работы.

Чеклист выполнения

  • Изучена документация по JTAG UART и системе команд NIOS II.
  • Написана и отлажена программа вывода символа в JTAG UART с проверкой WSPACE.
  • Экспериментально определен размер буфера FIFO на вывод.
  • Реализован многократный вывод символов с задержкой для переполнения буфера.
  • Написана процедура вывода текстовой строки (null-terminated string).
  • Написана и отлажена подпрограмма чтения из JTAG UART с проверкой RVALID.
  • Экспериментально определен размер буфера FIFO на ввод.
  • Реализован вывод кодов символов на светодиоды/индикаторы.
  • Реализован ввод строки в память с эхо-печатью.
  • Реализован ввод строки в режиме прерывания от JTAG UART.
  • Реализован периодический вывод символов с использованием таймера.
  • Реализовано совместное использование прерываний от JTAG UART и таймера.
  • Реализован "предохранительный клапан" в обработчике прерывания по записи.

Порядок выполнения лабораторной работы

Часть 1. Запись в UART JTAG (вывод информации)

  1. Напишите программу, которая записывает символ z в порт UART JTAG. Предусмотрите в программе анализ поля WSPACE в регистре управления UART, отражающего наличие свободного места в буфере FIFO для записываемых символов. Реализуйте выход из программы, если свободного места в буфере нет.

  2. Отладьте программу. По содержимому поля WSPACE определите размер буфера FIFO. Используйте для этого вкладку Memory приложения AMP. Измените символ z на другой символ.

  3. Модифицируйте программу из предыдущего пункта таким образом, чтобы символы выводились многократно. Реализуйте программную задержку в цикле вывода. Подберите задержку таким образом, чтобы происходили следующие события:

    • Программа завершается по переполнению буфера FIFO, и при этом выводится в терминальное окно AMP минимальное количество символов. Подсчитайте количество выведенных символов. Сравните со значением, полученным в пункте 2.
    • Буфер FIFO никогда не переполняется, и программа реализует бесконечный цикл.
    • Буфер FIFO успевает вывести некоторое количество символов до того, как он переполнится.

    Запишите в отчет подобранные задержки.

  4. Напишите программу, которая выводит в терминальное окно AMP некоторое сообщение. Сообщение разместите в сегменте данных программы. Для этого используйте директиву .asciz ассемблера. Программа должна анализировать выводимый байт, и если он равен нулю, то вывод должен прекращаться. Оформите эту программу в виде процедуры, которая в последующем может быть использована для вывода из процессорной системы различных сообщений. Параметром, передаваемым этой процедуре, является адрес текстовой строки в сегменте данных. В качестве тестового примера выведите свою фамилию, имя и отчество. Сделайте снимок на смартфон и поместите его в отчетные материалы.

Часть 2. Чтение из JTAG UART (ввод информации)

  1. Напишите подпрограмму, которая выполняет чтение из порта UART JTAG. Подпрограмма должна анализировать бит RVALID в регистре данных UART. Если этот бит равен 1, считанные данные присутствовали в буфере FIFO и они достоверны. В противном случае, данных в буфере нет, и подпрограмма должна ожидать их появления. Принятый из UART JTAG символ, основная программа должна отобразить на светодиодах и двух 7-сегментных индикаторах, записать обратно в JTAG и так далее в цикле. Отладьте подпрограмму.

  2. С помощью AMP скомпилируйте программу из предыдущего пункта и загрузите в память. Поставьте контрольную точку в программе после приема первого символа из UART JTAG. Проанализируйте поле RAVAIL. Запишите в отчет. Обратите внимание! Для анализа поля RAVAIL потребуется включить опцию Query all devices и нажать кнопку Refresh. Учтите, что при этом произойдет чтение регистра данных UART JTAG и, следовательно, удаление символа из вершины буфера FIFO и уменьшение поля RAVAIL на 1. Повторите эксперимент, но предварительно напечатайте в терминальном окне AMP несколько символов с клавиатуры. Рекомендуется набирать осмысленное сообщение, чтобы в последующем можно было бы проверить правильность его вывода в терминальное окно. Проанализируйте поле RAVAIL. Отключите опцию Query all devices, уберите контрольную точку и продолжите выполнение программы. Сравните количество выведенных символов в терминальном окне AMP с содержимым поля RAVAIL. Повторите эксперимент. Экспериментально определите размер буфера FIFO для читаемых данных.

  3. Модифицируйте программу таким образом, чтобы коды печатаемых на клавиатуре символов дополнительно отображались на светодиодах и двух HEX индикаторах. Составьте таблицу кодов ASCII для десяти цифр, нескольких букв вашей фамилии. Получите коды символов Enter, Backspace. Получите коды ASCII для одной и той же клавиши прописными и заглавными буквами, в английской и русской раскладке.

  4. Модифицируйте подпрограмму чтения из порта UART JTAG таким образом, чтобы ASCII-коды выводимых символов записывались в память побайтно. Ввод должен завершаться при получении символа Enter. В этом случае в конец строки должен быть добавлен нулевой байт, означающий конец строки. Подпрограмма в последующем будет полезна для реализации ввода информации в процессорную систему с клавиатуры инструментального компьютера. Отправьте сохраненную строку в UART JTAG порт, используя подпрограмму, написанную в пункте 7 первой части.

Часть 3. Ввод из UART JTAG в режиме прерывания

  1. Модифицируйте программу таким образом, чтобы ввод текстовой строки из инструментального компьютера осуществлялся в режиме прерывания. Для этого основная программа разрешает прерывания по чтению от UART JTAG и выполняет вывод строки с Вашей фамилией, именем и отчеством на дисплей LCD в режиме бегущей строки. Ввод текста в терминальном окне приводит к прерыванию основной программы, и обработчик прерывания далее осуществляет ввод строки в ОП и вывод в UART JTAG всей строки, пока не обнаружит символ конца строки. В это время вывод бегущей строки на LCD дисплей должен остановиться.

  2. Модифицируйте программу таким образом, чтобы после ввода каждого символа обработчик прерывания возвращал управление основной программе. Визуально наблюдайте правильность работы программы.

Часть 4. Ввод из JTAG UART в режиме прерывания с использованием вложенного прерывания от таймера

  1. Модифицируйте программу из предыдущей части таким образом, чтобы реализовать вывод символов в терминальном окне AMP следующим образом. Набранный на клавиатуре символ выводится в терминальном окне AMP каждые 500мс.

  2. Модифицируйте программу из предыдущей части таким образом, чтобы основная программа разрешила прерывания по чтению от UART JTAG и выводила бегущую строку на LCD. При нажатии клавиши клавиатуры инструментального компьютера формируется прерывание основной программы. Обработчик прерывания UART JTAG разрешает прерывания от таймера каждые 500 мс и возвращает управление основной программе. Обработчик прерывания от таймера выводит символ в терминальное окно AMP каждые 500 мс, причем, если клавиша не нажата, повторяется вывод последнего набранного символа.

Часть 5. Вывод из процессорной системы в JTAG UART с использованием вложенного прерывания

Экспериментально определите при каком условии формируется прерывание процессорной системы при выполнении вывода символьной информации в терминальное окно инструментального компьютера. Для этого в основной программе разрешите прерывания по записи от JTAG UART. Обработчик прерывания должен вывести на экран LCD предупреждающее сообщение, проверить, освободился ли буфер FIFO, и если да, вернуть управление основной программе. Таким образом, обработчик прерывания будет срабатывать как предохранительный клапан, принудительно приостанавливая процесс дальнейшего заполнения символьной информацией буфера FIFO, позволяя тем самым в это время его освободить.

Отчетные материалы

Отчетные материалы должны содержать:

  1. Цель лабораторной работы.
  2. Материалы, связанные с подготовкой к работе, включая теоретическую часть.
  3. Фрагменты программ JTAG UART.s и TEST_DE2_70_Media_Computer.
  4. Информацию по выполнению каждого пункта задания. Причем в отчете должны содержаться выполняемые Вами действия, наблюдаемые результаты, и Ваши объяснения.
  5. Написанные Вами фрагменты программ.
  6. Краткое заключение.

FAQ

Q: Что такое JTAG UART? A: Это компонент, который обеспечивает последовательный интерфейс связи (UART) между процессорной системой на ПЛИС и хост-компьютером через кабель JTAG. Это позволяет использовать терминал на компьютере для ввода/вывода данных.

Q: Зачем проверять биты RVALID и поле WSPACE? A: RVALID указывает, есть ли действительные данные в буфере приема (FIFO). WSPACE показывает количество свободного места в буфере передачи. Без проверки этих полей можно прочитать мусор или потерять передаваемые данные при переполнении буфера.

Q: Как работают прерывания в JTAG UART? A: Прерывания могут генерироваться при наличии данных для чтения (бит RE - Read Interrupt Enable) или при наличии места для записи (бит WE - Write Interrupt Enable). Это позволяет процессору не тратить время на постоянный опрос регистров (поллинг), а реагировать только на события.

Q: Почему важно использовать вложенные прерывания? A: Вложенные прерывания позволяют системе обрабатывать более приоритетные события (например, от таймера), даже если она уже занята обработкой менее приоритетного прерывания (например, ввода пользователя), что повышает отзывчивость системы реального времени.

Дополнительные материалы

Приложение

Листинг 8. Текст программы JTAG UART.s

/****************************************************************
* Программа выполняет следующее:
* 1. отправляет символьную строку из текстовой строки в JTAG UART
* 2. считывает данные из JTAG UART
* 3. отправляет считанные данные обратно в JTAG UART
********************************************************************/

.text
.global _start
_start:

movia sp, 0x07FFFFFC /* Определяем адрес вершины стека */

movia r6, 0x10001000

movia r8, TEXT_STRING /* адрес текстовой строки в r8 */
:
ldb r5, 0(r8) /* из текстовой строки в r5*/
call PUT_JTAG /* вызываем процедуру, которая помещает символ в JTAG */

addi r8, r8, 1

br /* переходим на вывод очередного символа */

GET_JTAG:
ldwio r4, 0(r6) /*читаем регистр управления JTAG UART*/
andi r8, r4, 0x8000 /*проверяем есть ли новые данные*/
beq r8, r0, GET_JTAG /*если данных нет, то ожидаем их появления*/
andi r5, r4, 0x00ff /*получаем данные*/

call PUT_JTAG /*отправляем символ обратно в JTAG UART*/
call GET_JTAG

.end

/********************************************************************
* Подпрограмма, которая выводит символы в JTAG UART
* r5 = выводимый символ
* r6 = JTAG UART базовый адрес
********************************************************************/

.global PUT_JTAG
PUT_JTAG:

/* сохраняем используемые регистры */

subi sp, sp, 4 /* резервирум место в стеке*/
stw r4, 0(sp) /* сохраняем r4 в стеке */

ldwio r4, 4(r6) /* читаем регистр управления JTAG UART */
andhi r4, r4, 0xffff /* проверяем есть ли место для записи в буфере UART */
beq r4, r0, KONEC /* если места нет, то переполнение буфера */
stwio r5, 0(r6) /* отправляем символ в UART */

OVERFLOW:

/* восстанавливаем содержимое r4 */

ldw r4, 0(sp)
addi sp, sp, 4

ret

/******************************************************************/

.data

TEXT_STRING:
.asciz "\nJTAG UART example code\n"

.end

Листинг 9. Фрагмент программы TEST_DE2_70_Media_Computer

/****************************************************************
* Процедура анализирует, имеются ли данные в буфере чтения JTAG порта.
* Если есть, то данные отправляются обратно в JTAG порт и дополнительно в
* COM-порт. Если новых данных нет, то выполняется проверка, имеются ли данные в буфере чтения com-порта. Если данные есть, то они отправляются обратно в com-порт и дополнительно в JTAG порт. А если нет, то происходит выход из процедуры.
********************************************************************/

.global UPDATE_UARTS
UPDATE_UARTS:

/* сохраняем регистры в стеке */

subi sp, sp, 28 /* резервируем область памяти в стеке*/
stw ra, 0(sp)
stw fp, 4(sp)
stw r15, 8(sp)
stw r16, 12(sp)
stw r17, 16(sp)
stw r18, 20(sp)
stw r19, 24(sp)
addi fp, sp, 28

movia r15, JTAG_UART_BASE /* помещаем в r15 адрес JTAG UART*/
movia r19, UART_BASE /* помещаем в r19 адрес com-порта*/

GET_CHAR:
ldwio r17, 0(r15) /* считываем слово из регистра данных JTAG UART*/
andi r18, r17, 0x8000 /* проверяем бит RVALID*/
beq r18, r0, GET_CHAR_UART /* если данных нет в буфере, то переходим на GET_CHAR_UART*/
andi r16, r17, 0x00ff /* выделяем байт данных из считанного из JTAG UART порта слова*/

PUT_CHAR:
ldwio r17, 4(r15) /* считываем слово из регистра управления JTAG UART*/
andhi r17, r17, 0xffff /* выделяем значение поля workspace*/
beq r17, r0, PUT_CHAR_UART /* если свободного места в буфере нет, то переходим на PUT_CHAR_UART*/
stwio r16, 0(r15) /* отправляем символ обратно в JTAG UART*/

PUT_CHAR_UART:
ldwio r17, 4(r19) /* считываем слово из регистра управления com-порта*/
andhi r17, r17, 0xffff /* выделяем значение поля workspace*/
beq r17, r0, GET_CHAR_UART /* если свободного места нет в буфере, то переходим на GET_CHAR_UART*/
stwio r16, 0(r19) /* отправляем символ в com-порт*/

GET_CHAR_UART:
ldwio r17, 0(r19) /* считываем слово из регистра данных com-порта*/
andhi r18, r17, 0xFFFF /* выделяем значение поля RAVAIL*/
beq r18, r0, NO_CHAR /* если новых данных нет, то переходим на NO_CHAR*/
andi r16, r17, 0x00ff /* выделяем байт данных из считанного из com-порта слова*/

ldwio r17, 4(r19) /* считываем слово из регистра управления com-порта*/
andhi r17, r17, 0xffff /* выделяем значение поля workspace*/
beq r17, r0, PUT_CHAR_JTAG /* если свободного места нет в буфере, то переходим на PUT_CHAR_JTAG*/
stwio r16, 0(r19) /* отправляем символ в com-порт*/

PUT_CHAR_JTAG:
ldwio r17, 4(r15) /* считываем слово из регистра управления JTAG UART*/
andhi r17, r17, 0xffff /* выделяем значение поля workspace*/
beq r17, r0, NO_CHAR /* если свободного места нет в буфере, то переходим на NO_CHAR*/
stwio r16, 0(r15) /* отправляем символ в JTAG UART*/
NO_CHAR:

/* извлекаем из стека регистры */

ldw ra, 0(sp)
ldw fp, 4(sp)
ldw r15, 8(sp)
ldw r16, 12(sp)
ldw r17, 16(sp)
ldw r18, 20(sp)
ldw r19, 24(sp)
addi sp, sp, 28

ret /* выполняем возврат из процедуры*/