nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

Аффинный алгоритм + информационный обмен, код

Память для этого монстрика мы разметили. Теперь ещё код подправить. Ранее у нас вообще никакого ввода-вывода для этого алгоритма не предусматривалось: программа стартует с нулевого адреса, обрабатывает заранее лежащие в памяти данные - и уходит в бесконечный цикл. Да и выбор, какой из алгоритмов использовать, для ближней дистанции или для дальней, мы делали сами, "комментируя" один прыжок. А надо худо-бедно заставить его работать самостоятельно.

И конечно, на таких более простых вещах QuatCore начинает жутко "тупить", в смысле что требовать кучу кода на простейшие операции. У нас же до сих пор нет флага нуля, обходились без него как-то! И соответственно, прыжков JE/JNE тоже нет, только JL и JGE, работающие по одному лишь знаку...


Вот посчитать аффинную матрицу, кватернионы перемножить, отнормировать, отсортировать точки против часовой стрелки или "вдоль оси" - здесь всё довольно-таки элегантно, благодаря широким возможностям адресации, простой организации циклов по 3 разным переменным (не как в x86, где LOOP работает только для регистра cx / ecx / rax), арифметическим операциям "с накоплением".

А проверить, сколько найдено точек, если найдено 4 - прыгнуть на процедуру "дальней дистанции", если 8 - "ближней дистанции", в любом другом случае остаться в режиме "поиск" - вот это прям нереальная задача!

Аж целую процедуру написал, IsZero:
;после этой процедуры, JL означает "прыгнуть если РАВНО НУЛЮ", а JGE - "прыгнуть если НЕ РАВНО"
;и ещё в акк. загружаем [X+1], потому что "так надо" :)
IsZero proc
	ABS	Acc
	SUB	1
	Acc	[X+1]
	JMP	[--SP]
IsZero endp


Вот так приходится изворачиваться, чтобы проверить на "ноль": берём модуль от числа и вычитаем единицу. Ответ может быть отрицательным в одном-единственном случае, когда изначально был ноль, поэтому JL будет означать "ноль", JGE: "не ноль". И сюда же сунули строку, чтобы повторно занести в аккумулятор исходное, "не тронутое" значение, которое мы решили вытаскивать из [X+1].

Приведём самое начало программы:
start:	SIO	UART		;на самом деле, вместо UART сейчас всунуто ожидание КС "синхронизация"
						;поставили перед стеком, чтобы избежать I/O hazard 

	SP	Stack		

	X	RR_PRF_NO	;чтобы по [X+1] обратиться к RR_Count - количество обнаруженных точек	(поставлено строкой ранее, избежать RAW hazard)

	Acc	IN		;останавливаемся, пока нас не "запустят". Так СИИЛЬНО удобнее отлаживать будет

;начало алгоритма захвата
;проверяем количество обнаруженных точек, т.е RR_Count
;пока что нас устраивает 4 (дальняя дистанция) либо 8 (очень ближняя дистанция, где кроме МБД больше нет нифига).
;Промежуточный вариант самый сложный, пока нет уверенности, как его лучше всего исполнить.
;Можно, к примеру, на этапе обнаружения выставить большой D1, тогда МДД и МБД предстанут как единое целое. Хорошо для больших дальностей
;но если уже часть МДД вышла за поле зрения - работать не сможем, там надо "поймать" кластер из 8, и отбросить более мелкие кластеры из 7. 

	Acc	[X+1]
	SUB	4
	CALL	IsZero	;проверяем RR_Count == 4 (что означает дальнюю дистанцию)
	JL	FindMDD3	;переходим на алгоритм захвата дальней дистанции
	SUB	8
	CALL	IsZero	;проверяем RR_Count == 8 (самый простой вариант ближней дистанции)
	JL	Find2Points	;переходим на алгоритм захвата ближней дистанции
	;ещё возможен вариант 7 точек, который улучшенный алг. ближ. дистанции, но пока не реализован
	;и ещё вариант ДОХРЕНАЛИОНА точек, когда на средней дистанции разрешаются уголковые отражатели как в МБД так и в МДД. Вот это вилы, пока не делали

	;если попали сюда - устанавливаем "режим поиска", и все признаки достоверности данных по нулям. 
	X	DA_header
	[X+1]	0		;режим поиск, флаги достоверности данных и стереорежима: нулевые
	JMP	start		;пока так... 


Как водится, все команды "перепутаны местами" - регистр X инициализируется заранее, чтобы не было одновременного чтения и записи в него. Также заранее выбирается устройство ввода-вывода, поскольку при выполнении SIO (Select I/O) одновременно с IN, вход пойдёт с ранее выбранного устройства.

В итоге: инициализируем указатель стека, куда без этого. Регистр X указывает на RR_PRF_NO, так что X+1 указывает на RR_Count - количество обнаруженных точек.

Устройство ввода-вывода UART я хочу отсюда выкинуть (вместо него "контроллер информационного обмена", работающий автономно, процессор не нагружающий), а взамен поставить простейшую хреновину. Вызвав команду IN, мы "застрянем" на ней до тех пор, пока не придёт команда "Синхронизация (с СД)" - таким способом на первых порах будем "синхронизироваться". При этом реально что-то подавать на вход шины данных пока не обязательно, значение будет иметь только сам факт приёма данного сообщения.

