nabbla (nabbla1) wrote,
nabbla
nabbla1

Category:

Вот же бандура!

Весь код ProcessFrame.asm целиком:

(если у кого не отображается форматирование, по идее должно отобразиться если из ленты зайти конкретно на эту запись)

;вариант без инструкции AUP (Acquire UPdate)		
;более сложен из-за этого: мы должны запоминать, есть ли за нами "должок" по заданиям на обработку
		
ProcessFrame proc			
				j		1	;отныне и во веки веков!	
				;ждём кадрового синхроимпульса
				ACQ		VSync		;"застревает" до тех пор, пока кадровый импульс не придёт
				;пропускаем "пустые строки" вверху изображения (нужно только для аналоговой камеры, в цифровой такой фигни нет вроде бы...)
				k		TopRows		;TopRows должны быть определены в основном файле												
	@@topRows:		ACQ		HSync
				kLOOP		@@topRows
				;теперь k=0, пусть так и остаётся, наши [X+k], [Y+k], [Z+k] будут означать [X],[Y],[Z], очень надо для списков
				
				;теперь начинаются информативные строки изображения, сейчас их 720, позже может стать 1024. 
				[SP+2j]		0		;в [SP+2] храним номер строки.
				
				JMP		@@FinalRange	;чтобы СНАЧАЛА отправить запрос, а потом уже смотреть результат, так почему-то удобнее					
				;можем заменить на JNO, переполнения здесь случаться не должно

; -------------------------------------------- через эту линию код не идёт (только перепрыгивает) ------------------------------------

				;НАЧАЛО ЦИКЛА ПО СТРОКАМ
	@@newRow:		[SP+2j]		Acc
				X		ActivePoints	;начинаем цикл по всем активным точкам
				
	@@ResetPending:		C		0		;уж явно не 0x8000			
	;даже если список пуст, первую половину цикла мы пройдём, она касается отрезка, предшествующего точке из списка
	@@ActPointsStart:	[SP+2j+1]	GPUL		;яркость самой яркой точки на отрезке
				[SP+1]		GPUH		;соотв. координата
				Acc		Threshold	;поставили задом наперёд, чтобы избежать Hazard'а	
				SUB		[SP+2j+1]	;вычитаем порог, положит. значение свидетельствует о новом найденном "пятне"						
				[SP]		@@MidCycle
				JL		TaskPending	;пятна нет, не торопимся заказывать отрезок
				;пятно есть. Надо проверить: не сливается ли оно с пятном слева или с пятном справа
				;X сейчас указывает на точку, расположенную слева (возможно, фиктивную, нам не важно)
				;коорд. текущей точки лежит в Acc, её яркость в C
				
				;первым делом, проверяем, не сливается ли с пятном слева
				Acc 		[SP+1]
				SUB		[X+2j+k]
				DIV2S		D1
				DIV2S		[X+1]
				Z		[X+k]		;указатель на пятно "справа"
				JL		@@LeftMerge	;сейчас процедура слияния заметно усложнилась, приходится 2 отдельные ветви
				
				;если дошли досюда, значит с пятном слева не сливаемся, надо проверить теперь на пятно справа						
				Acc		[Z+2j+k]
				SUB		[SP+1]
				DIV2S		D1
				DIV2S		[Z+1]
				JL		@@RightMerge
				
				;если попали сюда, значит слияния не было, и точку всё-таки надо добавить!
				;но сначала проверим, нет ли у нас "долга" за предыдущую точку?
				[SP]		@@NewBlob
						
	;по сути, процедура, хотя вызываем мы её, укладывая адрес возврата в [SP], БЕЗ ИНКРЕМЕНТА
	;в регистре С лежит некий флаг, подсказывающий, нужно ли нам выдавать задания на обработку отрезков,
	;которые мы не выдали сразу в страхе, что произойдёт слияние пятен, и надо будет дать несколько другое задание,
	;а команды AUP у нас нет
	;Регистр X указывает на то самое пятно, по которому надо выдать задания
	;самый быстрый способ ветвиться по содержанию C - это заносить туда либо -32768, либо любое другое значение
	;брать ABS и смотреть на переполнение
	;пусть -32768 будет означать "выполняй!"
	TaskPending proc
		ABS		C
		JNO		[SP]		;вот в чём прелесть стека без инкремента!
		;если дошли до сюда, заказываем отрезки на обработку
AcqNoCheck:	Acc		[X+2j+k]
		DIV2S		[X+1]	;теперь в аккмуляторе у нас X - Ceil(D/2)
		ACQ		Acc		;первый отрезок
		ADD		[X+1]	;а вот теперь X + Floor(D/2)
		ACQ		Acc		;второй отрезок
		JMP		[SP]
	TaskPending endp	
					
