nabbla (nabbla1) wrote,
nabbla
nabbla1

Category:

Тестируем разогнанный QuatCore :)

Похоже, работает!

По крайней мере, первые 10 команд исполнились образцово-показательно!


11-я команда проглючила, но процессор в этом не виноват, виноват программист (ваш покорный слуга), прохлопал RAW Hazard, и отчасти транслятор, тоже его прохлопал, ну и программист этого транслятора (ваш покорный слуга).


Мы решили вернуться к аффинному алгоритму, и начать с его первой части - нахождению самой отдалённой точки из четырёх.

Хотел под спойлером объявление сегмента данных, но оно и само по себе громоздкое, а когда оно обработано через hilite.me, то ЖЖ вообще не хочет запись публиковать - слишком большая! Ладно, если надо, потом приведу.

Куда интереснее код программы, аж 37 слов (74 байта):

.code
;основная программа
main proc
;эта команда может вращаться в бесконечном цикле, пока подана единица на reset
;и поэтому PC остаётся нулевым. Но она не страшная, ничего не испортит!
		SP		StackAdr
		NOP		0		;избежать Write Hazard: только здесь в SP запишется новое значение!
		SP		[SP]

;возможно, при занесении значения в SP сделаем сдвиг влево на пару позиций,
;чтобы разместить стек туда, куда мы не достаём через Immediate значения

;"реальный код" - запустим "алгоритм захвата"
;в действительности, такое деление на процедуры может оказаться лишним, но для отладки это довольно удобно
;по F8 выполняем всю процедуру, а по F7 входим в неё
		CALL		AffineAlgorithm
@@endless:	JMP		@@endless
main endp

		NOP		0	;чтобы было ясно, когда команды неверные
		NOP		0	;(мы до этого прыгали "через 1"
		NOP		0	;и начинало казаться что всё как надо)

;Аффинный алгоритм, он же алгоритм захвата
;содержание регистров произвольное, сохранять его не требуется
;исходные данные - Points2D - координаты 4 точек на фотоприёмной матрице,
;неупорядоченные.
AffineAlgorithm 	proc

	;алгоритм переупорядочивает точки "на месте", в порядке МДД3-МДД1-МБД-МДД2 (т.е против часовой стрелки от МДД3)
	Associate4Points	proc
	
		;состояние регистров неизвестно (за искл. PC и SP)
		FindMDD3	proc
		;перво-наперво, находим МДД3
		;как точка, сумма квадратов расстояний до которой максимальна (т.е самая отдалённая)
		;для каждой точки считаем сумму квадр. расстояний до всех остальных, в т.ч до самой себя (это 0, зато код упрощается)
		;внешний цикл (по строкам) - перем. j
		;внутр. цикл (по столбцам) - перем. i
		;самый-самый внутр. цикл - по индексу перем (X или Y) - перем k
				Y		Points2D
				[SP+1]		0	;максимальная отдалённость, инициализируем нулём
				;рег. Y будет хранить индекс точки с макс. отд. её иниц. не надо-заведомо на одной из итераций запишется
					
				;ijk		-61	;j=3, Inv=1 (пригодится в дальнейшем!)
				;попытаемся пока обойтись без ijk, уж больно он толстый
				j		3
		@@j_loop:	k		1	;также от 0 до 3, чтобы все расстояния просуммировать
				[SP]		0	;здесь будет храниться текущий максимум
		@@k_loop:	i		3	;от 0 до 1, т.е значения X и Y
		;а теперь непосредственно прибавляем к акк. ([X+2i+k]-[X+2j+k])^2
		@@i_loop:	Acc		[Y+2i+k]	;загрузили одно значение
				SUB		[Y+2j+k]	;вычитаем второе
				SQRD2		Acc			;возводим в квадрат
				ADD		[SP]		;прибавляем к пред. значению
				[SP]		Acc
				iLOOP		@@i_loop		;теперь то же самое для второй координаты
				kLOOP		@@k_loop
		;хорошо, нашли сумму расстояний от точки j до всех остальных
		;она лежит в Acc и в [SP]
		;нужно сравнить с самым большим, и если больше - заменить
		;самое большое значение мы храним в [SP+1],
		;а его индекс - в [SP+2]
				SUB		[SP+1]	;можно и "пожертвовать" значением в Acc,
		;т.к в [SP] лежит точно такое же!
				JL		@@skip
		;дистанция оказалась больше - надо обновить максимум и его индекс
				[SP+1]		[SP]	;двухпортовая память-это зашибись!
				X		j
		@@skip:		jLOOP		@@j_loop
		;по окончании этих циклов, у нас в Y лежит индекс точки, которая является МДД3
		;осталось эту точку поменять местами с точкой под индексом 0...
		;здесь i=j=k=0 (все циклы завершились!)
				i		X
				X		Points2D
				Z		Points2D
				CALL		SwapPoints	;потёрли текущий максимум (лежал в [SP])-и хрен с ним
		
		;на этом первая часть марлезонского балета закончена.
		FindMDD3	endp
	Associate4Points endp

			JMP			[--SP]
