Imax9
NEWS   ARTICLES   MINIMIG   FILES   ABOUT

ПРЕ-обработка-РЫВАНИЯ.

Предлагаю вам перевод цикла The EightThirtyTwo ISA Part 9: INTER-handling-RUPTS автора Alastair M. Robinson.

Сумев заставить процессор работать достаточно хорошо, чтобы запустить программу Hello World, я с тех пор уговорил его запустить программу декомпрессии LZ4, которую я опубликовал неделю или две назад, и был приятно удивлен, обнаружив, что он успешно работает на частоте 133 МГц на моей плате DE2.

Я размышлял о том, как лучше всего обрабатывать прерывания на этом процессоре, и понял, что я загнал себя в угол, используя регистр tmp в качестве регистра ссылки при ветвлении; это означает, что без добавления какого-либо временного регистра хранения или логики стека, особенно для этой задачи, я не могу изменить поток программы извне – например, в ответ на прерывание – без потери содержимого регистра tmp.

Даже если я добавлю такой регистр хранения или операцию стека, у меня также нет возможности отменить этот процесс в конце обработчика прерываний без добавления дополнительной инструкции. У меня все еще осталось немного места для кодирования инструкций с нулевым операндом, но я все равно хотел бы избежать этого, если смогу, так как это несколько увеличит размер логики. Я также предпочел бы не добавлять какие – либо операции стека, потому что (а) это приведет к принудительному использованию определенного регистра для стека, который в настоящее время полностью необязателен, и (б) я все равно потеряю содержимое tmp, когда мы восстановим счетчик программы, если я не добавлю инструкцию на подобии “rts” .

Решение, которое я выбрал на данный момент, сделка между потенциальным временем отклика для простоты и отсутствием дополнительной логики для обработки прерываний.

Проблема, которую необходимо решить, заключается в том, что регистр tmp уничтожается при обслуживании прерывания, поэтому я решил полностью обойти проблему, разрешив прерывания только тогда, когда следующая инструкция собирается записать в tmp. На практике это означает, что конвейер перехватит первый запуск инструкций li или инструкций mt. В принципе ld, ldinc или lddinc также могут быть перехвачены, но я еще не пробовал этого. При возвращении из процедуры прерывания перехваченная инструкция будет выполняться, записывая новое значение в tmp, и, таким образом, ее старое содержимое будет потеряно, но это не будет иметь значения.

Перехваченные инструкции заменяются операциями, которые помещают счетчик программ (с битами состояния процессора, закодированными в верхние четыре бита) на оба входа ALU, операция ALU установлена в xor, первый выход установлен для записи результирующего нуля в r7, а второй выход-для записи переданного значенияв tmp.

Таким образом, при прерывании мы переходим к нулевому местоположению с установленным нулевым флагом. Это сводит к минимуму дополнительную логику, необходимую для поддержки прерываний, не требует дополнительных регистров сохранения значений, логики стека или даже вектора прерывания; начальная точка входа и вектор прерывания совместно используются как нулевое местоположение, с нулевым флагом, установленным при прерывании и очищенным при включении питания.

Однако при сохранении и восстановлении обратного адреса требуется довольно деликатный регистровый танец; поскольку при возвращении нам нужно выполнить инструкцию, которую мы перехватили, чтобы вызвать прерывание, мы должны уменьшить обратный адрес на единицу, прежде чем перейти к нему. Код запуска и обработки прерываний выглядит следующим образом:

vector: // Инициализация кода и вектора прерываний. В прерывании флаг zero должен быть установлен.
	cond	NEQ
		li IMW1(PCREL(entry-1))
		li IMW0(PCREL(entry))
		add	r7

interrupt:	// Мы попадаем сюда, если это прерывание, а не событие включения питания.
	exg	r6	// Обмен указателя стека с адресом возврата
	stmpdec	r0	// Записать r0 в стек
	stmpdec	r6	// Записать адрес возврата в стек.
	stmpdec	r1	// Записать другие повреждаемые регистры в стек
	mr	r6	// Вернуть указатель стека в r6.

	// Обработка прерывания здесь...

	ldinc	r6	// Восстановить r1 и другие повреждаемые регистры  - но записанные ранее...
	mr	r1

	ldinc	r6	// Переместить адрес возврата в temp
	mr	r0		// и оттуда в r0
	li	IMW0(-1)
	add	r0		// Уменьшить адрес возврата
	ldinc	r6	// Переместить записанное значение из r0 в tmp.
	exg	r0		// Обменять tmp и r0, восстанавливая r0 и помещяем скорректированный адрес возврата в tmp
	mr	r7		// Переход на скорректированный адрес возврата.

entry:
	// Главный код программы...

Если это выглядит запутанным или многословным… хорошо… возможно, это так – но учтите, что это всего лишь 18 байт!

У меня есть эквивалент моей более ранней демо-версии прерывания ZPU, которая просто чередуется печатью слов “тик” или “так” в ответ на прерывание таймера. Единственное небольшое разочарование заключается в том, что моя логика перехвата является частью критического пути и, таким образом, снижает fmax демонстрационной программы примерно до 129 МГц на DE2.


Адрес для контактов : imax9@narod.ru

Если вам понравились мои работы и вы желаете поддержать сайт - сделайте дотацию.

При копировании статьи – обязательна ссылка на авторство и источник. Без разрешения автора копирование запрещено.

© Максим Ильин 2022г.

Яндекс.Метрика