Команда OUT на это устройство будет подавать сигнал Mark на "часы реального времени", т.е запишет на их выход текущее время, поставит метку, когда мы окончили работу. Это тоже не вполне соответствует замыслу, когда отмечается середина экспозиции кадра, но пока что, проверки ради, сделаем попроще. Пока же и кадра никакого нет - работаем по присланным точкам...

Если запустить эту программу, она наглухо "застрянет" на строчке "Acc IN". В это время мы можем "поиграться" с протокольным контроллером - задать исходные данные, посмотреть, что он "по умолчанию" подаёт на выход, а когда наиграемся - выдать "Синхронизация (с СД)" с нулевым словом данных - это установит часы реального времени в ноль и наконец-то позволит программе пойти дальше.

И сразу же мы начинаем проверять количество обнаруженных точек. Загружаем это число в аккумулятор, вычитаем 4 и вызываем IsZero. Процедура IsZero выставит флаг знака: "-" если ноль, "+" в противном случае, а также повторно загрузит количество обнаруженных точек в аккумулятор, для повторной проверки. Если действительно 4 точки, то начинаем выполнять алгоритм "дальней дистанции", где первым делом ищется наиболее отдалённая точка (МДД3), затем оставшиеся сортируются против часовой стрелки, и так далее.

В противном случае проверяется случай 8 точек. Если так и есть - запускаем алгоритм "ближней дистанции". В противном случае пока что ничего не запускаем, остаёмся в режиме "поиск", сообщаем об этом "флажками" в массиве целевой информации - и возвращаемся к началу, по сути ждать следующей синхронизации.

При этом много вариантов осталось за кадром. При работе по мишени ближней дистанции, если она отвёрнута в сторону почти на 30°, две точки могут слиться в одну, и вместо 8 получится 7. В общем-то, такой вариант я тоже рассматривал, но на ассемблере пока не реализовал.

И второй, куда более нехороший вариант - "промежуточный", что-то вроде такого:


Одна из мишеней дальней дистанции уже вышла за поле зрения, а те, что остались, разрешаются в отдельные уголковые отражатели. В такой ситуации, если мы только "открыли глазки" (не имеем ни малейшего понятия, где находимся), надо бы из всего этого нагромождения выделить одну лишь МБД (Мишень Ближней Дистанции) и работать уже с ней. Этого я тоже пока не реализовал, но хотя бы понятно, куда всё это всунуть.

Следующий кусок кода выполняется после окончания аффинного алгоритма:

;состояние регистров к этому моменту
;X = Exp (начало вектора параллельного переноса)
;Y = Rx (вектор параллельного переноса, в "пикселях")
;Z = Ty (вектор параллельного переноса, в метрах)
;i=k=0
;j=Exp
;Inv=1
;C, Acc - наверное пофиг
;в [SP] лежит "мантисса" масштаба
;в [SP+1] - либо 3, если дальняя дистанция, либо 5, если ближняя

;если дошли досюда, значит как-то свою позицию определили. 
;давайте перепишем найденный вектор в массив целевой информации
;(позже, в зависимости от UseCartes, можем решить преобразовать его в сферические координаты)
		k	3	;4 значения передвинуть хотим
		Y	DA_Vexp
@@MoveVector:	[Y+k]	[X+k]
		kLOOP	@@MoveVector

;и ещё давайте статус поставим: режим "захват", достоверность дальности и углов пока отсутствует (она появляется только на сопровождении)			
		X	DA_flags
		[X+k]	1		;захват, достоверности пока нет, стереорежима нет. 
;и ещё выставим метку времени, когда работа наша была завершена.
;Это не очень правильно: мы вообще-то хотели ставить метку на середине экспозиции (или ещё лучше: на середине экспозиции конкретно для тех участков, где мы обнаружили мишени)
		OUT	0			
;и как будто бы наша работа здесь завершена
		JMP	start


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

Ещё одна добавка к этому коду - включение файла SetClock.asm, там мы настраиваем тактовую частоту 25 МГц, в самом начале работы.


В итоге, программа занимает 284 слова кода (658 байт), т.е подрос на 30 слов. Нормально.

Пока что получаем ширину адресных шин по 9 бит, что ОЗУ, что ПЗУ. Соответственно, выделяем 1024 байта на ОЗУ и ещё 1024 байта на ПЗУ. Если я надеюсь уместиться в 5576ХС6Т, то 40% памяти уже занял, остаётся не так уж много.

Квартусовский проект вполне себе синтезируется, давая 1733 ЛЭ (в 5576ХС6Т имеется 2800 ЛЭ) и предельную частоту 27 МГц, нормально.

Но надо ещё I/O доковырять, после чего можно будет опробовать всё это безобразие в работе. Это уже в понедельник.
Tags: ПЛИС, программки, работа, странные девайсы
Subscribe

Recent Posts from This Journal

  • Тестируем atan1 на QuatCore

    Пора уже перебираться на "железо" потихоньку. Решил начать с самого первого алгоритма, поскольку он уже был написан на ассемблере. В программу внёс…

  • Формулы приведения, что б их... (и atan на ТРЁХ умножениях)

    Формулу арктангенса на 4 умножениях ещё немножко оптимизировал с помощью алгоритма Ремеза: Ошибка уменьшилась с 4,9 до 4,65 угловой секунды, и…

  • Алгоритм Ремеза в экселе

    Вот и до него руки дошли, причина станет ясна в следующем посте. Изучать чужие библиотеки было лениво (в том же BOOSTе сам чёрт ногу сломит), писать…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments