nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

А когда случайно брал на контрольной интеграл,

Он тут же отдавал его обратно!

Продолжаю ходить кругами, или, если быть оптимистами - развиваться по спирали :)

Был у меня старый модуль QuatCoreMem:


Модуль управления из него я дорабатывал напильником, с повышением функциональности, и при этом с УПРОЩЕНИЕМ модуля в плане ЛЭ.

Но ещё позже ВНЕЗАПНО оказалось, что вся эта штука не позволяет заносить в стек значения базовых регистров X,Y,Z, поскольку мультиплексор базового регистра у него всего один, и если обеим "полукомандам" (на чтение, Src, и на запись, Dest) нужны разные регистры, приоритет будет отдан команде на запись. В итоге, в стек будет заносится адрес стека :)

Меня это категорически не устраивало, поэтому я его маленько модернизировал:


Поставил ДВА одинаковых мультиплексора, так что теперь строка
[SP++]  Y

стала абсолютно корректной.

Но теперь, когда процессор "ускорился" (обзавёлся конвейером), может статься, что такой ситуации в программе ни разу и не сложится!

Вот задумался, может, вернуть, как было? Сэкономлю аж 16 ЛЭ :) И глядишь, fitter вздохнёт с облегчением, что-то он тяжело очень этот процессор компонует, на последнем издыхании...


В прошлый раз у меня ещё не было обнаружения и исправления Hazard'ов в трансляторе, потому и полез сразу "аппаратно" это исправлять. Теперь же можно и такую ситуацию предусмотреть, львиная доля кода для этого уже существует.

Проблема только в том, что здесь очень хитрючий выходит Hazard, который может не вписываться в нашу систему. До сих пор нам удавалось обойтись принципом "запретить доступ к одному ресурсу сразу двумя командами". И затем прописали, какие ресурсы использует каждая команда - и вуаля. Даже когда стало понятно, что в теории многие ресурсы имеют более хитрую "природу", Multiple-Read / Exclusive-Write, за счёт некой стройности системы команд удалось избежать усложнений.

А тут у нас вдруг всё наоборот: команды

[Yx]   Y


или
[Xx]   X


(и т.д., под x я подразумеваю разные варианты индексов, +1, +i, +k, +2j+i и пр.)
допустимы,

А вот
[Xx]  Y


не прокатит, вместо Y занесёт X.

Громко подумаем. Дадим команде X какой-нибудь ресурс, crMemX. И его же надо дать командам [Yx], [Zx], [SPx]. А вот команды [Xx] его не получают, поскольку всё в порядке. Пока проблем нет.

Теперь дадим команде Y ресурс crMemY. И его же даём командам [Xx], [Zx], [SPx], а команды [Yx] его не получают.

И затем то же самое с crMemZ и crMemSP. Похоже, что всё в порядке :) Громоздко - сейчас опять пройтись по 128 командам (64 на чтение и 64 на запись) и каждой добавить ресурсы, ну зато всё унифицировано.

И что вы думаете: в программе "аффинного алгоритма" новых Hazard'ов не появилось.

Правда, они могут появиться в процедуре поворота вектора с помощью кватерниона:

;Процедура поворота вектора с помощью кватерниона.
;Состояние регистров при вызове:
;X – адрес исходного вектора (вектор – это кватернион с произвольной скалярной частью)
;Y – адрес кватерниона
;Z – адрес результата
;i,j,k, Inv – любые.
;Адрес исходного и результирующего вектора может совпадать, вычисления будут корректными. 
;После вызова, значения регистров не изменятся, кроме Inv, который станет нулевым (мы могли бы и его сохранять, но скорее всего нет необходимости)

;использует 2 слова стека (адрес возврата + значение X) + вызывает QuatMultiply, так что в целом нужно 8 слов. 
RotateVecByQuat proc
			Inv 	1     	;чтобы было умножение на сопряжённый
			CALL 	QuatMultiply
;теперь по адресу Z лежит X * Conj(Y)
;осталось домножить его слева на Y
;для чего нужно поставить X=Y, Y=Z
;но X сохраним, чтобы потом восстановить
			[SP++]	X
			Inv	0   	;сопряжение больше не нужно  
			X	Y
			Y	Z
			CALL 	QuatMultiply
			;восстанавливаем Y
			Y	X
			;и наконец, X
			X	[--SP]
			JMP	[--SP]	;RET
RotateVecByQuat endp


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


Ах да, саму процедуру перемножения кватернионов тоже нужно взять:
;Процедура перемножения кватернионов.
;Состояние регистров при вызове:
;X – адрес левого операнда
;Y – адрес правого операнда
;Z – адрес результата
;i,j,k - произвольное значение
;Inv=1, если правый операнд нужно взять сопряжённым, =0 в противном случае.
;На адреса операндов и результата не накладывается ограничений (допустимо Z=X или Z=Y, или X=Y, вычисления будут корректными). 
;Значения регистров после вызова не изменятся.

;эта процедура не вызывает других, но требует 6 слов на стеке (включая адрес возврата)
QuatMultiply proc
		[SP++]	C
		[SP++]	ijk 		;заносим в стек все индекс. регистры разом
		i 	3		;i-номер компонента, который вычисляем в данный момент