AffineAlgorithm 	endp
;состояние регистров по окончании работы:
;X = AfTransf,
;Y = Rx,
;Z = Exp
;i=k=0,
;j=Exp
;Inv=1

;меняет местами точку с базой Z, инд. j  и базой X, инд. i
;изменяет регистр k (по окончании выходит 0), и C, Acc
SwapPoints	proc
		k		1
@@swap:		C		[Z+2j+k]
		[Z+2j+k]	[X+2i+k]
		[X+2i+k]	C
		kLOOP		@@swap
		JMP		[--SP]
SwapPoints	endp	


Пора начать смотреть команда за командой, как это выполняется.

Не стали пока сильно усердствовать с Reset: первая команда уже начинает исполняться, ну и пускай. Чаще всего там лежит совсем безобидная команда, от непрерывного выполнения которой ничего плохого не случится. (впрочем, выполнение команды 00 - не есть хорошо, мы в своей бесконечной мудрости сунули по этому адресу команду OUT! Да, всё-таки нужен discard по ресету...) В нашем случае - инициализация стека,
SP  StackAdr


Мы видим, что к второму такту на шине данных установилось значение 0xFFC7 (собственно, StackAdr), а также в DestAddr появилась команда 0xFD ("SP"). На третьем такте она уже выполняется, а также на четвёртом и на пятом - это первый, на котором Reset=0, то есть процессор запустился в нормальном режиме. К следующему такту PC=1, но мы продолжаем выполнять "нулевую" команду. И наконец, когда PC=2, в SrcAddr поступает код первой команды, а в DestAddr всё ещё осталась нулевая:

SP  0


И на шине данных всё ещё лежит значение StackAdr = 0xFFC7. Так что мы ещё один разок делаем это присвоение. По сути, именно отсюда началась корректная работа. А ещё мы героически защёлкиваем на шину данных значение 0, выполняя "полукоманду" по SrcAddr.

К следующему такту PC=3, а в DestAddr и SrcAddr мы видим такую пару:

NOP [SP]


Мы добавили в программу строку
NOP 0


из-за Write Hazard: если бы не долгая задержка из-за reset, у нас в предыдущем такте была бы пара:
SP  [SP]

В SP наконец-то мы записали бы значение StackAdr, но на этом же такте сформировали бы эффективный адрес [SP], используя его СТАРОЕ ЗНАЧЕНИЕ, наверняка НОЛЬ, что не соответствует нашему замыслу. Поэтому вставили команду "ничего не делать". Поэтому сейчас у нас пара:

NOP [SP]


Команда [SP] запросила остановку (stall), и действительно, выполнение задержалось на ещё один такт. Как мы и хотели, PC и SrcAddr и DestAddr на этот такт "замерли", остались на своих местах. На шине данных все нули - на первом такте это защёлкнутый "ноль", на втором - видимо содержание памяти по нулевому адресу.

Поехали дальше. PC=4, и на выполнение поступает пара:
SP   CALL0


На шине данных к тому времени появляется значение из памяти, [SP] = 0x005F, именно оно "защёлкивается" в SP - стек наконец-то инициализирован. Напомню, что мы можем элементарно указать первые 64 и последние 64 адреса в памяти (всего 128), а к центральным 128 ячейкам "добираться" сложнее, поэтому именно туда мы стремимся положить стек, вот и запихиваем его в два этапа. Ничего страшного, я считаю...

И в то же самое время выполняется команда CALL0 - она осуществляет переход на процедуру AffineAlgorithm, а также кладёт на шину данных адрес возврата, собственно, PC=4.

Смотрим следующий такт: имеем PC=8 (переход успешно выполнен), DataBus=4 (туда попал PC=4) и следующую пару:

[SP++] @@endless


При этом "загорелось" SrcDiscard=1, т.е нас предупреждают, что @@endless выполнять не надо. Правда, это команда для модуля QuatCoreImm (Immediate, непосредственное значение), заведомо без побочных эффектов, поэтому SrcDiscard в реальности никуда не идёт, и мы всё-таки выдаём на шину данных @@endless = 4, ничего страшного в этом нет.

