nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

Приводим в порядок знаки QuatCore

Громко подумали - теперь пора привести всё в чувства, во многом - вернуть как было! Мы несколько раз отказывались от оригинальной задумки, лишь бы программа, которую в этот момент писали, заработала как надо. Первый раз знаки нас не устроили (а всего-то надо было поменять адресацию - левому операнду i^j, правому i, а не наоборот). Вон, 8 июля 2019 года приводил ровно ту табличку, которую хочу сейчас, но когда дошло дело до реализации 3-го января 2020 года, почему-то она уже поменялась...

Сейчас наш модуль QuatCorePC (Program Counter) выглядит так:


Синим обведён модуль, который пора немножко помучать, NewQuatSigns. Видно, что сейчас он "подключён" к регистрам i,j, но вместо j надо взять k.


Глянем код этого модуля:

module NewQuatSigns (input [4:0] ireg, input [4:0] jreg, input Inv, input isLongOp, output PM);

wire [1:0] i = ireg[1:0];
wire [1:0] k = jreg[1:0];
					                  
wire PM_norm = 	(i == 0)? (
			(k == 0)? 1'b0:
			(k == 1)? 1'b1:
			(k == 2)? 1'b1:
				  1'b1) :
		(i == 1)? (
			(k == 0)? 1'b0:
			(k == 1)? 1'b0:
			(k == 2)? 1'b1:
				  1'b0) :
		(i == 2)? (
			(k == 0)? 1'b0:
			(k == 1)? 1'b0:
			(k == 2)? 1'b0:
				  1'b1) :
		(
			(k == 0)? 1'b0:
			(k == 1)? 1'b1:
			(k == 2)? 1'b0:
			          1'b0);
wire PM_inv = 	(i == 0)? (
			(k == 0)? 1'b0:
			(k == 1)? 1'b0:
			(k == 2)? 1'b0:
				  1'b0) :
		(i == 1)? (
			(k == 0)? 1'b0:
			(k == 1)? 1'b1:
			(k == 2)? 1'b0:
				  1'b1) :
		(i == 2)? (
			(k == 0)? 1'b0:
			(k == 1)? 1'b1:
			(k == 2)? 1'b1:
				  1'b0) :
			(
			(k == 0)? 1'b0:
			(k == 1)? 1'b0:
			(k == 2)? 1'b1:
			          1'b1);					          
					          
assign PM = isLongOp? (Inv? PM_inv : PM_norm) : Inv;
					          					          
endmodule


И тут видны "следы", что изначально мы хотели использовать регистры i,k, и лишь "в последний момент", в виде костыля, k был заменён на j. Было это примерно 14 января 2020 года. Ну ладно, и такое бывает.

А по названию, NewQuatSigns, можно догадаться, что был модуль просто QuatSigns, который по какой-то причине нас не устроил, но "на всякий случай" не стали его трогать. И правда, вот он:

module QuatSigns (input [1:0] i, input [1:0] j, input Inv, output PM);
				                  
wire PM_norm = 	(j == 0)? (
			(i == 0)? 1'b0:
			(i == 1)? 1'b1:
			(i == 2)? 1'b1:
				  1'b1) :
		(j == 1)? (
			(i == 0)? 1'b0:
			(i == 1)? 1'b0:
			(i == 2)? 1'b0:
				  1'b1) :
		(j == 2)? (
			(i == 0)? 1'b0:
			(i == 1)? 1'b1:
			(i == 2)? 1'b0:
				  1'b0) :
		(
			(i == 0)? 1'b0:
			(i == 1)? 1'b0:
			(i == 2)? 1'b1:
			          1'b0);
wire PM_inv = 	(i == 0)? (
			(j == 0)? 1'b0:
			(j == 1)? 1'b1:
			(j == 2)? 1'b1:
				  1'b1) :
		(i == 1)? (
			(j == 0)? 1'b0:
			(j == 1)? 1'b0:
			(j == 2)? 1'b0:
				  1'b1) :
		(i == 2)? (
			(j == 0)? 1'b0:
			(j == 1)? 1'b1:
			(j == 2)? 1'b0:
				  1'b0) :
		(
			(j == 0)? 1'b0:
			(j == 1)? 1'b0:
			(j == 2)? 1'b1:
			          1'b0);					          
					          
assign PM = Inv? PM_inv : PM_norm;
					          					          
endmodule


Дата создания и последнего изменения: 28 июня 2019 года! Забавно: процессора ещё не существовало (он 31-го декабря 2019 сколько-нибудь был готов, а потом ещё месяц его приводил в чувства), а этот модуль уже был :)

И вот в нём реализована исходная концепция: Inv по сути транспонирует таблицу (меняет местами индексы), и все знаки соответствуют вот этой таблице, самой первой:
SignTableTransposed.png

Правда, здесь почему-то тоже затесался регистр j (с ними тоже тогда никакой ясности не было), и тут не было варианта на "коротких" командах вроде PM (Plus-Minus) не привлекать индексных регистров, а просто на выход отправить значение Inv.

Так что теперь из этих двух модулей надо сделать один, вот такой:
module NewQuatSigns (input [4:0] ireg, input [4:0] kreg, input Inv, input isLongOp, output PM);

wire [1:0] i = ireg[1:0];
wire [1:0] k = kreg[1:0];
					                  
wire PM_norm = 	(i == 0)? (
			(k == 0)? 1'b0:
			(k == 1)? 1'b1:
			(k == 2)? 1'b1:
				  1'b1) :
		(i == 1)? (
			(k == 0)? 1'b0:
			(k == 1)? 1'b0:
			(k == 2)? 1'b0:
				  1'b1) :
		(i == 2)? (
			(k == 0)? 1'b0:
			(k == 1)? 1'b1:
			(k == 2)? 1'b0:
				  1'b0) :
		(
			(k == 0)? 1'b0:
			(k == 1)? 1'b0:
			(k == 2)? 1'b1:
			          1'b0);
wire PM_inv = 	(k == 0)? (
			(i == 0)? 1'b0:
			(i == 1)? 1'b1:
			(i == 2)? 1'b1:
				  1'b1) :
		(k == 1)? (
			(i == 0)? 1'b0:
			(i == 1)? 1'b0:
			(i == 2)? 1'b0:
				  1'b1) :
		(k == 2)? (
			(i == 0)? 1'b0:
			(i == 1)? 1'b1:
			(i == 2)? 1'b0:
				  1'b0) :
		(
			(i == 0)? 1'b0:
			(i == 1)? 1'b0:
			(i == 2)? 1'b1:
			          1'b0);					          
					          
assign PM = isLongOp? (Inv? PM_inv : PM_norm) : Inv;
					          					          
endmodule


Синтезируется аж в 3 ЛЭ, что меня вполне устраивает. По-новой создаём "схемотехнический символ" (вход jreg поменялся на kreg), и соединяем уже как надо:


Ещё нужно подправить обращение к памяти, многострадальный модуль QuatCoreSchemMemV5:


Снизу справа - формирователь эффективного адреса, состоящий из мультиплексора базового адреса QuatCoreMemBaseMuxWBand (WBand означает расширение на 2 бита, чтобы можно было включать "байтовый доступ", вот здесь ввели), мультиплексора первого индекса QuatCoreFirstIndexMuxV2 (версия 2 отличается тем, что 4j заменили на 3j, он оказался куда нужнее), мультиплексора второго индекса QuatCoreMemSecondIndexMux и двух сумматоров.

Пришла пора QuatCoreMemSecondIndexMux умучать (он обведён синим):
module QuatCoreMemSecondIndexMux (input [15:0] ijk, input [1:0] Base, input [1:0] adr, output [4:0] Q);

wire [4:0] i = ijk[9:5];
wire [4:0] j = ijk[4:0];
wire [4:0] k = ijk[14:10];

assign Q = 	(adr == 2'b00)? 	5'b00001 :
		(adr == 2'b01)? 	i :
		(adr == 2'b10)? 	k :
		(Base[0]&Base[1])?	5'b00000 :
					i^j;
							
endmodule


Данный файл был создан 30 декабря 2019, последний раз изменён 14 января 2020 года! Ну, пора пошевелить. Всё просто: i^j заменяем на i^k. Даже получившийся код приводить не буду, и так понятно. Видно, в общем-то, что регистр j здесь был лишним, всё остальное зависело только от i и k. Так что всё правильно сделали.

В целом, проект успешно отсинтезировался в 1699 ЛЭ на этапе Analysis&Synthesis (до переделки было 1701, кажется, или ещё чуть больше), после Place&Route стало 1727 - тоже чуть меньше. Кажись, до этого у меня было число Рамануджана, 1729. Скорость пока устраивает, 27,78 МГц, уже радостно...

С "аппаратной частью" закончили. Теперь компилятор. Там немного: все упоминания i^j в файле конфигурации переправить на i^k. Всего таких команд 18:
[X+i^j]  [X+2j+i^j]   [X+3j+i^j]
[Y+i^j]  [Y+2j+i^j]   [Y+Treug[j]+i^j]
[Z+i^j]  [Z+2j+i^j]   [Z+3j+i^j]
,
и каждая из этих 9 - как на запись, так и на чтение, это по сути две разные команды, хоть и называются одинаково.

Одну пару приведу для примера, [X+i^j]:

  object TQuatCoreCommand
    Key = '[X+i^j]'
    Code = 204
    Description = 'Access memory [X+i^j]'
    Place = [cpDest]
    Resources = [crMemBus,crMemY,crMemZ,crMemSP]
    DataType = dtNumeric
  end
  object TQuatCoreCommand
    Key = '[X+i^j]'
    Code = 204
    Description = 'Access memory [X+i^j]'
    Place = [cpSrc]
    Resources = [crX,cri,crj,crMemBus]
    DataType = dtNumeric
  end


Понятное дело, надо переправить Key. На всякий случай и Description, хотя пока он "в ход не пошёл" нигде, так, на всякий пожарный.
Затем смотрим Resources - они здесь введены, чтобы отслеживать возникновение Hazards, не знаю, как это по-русски, наверное, конфликты. Тут важны ресурсы cri, crj при чтении из памяти. Это позволяет компилятору "сообразить", что предыдущая команда "на запись" (DestAddr) как раз изменяет один из регистров i,j, а сейчас мы читаем [X+i^j], то поскольку в конвейере два этих действия происходят одновременно, мы получим не тот результат, который ожидали, "наивно полагая", что команды исполняются одна за другой. Прочитаем из памяти по старому значению i или j.

В общем, тут тоже всё просто: следим, какие регистры используются и запихиваем соответствующие ресурсы. В приведённом примере crj заменяем на crk. Но в упоротой команде [X+2j+i^j], ресурс crj (command resource j) надо ОСТАВИТЬ, но добавить ещё один, crk.

И так все 9 команд, относящихся к SrcAddr (у которых Place=[cpSrc]).

Запускаем компилятор - и получаем следующее:
Загружаем файл конфигурации транслятора
Файл конфигурации прочитан, готовы к работе


То есть, очевидных проблем нет.

Но при попытке откомпилировать нашу программу (алгоритмы захвата + информационный обмен) он ругается, ведь там сейчас где-то уже i^k, но в 5 местах осталось i^j, и просто так провести замену нельзя.


Осталось программу переправить, убедиться что "ничего не сломали" - и запустить наш многострадальный арктангенс!
Tags: ПЛИС, программки, работа, странные девайсы
Subscribe

  • Тестируем 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