nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

Разогнанный QuatCore: чиним bypass в АЛУ

В арифметическом устройстве у меня был очередной "финт ушами" - хотя шина данных 16-битная, но аккумулятор и сумматор - 32-битные. Дополнительные биты позволяют избежать многих ошибок во время матричных и кватернионных вычислений, поскольку мы сначала складываем все-все компоненты, используя FMA (Fused Multiply-Add), FMS (Fused Multiply-Subtract) и FMPM (Fused Multiply - Plus-Minus), и только под самый конец округляем до 16 бит.

Но при попытке реализовать алгоритм сопровождения, а конкретнее решение системы линейных уравнений с помощью UUT-разложения, обнаружилось, что этого мало.

Поэтому была введена "фича": если для очередной операции с АЛУ у нас источником выступает аккумулятор Acc (выход АЛУ), то 16 старших бит попадут с выхода на вход через штатную шину данных, а младшие биты будут скоммутированы уже внутри АЛУ с помощью того самого "байпаса". Потом оказалось, что и взятие обратной величины сработает точно (все 16 бит, что пойдут "в дело", будут точными), только если этот байпас присутствует.

Пока у нас получение значения на шину данных происходило на том же такте, что и запуск АЛУ, всё работало нормально. Но сейчас старшие биты (идущие через шину данных) задерживаются на один такт, а младшие - нет. И всё рушится...

Исправление должно быть довольно простым, но всё-таки нужно к нему подступиться аккуратно...


Возьмём кусочек из нашей программы:

		@@i_loop:	Acc		[Y+2j+k]	;загрузили одно значение
				SUB		[Y+2i+k]	;вычитаем второе
				SQRD2		Acc			;возводим в квадрат
				ADD		[SP]		;прибавляем к пред. значению
				[SP]		Acc


Байпас должен сработать в одном-единственном месте:
SQRD2  Acc

То есть, у нас в аккумуляторе лежало значение - разность двух координат. А теперь мы хотим возвести его в квадрат и поделить на два. Хорошо будет, если в 32-битный регистр B попадёт аккумулятор целиком, а не только старшие 16 бит.

Но посмотрим, как в действительности процессор будет обрабатывать эти строки. Весь левый столбец оказывается сдвинут вниз, поскольку на одном такте мы получаем значение (это правый столбец), а только на следующем обрабатываем его или сохраняем куда надо:
		[Y+2j+k]	
Acc		[Y+2i+k]
SUB		Acc
SQRD2		[SP]
ADD		Acc
[SP]


Надо понимать, что на строке
SUB  Acc

вычитается не значение аккумулятора, а вычитается ранее полученное значение [Y+2i+k].

А вот уже в
SQRD2      [SP]


берётся значение из аккумулятора.

Чтобы понять, что нужно "подключить байпас", а не просто заполнить младшие биты нулями, мы использовали небольшой модуль:
module QuatCoreEnableBypass (input [7:0] SrcAddr, output Q);

assign Q = (SrcAddr[7:5] == 3'b100); //source is within ALU as well

endmodule


Как видно, он нуждается в доработке. Его значение также надо задержать с предыдущей команды, а ещё добавить один бит для проверки, поскольку часть адресного пространства АЛУ в SrcAddr мы отдали на внешнюю память и ввод-вывод:

module FastQuatCoreEnableBypass (input clk, input PipeStall, input [7:0] SrcAddr, output reg Q = 1'b0);

always @(posedge clk) if (~PipeStall)
	Q <= (SrcAddr[7:4] == 4'b1000); //source is within ALU as well

endmodule


Ну и подключим недостающие провода, в том числе, заведём PipeStall внутрь АЛУ. Можно было бы поразмышлять, что DestStall может дать только сам АЛУ, а SrcStall - только QuatCoreMem, но если он был вызван, значит байпас должен быть отключён, но наверное проще здесь не закапываться особо. Ведь в дальнейшем мы ещё подключим ввод-вывод, и он тоже будет конвейер останавливать время от времени, так что сделаем чуть "на будущее":



Хорошо, теперь мы будем знать, когда задействовать байпас, а когда нет. Но когда он задействован - будут ли на нём лежать правильные данные?

Должны лежать... Есть только одна причина, почему аккумулятор может "в самый последний момент" поменяться - это если по DestAddr на прошлом шаге была ещё одна операция с АЛУ. Но тогда изменившийся аккумулятор - это фича, а не баг. Мы специально ввели интерлок в прошлый раз, чтобы дождаться обновлённого значения.

Проверим, что из всего этого выходит...


Ура! Мы видим, как bypass=1 приходится аккурат на следующую за Acc командой. Сейчас посмотрел ещё раз на это дело и подумал: можно было бы с PipeStall и не заморачиваться, задержки на один такт вполне бы хватило. Ну ладно, ломать не строить.

Наконец-то результатом возведения нуля в квадрат и прибавления ещё одного нуля стал ноль, который мы заносим в [SP] - это локальная переменная, сумма квадратов разности координат.
Tags: ПЛИС, программки, работа, странные девайсы
Subscribe

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

    Вчера опробовал "сценарий", когда варьируем дальность от 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 

  • 0 comments