@@row_loop:	ZAcc	RoundZero	;обнуляем аккумулятор
		k	3    		;k-номер слагаемого
@@col_loop:	C	[X+i^k]		;загрузили левый операнд
		FMPM	[Y+k] 		;помножили на правый, с нужным знаком
		kLOOP	@@col_loop

		[SP+i]	Acc 		;храним ответы на стеке
;потому что если X=Z или X=Y (обновляем кватернион),
;то занесение сразу в Z исказит последующие вычисл.
		iLOOP	@@row_loop
		i	3
@@out_loop:	[Z+i]	[SP+i] 		;помещаем ответ куда надо
		iLOOP	@@out_loop
		ijk	[--SP]		;восстанавливаем исх. значения регистров
		C	[--SP]
		JMP	[--SP]		;возврат из процедуры
QuatMultiply endp


И снова облом: команды [X+i^k] не существует. Всё верно, мы заменили её на [X+i^j]. Вот хорошо, что у меня все ходы записаны. Там же лежит корректная процедура умножения кватернионов:

QuatMultiply proc
		[SP++]		C
		[SP++]		i 			;заносим в стек все индекс. регистры разом
		[SP++]		j
		[SP++]		k
		j 		3			;j-номер компонента, который вычисляем в данный момент
@@row_loop:	ZAcc		RoundZero	;обнуляем аккумулятор
		i		3    		;k-номер слагаемого
@@col_loop:	C		[X+i^j]		;загрузили левый операнд
		FMPM		[Y+i] 		;помножили на правый, с нужным знаком
		iLOOP		@@col_loop
		k		j
		[SP+k]		Acc 		;храним ответы на стеке
;потому что если X=Z или X=Y (обновляем кватернион),
;то занесение сразу в Z исказит последующие вычисл.
		jLOOP		@@row_loop
		i		3
@@out_loop:	[Z+i]		[SP+i] 	;помещаем ответ куда надо
		iLOOP		@@out_loop
		k		[--SP]
		j		[--SP]
		i		[--SP]		;восстанавливаем исх. значения регистров
		C		[--SP]
		JMP		[--SP]		;возврат из процедуры
QuatMultiply endp


Вот что нам выдаёт транслятор по поводу Hazard'ов:

Конфликт (Hazard) между командами i и [X+i^j], процедура QuatMultiply, строки:
		i		3    		;k-номер слагаемого
@@col_loop:	C		[X+i^j]		;загрузили левый операнд
Вставляем NOP

Конфликт (Hazard) между командами i и [SP+i], процедура QuatMultiply, строки:
		i		3
@@out_loop:	[Z+i]		[SP+i] 	;помещаем ответ куда надо
Вставляем NOP


Это старые добрые RAW Hazard'ы (Read After Write). Первый "лечится" перестановкой строк

	ZAcc		RoundZero	;обнуляем аккумулятор
	i		3   


Второй лечится перестановкой строк

		jLOOP		@@row_loop
		i		3

Кстати, теперь мы точно знаем, что в начале каждой следующей итерации этого цикла уже будет i=3 (кроме самой первой). Это позволяет переставить метку @@row_loop на 1 вниз, вместо

@@row_loop:	i	3
		ZAcc	RoundZero

ставим

		i	3
@@row_loop:	ZAcc	RoundZero


Таким образом, мы избавляемся от обоих Hazard'ов, и нисколько не теряем в производительности или размере исполняемого кода.

И всё, теперь ни одного Hazard'а не осталось, как это ни странно.

Но давайте убедимся, что он всё-таки умеет отслеживать такие вещи, и вдогонку к
	[SP++]	X


поставим
	[SP++] Y ;проверить Hazard "один мультиплексор базового адреса на двоих"


Чтобы "не отвертелся". Вот что получается:

Конфликт (Hazard) между командами [SP++] и Y, процедура RotateVecByQuat, строки:
			[SP++]	X
			[SP++]	Y	;проверить Hazard "один мультиплексор базового адреса на двоих"
Вставляем NOP

Да, работает :)

Так что считаю, что нужно лишний мультиплексор выкинуть!

Сказано-сделано. Количество ЛЭ сократилось на 8 (вместо ожидаемых 16), скомпоновалось даже при выключенных "агрессивных настройках" фиттера, и довольно быстро. Но выскочил Critical Warning: частота в 25 МГц не обеспечивается, только 24,45 МГц. В общем, как обычно, рандом. Хотя если посмотреть список цепей, для которых не удалось выдержать тайминги, почти все начинаются из регистра, формирующего DestStall. Надо будет об этом поразмыслить...

И ещё одно: череда везения с формированием QuatCoreCallTable из 0 ЛЭ закончилась. С новой процедурой QuatMultiply, получается уже 5 уникальных последовательностей битов в их адресах, и 1 ЛЭ всё-таки нужен.


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

  • Так ли страшно 0,99969 вместо 1?

    В размышлениях о DMA, о возможных последствиях "смешивания" старых и новых значений при выдаче целевой информации, опять выполз вопрос: насколько…

  • Как продлить агонию велотрансмиссии на 1500+ км

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

  • DMA для QuatCore

    Вот фрагмент схемы нашего "процессорного ядра" QuatCore: Справа сверху "притаилась" оперативная память. На той ПЛИС, что у меня есть сейчас…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 9 comments