Тем временем, в стек заносится адрес возврата. По строке MemAdr = 0x5F мы видим, что стек инициализировался правильно!

Смотрим следующий такт: PC=9, DataBus=4 (теперь от @@endless) и следующая пара на выполнение:
JMP  Points2D


Если этот JMP выполнится - у нас серьёзные неприятности - сильно раньше времени мы войдём в финальный бесконечный цикл
@@endless:  JMP @@endless

которым мы хотим завершить выполнение программы.

Но у нас "загорелся" DestDiscard=1, поэтому выполнять JMP мы не будем :) А вот значение Points2D=0x18 положим на шину данных.

Смотрим следующий такт: PC=0xA (действительно, прыжка не произошло), DataBus=0x18 и следующая пара для выполнения:
Y   0


Действительно, в регистр Y запихиваем значение 0x18 - координаты точек с фотоприёмной матрицы, а на шину данных заносим нолик.

Следующий такт: PC=0xB, DataBus=0 и следующая пара для выполнения:
[SP+1]  3


Заносим в [SP+1] нолик (это максимальная дистанция от точки до всех остальных, которая перед начало цикла инициализируется нулём). Мы видим, что MemAdr = 0x61, то есть при занесении адреса возврата мы действительно прибавили единичку к SP, а сейчас ещё одну добавили к эффективному адресу (SP не изменился разумеется). А на шину данных кладём тройку.

Следующий такт: PC=0xC, DataBus=3 и следующая пара для выполнения:
j    1


Всё нормально, в j поступает тройка, на шину данных - единичка.

Следующий такт: PC=0xD, DataBus=1 и пара для выполнения:
k    0


Жизнь идёт своим чередом: в k поступает единичка, на шину данных - нолик.

Следующий такт: PC=0xE, DataBus=0 и очередная пара:
[SP]  3


В [SP] заносим нолик - это текущая сумма квадратов дальностей от точки номер j до всех остальных. MemAdr=0x60 - всё верно. А на шину данных поступает тройка.

Следующий такт: PC=0xF, DataBus=3 и очередная пара:
i    [Y+2i+k]


В регистр i мы занесли тройку, тем самым окончив инициализацию ТРЁХ вложенных циклов :) А [Y+2i+k] - это наконец-то начало реальных вычислений. Поскольку это обращение к памяти, мы замираем на один такт.

При этом можно заметить одну нехорошую вещь: мы сформировали эффективный адрес для старого значения i, т.е 0x18+2*0+1 = 0x19, и начали запрашивать память именно по этому адресу. И только к следующему адресу у нас защёлкнулось i=3, и мы запросили 0x18+2*3+1=0x1F, но было уже поздно, на шину данных попало значение для i=0, запоров нам все вычисления. Это так называемый Read-After-Write Hazard, или RAW.

Процессор не виноват - мы и не просили его самостоятельно разрешать подобные Hazard'ы, и сами пропустили. Надо будет научить транслятор обнаруживать подобное и громко материться.


Самое смешное, что нам даже не нужно вставлять NOP, ведь код выглядит так:
		@@k_loop:	i		3	;от 0 до 1, т.е значения X и Y
		;а теперь непосредственно прибавляем к акк. ([X+2i+k]-[X+2j+k])^2
		@@i_loop:	Acc		[Y+2i+k]	;загрузили одно значение
				SUB		[Y+2j+k]	;вычитаем второе
				SQRD2		Acc			;возводим в квадрат


Мы можем два операнда поменять местами:
		@@k_loop:	i		3	;от 0 до 1, т.е значения X и Y
		;а теперь непосредственно прибавляем к акк. ([X+2i+k]-[X+2j+k])^2
		@@i_loop:	Acc		[Y+2j+k]	;загрузили одно значение
				SUB		[Y+2i+k]	;вычитаем второе
				SQRD2		Acc			;возводим в квадрат


Вот теперь должно всё выполниться правильно.


Ща перекомпилим программу и пересинтезируем QuatCore - и продолжим отладку.
Tags: ПЛИС, программки, работа, странные девайсы
Subscribe

  • Огари разговаривают

    Сегодня по пути на работу встретил огарей прямо в Лосином острове, на берегу Яузы. Эти были на удивление бесстрашны, занимались своими делами, не…

  • Очень запоздало о лыжах

    Давненько (с 16 февраля) не писал о лыжах, хотя каждую неделю катался. Первый раз - на Гремячий и назад, с разведкой нового пути к маленькому…

  • Воскресная прогулка в сторону Гремячего

    Не было особенных планов "доехать во что бы то ни стало", хотел просто посмотреть "что вообще творится"? после обильных снегопадов в пятницу-субботу.…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments