nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

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

Посидел глубоко в отладке, вроде разобрался, что к чему. Всё неправильное поведение свелось к двум багам, один в модуле RTC (Real Time Clock), второй - в программе, я в неё влез по дурости, показалось, что можно получше сделать, в итоге выкинув одну строчку, запорол одновременно и нормировку кватерниона, и компоненты Y,Z вектора параллельного переноса. Это ещё умудриться надо было...


Первая проблема: выполнение алгоритма занимало самую чуточку БОЛЬШЕ 195 мкс, поэтому в метке времени этот факт должен был отобразиться. Приведём код модуля QuatCoreRTC, благо он довольно маленький:

module QuatCoreRTC (input clk, input sync, input [15:0] D, input Mark,
output reg [15:0] Q = 1'b0, output tick, output ce_2_44_us);

	
	wire [15:0] Time;
	wire LowCounterCout;
	wire ce_195us;
							
	lpm_counter HighCounter (
				.clock (clk),
				.cnt_en (LowCounterCout & ce_195us),
				.data (D[6:0]),
				.sload (sync),
				.q (Time[15:9]) );
	defparam
		HighCounter.lpm_direction = "UP",
		HighCounter.lpm_port_updown = "PORT_UNUSED",
		HighCounter.lpm_type = "LPM_COUNTER",
		HighCounter.lpm_width = 7;
		
	lpm_counter LowCounter(
			.clock (clk),
			.cnt_en (ce_195us),
			.sclr(sync),
			.q (Time[8:0]),
			.cout (LowCounterCout));
						
	defparam
		LowCounter.lpm_direction = "UP",
		LowCounter.lpm_port_updown = "PORT_UNUSED",
		LowCounter.lpm_type = "LPM_COUNTER",
		LowCounter.lpm_width = 9;
		
	always @(posedge clk) if (Mark)
		Q <= Time;		

	lpm_counter LowDivider (
				.clock (clk),
				.cnt_en (1'b1),
				.sclr (sync),
				.sset (ce_2_44_us),
				.cout (ce_2_44_us) );
	defparam
		LowDivider.lpm_direction = "UP",
		LowDivider.lpm_port_updown = "PORT_UNUSED",
		LowDivider.lpm_type = "LPM_COUNTER",
		LowDivider.lpm_width = 6,
		LowDivider.lpm_svalue = 3;
		
	lpm_counter HighDivider (
				.clock (clk),
				.cnt_en (ce_2_44_us),
				.sclr (sync),
				.sset (ce_195us),
				.cout (ce_195us) );
	defparam
		HighDivider.lpm_direction = "UP",
		HighDivider.lpm_port_updown = "PORT_UNUSED",
		HighDivider.lpm_type = "LPM_COUNTER",
		HighDivider.lpm_width = 7,
		HighDivider.lpm_svalue = 47;
				
	assign tick = ce_195us;

endmodule


С делителем частоты традиционно "перемудрил". Последний раз этот модуль ковыряли здесь, чтобы "вывести" импульсы примерно в 2 мкс, благодаря которым будет обеспечена пауза между командным словом и ответным словом. Для этого делитель частоты разделили на два модуля, "Low" и "High", первый делит тактовую частоту 25 Мгц в 61 раз, а второй - ещё в 80 раз.

Но мне страшно не нравится в таких делителях применять компараторы, когда счёт начинается от нуля, но не доходит до максимального для данного размера значения (63 для 6-битного счётчика, 127 для 7-битного), а сбрасывается ранее. Хотя у меня есть небольшое "ноу-хау", как эти компараторы уменьшить в размерах, проверяя только наличие "единичек" на своих местах и не требуя ноликов, всё равно самое компактное исполнение - счёт от некоторой константы до крайнего значения. В нашем случае: от 3 до 63 и от 47 до 127.

Одна проблема: нужно было сигнал sync подать не на вход обнуления (sclr, synchronous clear), а на тот же самый sset (synchronous set), для установки начального значения. Иначе получалось деление не в 80 раз, как положено, а в 127 раз, и ПЕРВЫЙ импульс длится не 195 мкс, а 310 мкс. Следующие импульсы уже правильные, так что в целом проблема не шибко большая, но неаккуратненько! Решение очень простое: sclr вообще не используем, а в sset через "OR" добавляем вход sync:

	lpm_counter LowDivider (
			.clock (clk),
			.cnt_en (1'b1),
			.sclr (1'b0),
			.sset (ce_2_44_us | sync),
			.cout (ce_2_44_us) );
	defparam
		LowDivider.lpm_direction = "UP",
		LowDivider.lpm_port_updown = "PORT_UNUSED",
		LowDivider.lpm_type = "LPM_COUNTER",
		LowDivider.lpm_width = 6,
		LowDivider.lpm_svalue = 3;
		
	lpm_counter HighDivider (
				.clock (clk),
				.cnt_en (ce_2_44_us),
				.sclr (1'b0),
				.sset (ce_195us | sync),
				.cout (ce_195us) );
	defparam
		HighDivider.lpm_direction = "UP",
		HighDivider.lpm_port_updown = "PORT_UNUSED",
		HighDivider.lpm_type = "LPM_COUNTER",
		HighDivider.lpm_width = 7,
		HighDivider.lpm_svalue = 47;


Раньше этот модуль QuatCoreRTC синтезировался в 53 ЛЭ, теперь: в 55 ЛЭ. Ну да, ровно два этих "OR" и добавилось. Жить можно...

Чтобы найти второй баг, всё-таки запустил программу на симуляции. Отдельно на модуле PipelineQuatCore, это ЯДРО ПО ОТДЕЛЬНОСТИ, без периферии. Поэтому команды IN и OUT всегда выполняются за 1 такт, "вхолостую". Поэтому ожидания синхронизации не будет, что меня устраивает. Но забыл, что выполнив всю работу, процессор тут же начнёт делать по-новой, поэтому смотреть дамп памяти неудобно. Так что заменил прыжок на старт бесконечным циклом.

В итоге получил В ТОЧНОСТИ ТЕ ЖЕ РЕЗУЛЬТАТЫ, что и "на железе":


Потом сравнил с дампом памяти отсюда и увидел те же самые "промежуточные значения", только вот кватернион там нормирован "как следует", и значения Y,Z правильные, маленькие.

В общем, "методом пристального взгляда" понял, что в процедуре FindVector я накосячил. Она вот так выглядела:

;состояние регистров:
;X=AfTransf=Exp   (это одно и тоже, просто в разное время)
;Y=Matrix (первые 4 значения можно использовать как временные)
;Z=QuatY (два нулевых значения, не надо их трогать)
;i=k=0
;j=Exp
;Inv=1
;C, Acc - пофиг (сейчас сотрём)
;в [SP] лежит "мантисса" масштаба, которую надо обратить
;в [SP+1] - либо 3, если дальняя дистанция, либо 5, если ближняя.
FindVector	proc	
		k	3
		C	43648	;наше нулевое приближение к обр. величине
@@Newton:	Acc	0		;занести двойку
		UFMS	[SP]	;Acc = 2 - a * x
		MULU	UAC		;Acc = Acc * x = x * (2- a*x), т.е итерация метода Ньютона
		Y	Rx								
		Z	QuatA
		i	1				
		C	UAC				
		kLOOP	@@Newton			
;золотой ключик почти у нас в кармане
;если на входе было 16384, то правильный результат 65536 не влезет в 16 бит, сократившись до нуля. 
;это мы должны заметить по переполнению, только вот не соображу, оно должно ПОЯВИТЬСЯ, или исчезнуть?
		JGE	@@SkipOflo
		j++	0 			;раз не вмещается в мантиссу, значит добавим экспоненту, а в мантиссу нужно 32768 теперь.Ща найдём...
		C	32768 
@@SkipOflo:	[X+k]	j						
		[X+1]	C

		X	Ty
;и ещё осталось найти Ty, Tz
@@vector:	MULSU	[Y+i]					
		[X+i]	Acc
		iLOOP	@@vector
		
		CALL	NormSiCo
				
FindVector	endp


И чего-то мне показалось, что строка "Z QuatA" здесь лишняя! Потому как дальше на протяжении всего кода этот регистр Z вообще не используется. Ещё и регистр X "затирался" - то он указывал на начало вектора параллельного переноса, того, что мы собирались скопировать в массив целевой информации, если используется декартова система координат.

Поэтому я по дурости удалил эту строчку, а "X Ty" заменил на "Z Ty", чтобы значение X сохранить для дальнейшей работы.

И всё было бы хорошо, но я совсем забыл про вызов процедуры NormSiCo в конце. Именно для неё мы инициализировали Z! Как водится, эта инициализация была раньше времени, чтобы полезно использовать такты, когда АЛУ умножает. В итоге кватернион не нормировался, а взамен него "нормировались" значения Y и Z вектора, именно поэтому они стали такими большими!

Ладно, возвращаем "как было" - и теперь всё должно заработать как положено.


Завтра проверю, что всё правильно - и придётся в очередной раз резко сменить род деятельности. Сегодня мне в РКК полтора часа мозги компостировали насчёт юстировки прибора. У нас для этого в теории существуют КПАшники (те, кто занимаются Контрольно-Проверочной Аппаратурой), целый отдел, но они самоустранились, сославшись на Стандарт Предприятия (СТП), по которому они делают только ту часть, что отправляется на входной контроль, это абсолютный минимум, способный лишь показать "ничего не повредили пока везли", а вот рабочее место, на котором проверяется львиная доля требований, а также всё, что касается работы заказчиков с прибором, включая измерение положения прибора на аппарате - должен придумать разработчик прибора, то есть я. Да, идейки были, но пора их оформить...
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 

  • 2 comments