; ------------------------------ Через эту линию код не проходит (только перепрыгивает) -----------------------------------------------------------------

											
				;[SP+2j+1]=[SP+3] указывает яркость этой точки, [SP+2j]=[SP+2]: Y-координату, [SP+1]: X-координата
				;а куда вставить, указывает рег. X
				;в Acc содержится модуль разности между правой точкой и текущей, за вычетом двух радиусов
	@@NewBlob:		Y		Heap									
				[SP]		@@AfterListOp
				NOP		0		;на следующей строке ListOp нам нужен и Y уже готовый, и доступ к памяти, так что Hazard по-любому будет какой-то...
				
				;добавит в X новую, пока что пустую точку.
				
						
;взять первый элемент из списка Y и перецепить его в начало списка X
;значения X,Y остаются неизменными,
;при вызове должно быть k=0, затираем регистр Z, Acc и флаг знака
;адрес возврата хранится в стеке, как положено
	ListOp proc			
			Z		[Y+k]
			ABS		[Y+k]
MergeBlobCase:		[Y+k]		[Z+k]
			JO		OutOfMemory									
			[Z+k]		[X+k]
			[X+k]		Z
			NOP		0		;избежать Memory hazard (одновременный доступ на чтение и запись)
			JMP		[SP]
	ListOp endp						

; -------------------------------- Через эту линию код не идёт (только перепрыгивает) ------------------------------------------------------------------------
													
	;обнаруженная точка примыкает к пятну слева от себя. Возможно, потребуется слияние и с пятном справа от себя
	;X указывает на пятно слева, Z - на пятно справа 
	@@LeftMerge:	Acc		[Z+2j+k]	;взяли X-координату правого пятна
			SUB		[X+2j+k]	;вычитаем X-координату левого
			DIV2S		[Z+1]		;вычитаем радиус правого пятна
			DIV2S		[X+1]		;и радиус левого пятна
			SUB		D1		;и минимально допустимый диаметр пятна
			JL		@@MergeBlobs	;делаем слияние ПЯТЕН
			;если дошли до этого места, значит пятна сливать не нужно, достаточно лишь уточнить параметры левого пятна
			;увы, аккумулятор совсем другим набит, нужно "с нуля" начинать
			Acc		[SP+1]	;Acc = x, т.е координата обнаруженной точки
			SUB		[X+2j+k]	;вычитаем X, т.е Acc = x - X - разность между обнаруженной точкой и центром левого пятна
			DIV2A		[X+1]		;прибавили D/2, получаем Acc = x-X+D/2 
			[X+1]		Acc		;обновили диаметр левого пятна.
			NOP		0
			;теперь подкорректировать X-координату
			Acc		[SP+1]	;берём координату обнаруженной точки
			DIV2S		[X+1]		;вычитаем половинку диаметра
			[X+2j+k]	Acc
			JMP		AcqNoCheck	;если слева от нас есть РЕАЛЬНОЕ пятно (фиктивное мы трогать не можем, его X-координата "-32768", условие заведомо не выполнится),
			;значит проверять "есть ли за нами должок" не нужно - точно есть!						
						
; ---------------------------------- Через эту линию код не идёт (только перепрыгивает) ---------------------------------------------------------------------------
	@@MergeBlobs:	Acc		[X+1]
			ADD		[Z+1]
			[X+1]		Acc	;диаметр равен сумме диаметров
			ZAcc		RoundZero
			DIV2A		[X+2j+k]
			DIV2A		[Z+2j+k]
			[X+2j+k]	Acc	;среднее арифметическое от центров точек
			;теперь удаляем правое пятно
			Y		X
			X		Heap
			[SP]		@@EndOfCycle
			JMP		MergeBlobCase
						
; ---------------------------------- Через эту линию код не идёт (только перепрыгивает) ----------------------------------------------------------------------------												
						
	@@RightMerge:	ADD		[Z+1]	;в аккумуляторе лежало x-X-D/2-D1/2. Теперь прибавили D, получили x-X+D/2-D1/2
			DIV2A		D1	;прибавили D1/2, получили x-X+D/2 - новый диаметр пятна
			[Z+1]		Acc	;ага, записали.
			;теперь подкорректировать X-координату
			DIV2		Acc
			ADD		[SP+1]
			[Z+2j+k]	Acc
	;а теперь с чистой совестью отправляем задание на обработку точки
			JMP		TaskPending

; --------------------------------- Через эту линию код не идёт (только перепрыгивает) ---------------------------------------------------------------------------

			;если добрались досюда, значит памяти хватило. X содержит адрес указателя на новую точку, а вот Z содержит адрес самой точки!
			;наполним её содержимым!						
			;на первую строку всегда попадаем в результате прыжка!
	@@AfterListOp:	[Z+2j+k]	[SP+1]	;X-координата точки
			X		Z		;для дальнейшей работы (после MidCycle)... Чтобы мы не взялись за только что добавленную точку!
			[Z+2j+1]	[SP+2j]
			[Z+1]		D1		;гориз. размер точки (будет расти при слияниях, а поначалу D, т.к мы сюда включаем и )
			;и теперь ещё нужно выдать задания на обработку, СРАЗУ.
			[SP]		CALL(AcqNoCheck)

	;теперь надо посмотреть - а есть ли точка? Или мы уже прошли последний отрезок между точками?
	;но у нас в конце списка правая фиктивная точка - её надо игнорировать!			
	;улучшенный вариант:
			%CheckHazards OFF
	@@MidCycle:	X		[X+k]
			Y		X
			%CheckHazards ON
			ABS		[X+k]
			JO		@@FinalRange
									
	;запрашиваем отрезок, где ожидаем точку. Первой придёт яркость, как всегда
	@@EndOfCycle:	[SP+2j+1]	GPUL			;яркость
			[SP+1]	GPUH			;пока не забыли - сразу второе слово запрашиваем (коорд точки), чтобы не сбить весь FIFO 
			C		Nil			;по умолчанию ставим метку "за нами должок!"				
			;входит ли этот отрезок в уже найденное пятно?
			Acc		[SP+1]		;координата точки (из списка)
			DIV2S		[X+1]			;прибавить половинку её диаметра
			SUB		[X+2j+1]		;вычесть текущий номер строки
			JL		@@ActPointsStart
			;возможно, мы ещё не очень далеко ушли от пятна, и как оказывается, пятно здесь продолжается
			SUB		D1
			JL		@@RemoveBlob	;уже далеко, пора его удалять!
			;да, мы ещё близко. Но превышает ли яркость порог?
			Acc		Threshold		;чем выше значение - тем ЧЕРНЕЕ
			SUB		[SP+2j+1]		;поэтому если получится значение меньше нуля - значит точка тусклая, расширять пятно не стоит
			JL		@@RemoveBlob
			;раз оба раза перехода не было, значит пора пятно сдвигать немного вниз!
			;просто прибавим единицу и к диаметру, и к Y-координате
			Acc		[X+2j+1]
			ADD		1
			[X+2j+1]	Acc
			NOP		0
			Acc		[X+1]
			ADD		1
			[X+1]		Acc
			JMP		@@ActPointsStart
				
; ------------------------------------------ Через эту линию код не идёт (только перепрыгивает) ------------------------------------------------------------------
										
			;пора эту точку перетаскивать в другой список... 
	@@RemoveBlob:	X		AllPoints
			[SP]		CALL(ListOp)
			X		Y			;теперь [X] ссылается уже не на обработанную и удалённую точку, а на следующую, так что всё верно
			JMP		@@ResetPending	;ура...					
			;до PixelsProcessed проскрипели, теперь последний участок запросить на обработку, и синхроимпульс для кучи
				
; ------------------------------------------ Через эту линию код не идёт (только перепрыгивает) ------------------------------------------------------------------												
				
	@@FinalRange:	ACQ		WholeRow
			ACQ		HSync
			Acc		[SP+2j]
			SUB		ImgHeight
			ADD		ImgHeightP1
				
		;на @@NewRow мы переопределим и Y, и X, и [SP], так что можно...
			;это приготовления уже к transfer, но чтобы избежать хазарда, пришлось сюда перенести. Вообще, это нам сжирает лишних 3600 тактов = 144 мкс. 
			Y		ActivePoints	;это заведомо фиктивная, и указывает либо на другую фиктивную, то ли на реальную
			X		AllPoints
			Y		[Y+k]			;это уже может быть реальная точка, а может быть правой фиктивной
			[SP]		@@Transfer																				
				
			JL		@@newRow
			;усё, отстрелялись...
			


	@@Transfer:	ABS		[Y+k]
			JNO		ListOp		;если [Y+k]=NULL, значит пора закругляться											
ProcessFrame endp


Ещё чуть-чуть это дело причесал, уменьшив размер с 248 до 245 слов кода после компиляции. Конретно этот файл (алгоритм обнаружения точек) компилируется в 124 слова данных, если нигде не обсчитался (оно не компилируется "само по себе" из-за огромного количества констант заданных "выше", а при общей компиляции я пока не получаю "разбивки по файлам", хотя можно было бы сделать...)

Один из обнаруженных Hazard'ов удалось легко исправить, поменяв порядок строк. Второй - ну никак, аккурат перед процедурой ListOp. Там всего 2 строки, и в каком порядке их не ставь - не склеивается каменный цветок.

Но ещё я самую нижнюю часть, "Transfer", основательно обкромсал. Было так:
		;надо ещё все оставшиеся в ActivePoints точки "перецепить" в AllPoints
		Y	ActivePoints	;это заведомо фиктивная, и указывает либо на другую фиктивную, то ли на реальную
		X	AllPoints
		Y	[Y+k]			;это уже может быть реальная точка, а может быть правой фиктивной
@@Transfer:	ZAcc	RoundZero
		SUB	[Y+k]			;если [Y+k]=NULL, значит пора закругляться
		[SP++]	@@Transfer
		JL	ListOp
		NOP	[--SP]		;раз не было вызова - возвращаем стек в исходное состояние	


Но поскольку ListOp перешёл на чуть другую "конвенцию" - без инкремента-декремента стека, просто в [SP] передавать адрес возврата, то удалось избавиться от самой нижней строки (она для того и была, чтобы исправить положение стека), а "[SP++] @@Transfer" делать не в цикле, а перед ним. И наконец, мы вспомнили, что NULL можно проверять одной командой ABS [Y+k] (только число -32768 при этом уйдёт в переполнение).

В итоге получилось бы так:
		Y	ActivePoints	;это заведомо фиктивная, и указывает либо на другую фиктивную, то ли на реальную
		X	AllPoints
		Y	[Y+k]		;это уже может быть реальная точка, а может быть правой фиктивной
		[SP]	@@Transfer																				
@@Transfer:	ABS	[Y+k]
		JNO	ListOp		;если [Y+k]=NULL, значит пора закругляться	


Но опять Hazard'ы пошли, и как не поменяй местами верхние 4 строки, либо Hazard останется, либо работать станет неправильно!
То есть, между "Y ActivePoints" и "Y [Y+k]" должна быть помещена какая-то другая команда, иначе [Y+k] обратится по старому значению Y. А перед командой "ABS [Y+k]" нельзя присваивать значение Y по той же причине (не успеет обновится), но и записывать что-то в память перед ней нельзя, потому как совпадёт во времени с выборкой [Y+k]! А если "[SP] @@Transfer" поменять местами с "X AllPoints", то между второй и третьей строкой возникнет конфликт по шине памяти (одновременное чтение и запись), тоже нельзя!

Поэтому единственный способ обойтись без лишнего NOP, который вставит компилятор - эти 4 строки поместить в самый конец цикла по строкам. Возможно, не самое лучшее решение - они будут 719 раз исполняться "вхолостую", теряя почти 3600 тактов (145 мкс), но в целом это будет "размазано" по всему кадру, мы потеряем по 200 нс в конце каждой строки, что погоды сделать не должно...


Самое время делать ставки, сколько времени уйдёт на отладку этого добра?

Poll #2107906 Алгоритм обнаружения ярких пятен на изображении

Сколько времени займёт его отладка?

неделя или меньше
3(33.3%)
1-2 недели
1(11.1%)
свыше 2 недель
5(55.6%)
он так и не заработает
0(0.0%)


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

Recent Posts from This Journal

  • О вытягивании себя из болота по методу Мюнхгаузена

    Всё готовлюсь к встрече с представителями РКК Энергия, нужно убедить их в моём способе определения положения ВидеоИзмерителя Параметров Сближения на…

  • Ремонт лыжных мостиков

    Вернулся с сегодняшнего субботника. Очень продуктивно: отремонтировали все ТРИ мостика! Правда, для этого надо было разделиться, благо народу…

  • Гетто-байк

    В субботу во время Великой Октябрьской резни бензопилой умудрился петуха сломать в велосипеде. По счастью, уже на следующий день удалось купить…

  • А всё-таки есть польза от ковариаций

    Вчера опробовал "сценарий", когда варьируем дальность от 1 метра до 11 метров. Получилось, что грамотное усреднение - это взять с огромными весами…

  • Так есть ли толк в ковариационной матрице?

    Задался этим вопросом применительно к своему прибору чуть более 2 недель назад. Рыл носом землю с попеременным успехом ( раз, два, три, четыре),…

  • Big Data, чтоб их ... (4)

    Наконец-то стряхнул пыль с компьютерной модели сближения, добавил в неё код, чтобы мы могли определить интересующие нас точки, и выписать…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 3 comments