nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

QuatCore+GPU: компилируем программу

Очередные "последние приготовления".

Конфигурацию компилятора нужно привести в соответствие с "железом". Последний раз, когда мы её писали, предполагалась одна команда для получения данных c видеопроцессора, по имени "GPU". Теперь их стало 4: GPUL, GPUH, GPUPL и GPUPH. Где наша не пропадала, перепишем:

  object TQuatCoreCommand
    Key = 'GPUL'
    Code = 136
    Mask = 254
    Description = 'Read Luminance/Low 1st sum word from Graphic Processor (which reads image from sensor)'
    Place = [cpSrc]
    Resources = []
    DataType = dtNumeric
  end
  object TQuatCoreCommand
    Key = 'GPUH'
    Code = 138
    Mask = 254
    Description = 'Read Hor coord/High 1st sum word from Graphic Processor (which reads image from sensor)'
    Place = [cpSrc]
    Resources = []
    DataType = dtNumeric
  end
  object TQuatCoreCommand
    Key = 'GPUPL'
    Code = 140
    Mask = 254
    Description = 'Read Product Low from Graphic Processor (which reads image from sensor)'
    Place = [cpSrc]
    Resources = []
    DataType = dtNumeric
  end
  object TQuatCoreCommand
    Key = 'GPUPH'
    Code = 142
    Mask = 254
    Description = 'Read Product High from Graphic Processor (which reads image from sensor)'
    Place = [cpSrc]
    Resources = []
    DataType = dtNumeric
  end


Получаем такую таблицу инструкций по SrcAddr:

SrcAddr


Адрес +0 +1 +2 +3 +4 +5 +6 +7
00 IMM0 IMM1 IMM2 IMM3 IMM4 IMM5 IMM6 IMM7
08 IMM8 IMM9 IMM10 IMM11 IMM12 IMM13 IMM14 IMM15
10 IMM16 IMM17 IMM18 IMM19 IMM20 IMM21 IMM22 IMM23
18 IMM24 IMM25 IMM26 IMM27 IMM28 IMM29 IMM30 IMM31
20 IMM32 IMM33 IMM34 IMM35 IMM36 IMM37 IMM38 IMM39
28 IMM40 IMM41 IMM42 IMM43 IMM44 IMM45 IMM46 IMM47
30 IMM48 IMM49 IMM50 IMM51 IMM52 IMM53 IMM54 IMM55
38 IMM56 IMM57 IMM58 IMM59 IMM60 IMM61 IMM62 IMM63
40 IMM-64 IMM-63 IMM-62 IMM-61 IMM-60 IMM-59 IMM-58 IMM-57
48 IMM-56 IMM-55 IMM-54 IMM-53 IMM-52 IMM-51 IMM-50 IMM-49
50 IMM-48 IMM-47 IMM-46 IMM-45 IMM-44 IMM-43 IMM-42 IMM-41
58 IMM-40 IMM-39 IMM-38 IMM-37 IMM-36 IMM-35 IMM-34 IMM-33
60 IMM-32 IMM-31 IMM-30 IMM-29 IMM-28 IMM-27 IMM-26 IMM-25
68 IMM-24 IMM-23 IMM-22 IMM-21 IMM-20 IMM-19 IMM-18 IMM-17
70 IMM-16 IMM-15 IMM-14 IMM-13 IMM-12 IMM-11 IMM-10 IMM-9
78 IMM-8 IMM-7 IMM-6 IMM-5 IMM-4 IMM-3 IMM-2 IMM-1
80 Acc Acc UAC C Acc Acc UAC C
88 GPUL GPUL GPUH GPUH GPUPL GPUPL GPUPH GPUPH
90 IN IN IN IN IN IN IN IN
98 [ER++] [ER++] [ER++] [ER++] [ER++] [ER++] [ER++] [ER++]
A0 i j k Inv ijk ijk ijk ijk
A8 i j k Inv ijk ijk ijk ijk
B0 CALL0 CALL1 CALL2 CALL3 CALL4 CALL5 CALL6 CALL7
B8 CALL8 CALL9 CALLA CALLB CALLC CALLD CALLE CALLF
C0 [X+1] [X+2i+1] [X+2j+1] [X+4j+1] [X+i] [X+3i] [X+2j+i] [X+4j+i]
C8 [X+k] [X+2i+k] [X+2j+k] [X+4j+k] [X+i^j] X [X+2j+i^j] [X+4j+i^j]
D0 [Y+1] [Y+2i+1] [Y+2j+1] [Y+Treug[j]+1] [Y+i] [Y+3i] [Y+2j+i] [Y+Treug[j]+i]
D8 [Y+k] [Y+2i+k] [Y+2j+k] [Y+Treug[j]+k] [Y+i^j] Y [Y+2j+i^j] [Y+Treug[j]+i^j]
E0 [Z+1] [Z+2i+1] [Z+2j+1] [Z+4j+1] [Z+i] [Z+3i] [Z+2j+i] [Z+4j+i]
E8 [Z+k] [Z+2i+k] [Z+2j+k] [Z+4j+k] [Z+i^j] Z [Z+2j+i^j] [Z+4j+i^j]
F0 [SP+1] [SP+2i+1] [SP+2j+1] [SP++] [SP+i] [SP+3i] [SP+2j+i] [i-1+SP++]
F8 [SP+k] [SP+2i+k] [SP+2j+k] [--SP+k] [SP] SP [SP+2j] [--SP]


Неплохо: конфликтов (нескольких команд, занимающих один адрес, или наоборот, адрес, не приписанный никому) не обнаружено, а "серых пятен" (дублирующихся команд) становится всё меньше. С одной стороны, чем больше у нас "уникальных" команд - тем сложнее становится QuatCore, зато коды команд используются всё более эффективно.

Ещё нужно разобраться с тем, как яркость у нас поступает инвертированной...


Сделано это, дабы сберечь 12 ЛЭ в модуле, который находит либо максимальную яркость и координату самой яркой точки, либо сумму яркостей пикселей на отрезке. Сначала мы решили суммировать "в минус", но это оказалось очень напряжно для последующего "взвешенного суммирования", поэтому суммировать стали нормально, а вот максимальную яркость находить с инверсией.

Далее, на выходе FIFO, на входе на шину QuatCore, никакого "расширения знака" мы делать не стали, поэтому можем не сомневаться: значением яркости всегда будет число от 0 до 4095 (в случае 12-битного АЦП), вот только 4095 будет означать вообще нулевую яркость, а 0 будет означать максимальную, те самые 4095.

Мы хотели ввести порог в 1/8 от максимальной яркости, это получалось число 512. Порог вычитался из значения яркости, и если результат получался отрицательным - мы не добавляли новую точку, справедливо полагая, что на этом отрезке ничего важного нет. Сейчас выходит, что и значение порога необходимо инвертировать. Если было 512 = 0x200 = 0010_0000_0000, то должно стать 1101_1111_1111 = 0xDFF = 3583 = 4095-512. Логично :) И теперь, если мы из полученной яркости на отрезке вычитаем порог и получаем отрицательное значение, то это, напротив, означает превышение порога обнаружения!

Ничего шибко сложного, переправляем.

Вот она, наша программа:
;громко думаем, как организовать видеоообработку
;наилучшее качество получим с сигнала CVI, т.к у него длительность видимой строки максимальная, 43 мкс, и 10,3 мкс на обратный ход
;у нас есть не более 308 тактов для подготовки к следующей строке!

;в 1205ХВ014 логика будет существенно отличаться: сканирование "в два луча", всего 8 тактов на обратный ход
;но давайте решать проблемы по мере их поступления. Потренируемся на кошках, то бишь на аналоговых камерах высокой чёткости...

%include "QuatCoreConsts.inc"
%include "Win1251.inc"
.rodata
	VSync			EQU	0x8000	;ожидание кадрового синхроимпульса, значение для регистра произвольное
	HSync			EQU	0x4000	;ожидание строчного синхроимпульса, а затем ещё интервал front porch (между импульсом и началом картинки)
	WholeRow		EQU	1023		;для обработки всей строки целиком	
	ImgHeight		EQU	720		;720p пока что 
	Threshold		EQU	0xDFF		;минимальная яркость пятен, сейчас поставили 1/8 от "насыщения" (4096), но с инверсией, так надо!

	;строка для установки тактовой частоты 25 МГц и отключения Ethernet-контроллера (чтобы лишнего не жрал)
	EthDisable	dw	2,3,0x22,0x54,0x00,0x01,3,0x22,0x66,0x00,0x18,2,0x22,0x6F,0x02
	; Hello	Int16 'Привет лунатикам! (через СОЗУ)',0x0D,0x800A
	
.data
	Nil		EQU	-32768	;традиционно это 0, но у нас нет флага нуля! Поэтому куда удобнее гигантское отриц. значение, всяко у нас 65535 слов памяти не наберётся, надеемся что 256..512 слов вполне хватит
	
	;расчётный диаметр пятна. Вообще, должен грубо устанавливаться исходя из экспозиции кадра, затем корректироваться по вычисленной дальности
	;но пока, для отладки, сразу зададим 3
	D1		dw	3
	;ДИВАН
	;в дальнейшем будет ещё R2 - радиус МДД, когда работаем в ближней зоне (они больше не используются для измерения, но нужно проверять их наличие)

	ActivePoints	dw	APHeadDummyX
	AllPoints	dw	Nil	
	APHeadDummyX	dw	Nil	;когда будем сверять с X-координатой ПРЕДЫДУЩЕЙ ТОЧКИ, это позволит не вводить лишних проверок!
	Heap		dw	Elem0	
	APTailDummyX	dw	32767
	;возможно, и оставшиеся значения Y, Lum, Path могут пригодится, а пока пущай будут чем угодно
	
	;область для списков точек, для начала она вся объединена в список Heap
	Elem0:
	Elem0Next	dw	Elem1
	Elem0X		dw	?
	Elem0Y		dw	?
	Elem0L		dw	?
	Elem0P		dw	?
	Elem1		dw	Elem2,?,?,?,?
	Elem2		dw	Elem3,?,?,?,?
	Elem3		dw	Elem4,?,?,?,?
	Elem4		dw	Elem5,?,?,?,?
	Elem5		dw	Elem6,?,?,?,?
	Elem6		dw	Elem7,?,?,?,?
	Elem7		dw	Elem8,?,?,?,?
	Elem8		dw	Elem9,?,?,?,?
	Elem9		dw	ElemA,?,?,?,?
	ElemA		dw	ElemB,?,?,?,?
	ElemB		dw	ElemC,?,?,?,?
	ElemC		dw	ElemD,?,?,?,?
	ElemD		dw	ElemE,?,?,?,?
	ElemE		dw	ElemF,?,?,?,?
	ElemF		dw	Elem10,?,?,?,?
	Elem10		dw	Elem11,?,?,?,?
	Elem11		dw	Elem12,?,?,?,?
	Elem12		dw	Elem13,?,?,?,?
	Elem13		dw	Elem14,?,?,?,?
	Elem14		dw	Elem15,?,?,?,?
	Elem15		dw	Elem16,?,?,?,?
	Elem16		dw	Elem17,?,?,?,?
	Elem17		dw	Elem18,?,?,?,?
	Elem18		dw	Elem19,?,?,?,?
	Elem19		dw	Elem1A,?,?,?,?
	Elem1A		dw	Elem1B,?,?,?,?
	Elem1B		dw	Elem1C,?,?,?,?
	Elem1C		dw	Elem1D,?,?,?,?
	Elem1D		dw	Elem1E,?,?,?,?
	Elem1E		dw	Elem1F,?,?,?,?
	Elem1F		dw	-32768,?,?,?,?

	Stack	dw	?,?
.code
	main proc
		; SetClock proc
		; ;конфигурирует Ethernet-контроллер на частоту 25 МГц и отключает собственно Ethernet
		; ;SP=0 при включении питания, на ПЛИС с этим строго
					; SIO		ETH
					; j		[SP++]	;количество посылок (счёт с нуля)
			; @@EthWord:	k		[SP++]	;количество байт в посылке (счёт с нуля)
			; @@EthByte:	OUT		[SP++]
					; kLOOP		@@EthByte
					; ;нужно, чтобы nCS сбросилось в единицу
					; NOP		IN
					; jLOOP		@@EthWord
		; SetClock endp				
				
		SP		Stack	;инициализация стека.
		ProcessFrame proc
			;серьёзные подготовительные операции здесь
			;времени вагон (100 мс, при том что кадр мы считываем за 21 мс при такт. частоте 25 МГц. 
			;вообще, хотелось бы успеть ещё быстрее - желательно на 50 МГц выйти, а то и на 100 МГц.
			;чтобы снизить эффект Rolling Shutter
			
			;в том числе, номер строки устанавливаем в ноль		
						j		1	;отныне и во веки веков!	
						;ждём кадрового синхроимпульса
						ACQ		VSync		;"застревает" до тех пор, пока кадровый импульс не придёт
						;пропускаем "пустые строки" вверху изображения (нужно только для аналоговой камеры, в цифровой такой фигни нет вроде бы...)
						k		10
						Z		D1												
			@@topRows:		ACQ		HSync
						kLOOP		@@topRows
						;теперь k=0, пусть так и остаётся, наши [X+k], [Y+k], [Z+k] будут означать [X],[Y],[Z], очень надо для списков
						
						;теперь начинаются информативные строки изображения, сейчас их 720, позже может стать 1024. 
						C		[Z+k]			;ожидаемый диаметр точки, всё-таки штука весьма нужная, лучше держать локальной перем.												
						[SP+2j]	ImgHeight		;в [SP+2] храним номер строки, начинать надо с 720, ибо сразу же единичку вычитаем
						
						JMP		@@FinalRange	;чтобы СНАЧАЛА отправить запрос, а потом уже смотреть результат, так почему-то удобнее					
						;можем заменить на JNO, переполнения здесь случаться не должно

						;НАЧАЛО ЦИКЛА ПО СТРОКАМ
			@@newRow:		X		ActivePoints	;начинаем цикл по всем активным точкам
			
			;даже если список пуст, первую половину цикла мы пройдём, она касается отрезка, предшествующего точке из списка
			@@ActPointsStart:	[SP+2j+1]	GPUL			;яркость самой яркой точки на отрезке
						[SP+1]		GPUH			;соотв. координата
						Acc		Threshold		;поставили задом наперёд, чтобы избежать Hazard'а	
						SUB		[SP+2j+1]		;вычитаем порог, положит. значение свидетельствует о новом найденном "пятне"						
						JL		@@MidCycle		;пятна нет, не торопимся заказывать отрезок
						;пятно есть. Надо проверить: не сливается ли оно с пятном слева или с пятном справа
						;X сейчас указывает на точку, расположенную слева (возможно, фиктивную, нам не важно)
						;коорд. текущей точки лежит в Acc, её яркость в C
						Z		X		;Чтобы в Merge всегда использовать Z, независимо от того, левая или правая точка оказались
						i		1
			@@checkCycle:		Acc		[SP+1]
						SUB		[Z+2j+k]
						ABS		Acc
						[SP]		Acc		;модуль разности - это и будет новый "диаметр" точки, если сработает слияние
						DIV2S		C		;не будем мудрить пока что, всё "в лоб"
						DIV2S		[Z+1]						
						JL		@@DoMerge
						Z		[X+k]
						iLOOP		@@checkCycle
						;если попали сюда, значит слияния не было, и точку всё-таки надо добавить!
											
						;[SP+2j+1]=[SP+3] указывает яркость этой точки, [SP+2j]=[SP+2]: Y-координату, [SP+1]: X-координата
						;а куда вставить, указывает рег. X
						;в Acc содержится модуль разности между правой точкой и текущей, за вычетом двух радиусов
						Y		Heap									
						CALL		ListOp	;добавит в X новую, пока что пустую точку.
						;если добрались досюда, значит памяти хватило. X содержит адрес указателя на новую точку, а вот Z содержит адрес самой точки!
						;наполним её содержимым!						
						X		Z		;для дальнейшей работы (после MidCycle)... Чтобы мы не взялись за только что добавленную точку!
						[Z+1]		C		;гориз. размер точки (будет расти при слияниях, а поначалу D, т.к мы сюда включаем и )																														
						CALL		AcqQueue

						JMP		@@MidCycle	;пропускаем слияние точек, 
						;можно заменить на JNO, у нас значения все мааленькие
			;а вот и слияние. В простейшем случае увеличиваем размер точки, и на этом считаем работу выполненной...
			@@DoMerge:		[Z+1]		[SP]	;пока так			
			;теперь надо посмотреть - а есть ли точка? Или мы уже прошли последний отрезок между точками?
			;но у нас в конце списка правая фиктивная точка - её надо игнорировать!			
			@@MidCycle:		Y		X
						X		[X+k]								
						Acc		0
						SUB		[X+k]
						JGE		@@FinalRange	;наткнулись на NULL, значит, все точки обработали...						
			;запрашиваем отрезок, где ожидаем точку. Первой придёт яркость, как всегда
						[SP+2j+1]	GPUL		;яркость
						Acc		GPUL
						SUB		[X+2j+1]	;сравниваем по яркости						
						[SP+1]		GPUH		;пока не забыли - сразу второе слово запрашиваем (коорд точки), чтобы не сбить весь FIFO 
						JGE		@@NotSoBright	;похоже, на спад пошло
						;нужно обновить координаты и яркость нашей яркой точки, а заодно и "размер" прибавить					
						;хорошо, "заказываем" два отрезка для этой точки: первый "на поиск", второй "на коррекцию".
			@@QueueAnyway:		CALL		AcqQueue						
						JMP		@@ActPointsStart	;завершили цикл					
						;ветвь исполнения, если старая точка оказывалась ярче.
						;Нужно сообразить: не пора ли ей на покой, то бишь в список AllPoints?
						;в кои-то веки работаем с Y-координатой!
			@@NotSoBright:		Acc		[X+2j+1]	;Y-координата ранее обнар. точки. Сейчас 
						SUB		[SP+2j]	;текущий номер строки, чем меньше - тем НИЖЕ, идём от 719 (или от 1024) до нуля. При этом вычитании всегда получаем положительное значение
						DIV2S		[X+1]		;ну и размер точки, в кои-то веки уполовиненный
						JL		@@QueueAnyway	;не так уж эта точка далеко, попробуем всё равно 
						;пора эту точку перетаскивать в другой список... 
						X		AllPoints
						CALL		ListOp
						X		Y		;теперь [X] ссылается уже не на обработанную и удалённую точку, а на следующую, так что всё верно
						JMP		@@ActPointsStart	;ура...					
						;до PixelsProcessed проскрипели, теперь последний участок запросить на обработку, и синхроимпульс для кучи
			@@FinalRange:		ACQ		WholeRow
						ACQ		HSync
						Acc		[SP+2j+1]
						SUB		1
						JGE		@@newRow
						;усё, отстрелялись...
		ProcessFrame endp
			
	OutOfMemory:	NOP		0		
	@@endless: 	JMP 		@@endless
	main endp	
	
;взять первый элемент из списка Y и перецепить его в начало списка X
;значения X,Y остаются неизменными,
;при вызове должно быть k=0, затираем регистр Z, Acc и флаг знака
;адрес возврата хранится в стеке, как положено

	ListOp proc			
			Acc		[Y+k]
			Z		Acc			
			SUB		0			
			[Y+k]		[Z+k]
			JL		@@OutOfMem			
			[Z+k]		[X+k]
			[X+k]		Z
			NOP		0	;избежать Warning'а. Как убрать этот Hazard - понятия не имею.. Разве что для вызова функции вместо стека применить регистр C? Возможно...
			JMP		[--SP]
	@@OutOfMem:	[--SP]		Call(OutOfMemory)	;SP возвращается на место, но выпрыгиваем в другое место, на обработку ошибки
	ListOp endp

;"заказать" видеопроцессору два отрезка на обработку,
;первый - на поиск новой точки (т.е здесь мы пока не ожидаем ничего найти),
;второй - на уточнение координат существующей
;в [SP+1], теперь уже [SP], лежит X-координата
;в [SP+2j]=[SP+2], теперь уже [SP+1] - Y-координата точки,
;в [SP+2j+1]=[SP+3], теперь уже [SP+2j]=[SP+2] - яркость точки
;в зависимости от знака, либо обновим значения, либо не будем

;в [X+1] лежит радиус точки,
;в [X+2j+k]=[X+2] - X-координата
;в [X+2j+1]=[X+3] - Y-координата
;в [X+4j+k]=[X+4] - яркость

	AcqQueue proc	
			i		2	;даже если не нужно обновить точку, не навредит... (это мы Hazard убираем мучительно!)
			JL		@@queue
			;здесь обновляем точку
	@@updCycle:	[X+2j+i]	[SP+i]
			iLOOP		@@updCycle
	@@queue:	Acc		[X+2j+k]
			DIV2S		[X+1]	;теперь в аккмуляторе у нас X - Ceil(D/2)
			ACQ		Acc		;первый отрезок
			ADD		[X+1]	;а вот теперь X + Floor(D/2)
			ACQ		Acc		;второй отрезок
			JMP		[--SP]
	AcqQueue endp	

Увы, без красивой подсветки синтаксиса: козёл Фрэнк жалуется "Запись слишком большая!". Да, там html-кода немножко перебор.
Код этот успешно компилируется, вот наиболее важная информация:

Ширина адреса сегмента кода (и регистра PC):           7   
Ширина адреса сегмента данных (и регистров X,Y,Z,SP):  8   
Ширина сумматора для относительных прыжков:            7   
Количество инициализированных слов данных:             183 
Количество инициализированных слов кода:               81  
Количество адресов процедур:                           3  


И создаётся 4 выходных файла: QuatCoreCode.mif, QuatCoreData.mif (содержание памяти кода и памяти данных), QuatCoreCallTable.v (таблица вызовов функций) и QuatCoreImmTable.v (таблица непосредственных значений). Копируем их в проект для Quartus'а. Задаём параметры QuatCore:
EnableSRAM = 0
AccWidth = 32
RamWidth = 8
ijkEnabled = 0
EnableIO = 0
RelAddrWidth = 7
RomWidth = 8

Иными словами, мы знаем, что внешняя статическая память SRAM и устройства ввода-вывода (UART, SPI, LCD, а там и MIL-STD1553 когда-нибудь) тоже пока не нужны. Аккумулятор пока тоже шибко широкий не нужен на самом деле, для сложений и вычитаний вполне хватило бы "родных" 16 бит, плюс ещё один, старший бит, чтобы не было переполнения. Но пускай уж будет 32 бита... Команда ijk (положить 5-битные регистры i,j,k "друг на друга" в 16-битное слово, и 1-битный регистр Inv в придачу) тоже пока оказалось не нужной. Может, как за сопровождение возьмёмся, захочется её ввести. RamWidth = 8 - обязательно, чтобы влезло всё, что нам захотелось запихать в оперативную память. RelAddrWidth = 7 - тоже в соответствии с результатами компилятора, он увидел один ОЧЕНЬ ДЛИННЫЙ относительный прыжок в начало цикла обработки строки. А вот RomWidth = 8 - это мы памяти взяли вдвое больше, чем было необходимо, компилятор считал, что хватит и 27 слов, но если у нас будет RomWidth = RelAddrWidth, у нас внезапно образуется "счётчик для старших бит PC" с нулевой шириной, что страшно не понравится Quartus'у. Можно было бы там поставить директивы Generate, чтобы этот случай рассматривался особо, но пока не хочется... Да и нет особого смысла: один блок внутренней памяти (БВП, он же EAB) занимает как раз 512 байт, или 256 слов по 16 бит.

Запускаем синтез "схемы в сборе" (QuatCore + видеопроцессор) - и получаем на этот раз целых 991 ЛЭ (совсем недавно, вроде с той же схемой, было 949 ЛЭ), и тайминги заваливаем, максимальная частота выходит 24,94 МГц.

Похоже, камнем преткновения стал QuatCoreImm.v, поскольку выглядит он мерзопакостно:
//таблица непосредственных значений, сгенерированная под конкретный исполняемый код
module QuatCoreImmTable (input [7:0] SrcAddr, output [15:0] Q);
	wire[5:0] adr = SrcAddr[5:0];
	assign Q = 
		(adr==5'h00)?	16'h00B5:
		(adr==5'h01)?	16'h0001:
		(adr==5'h02)?	16'h8000:
		(adr==5'h03)?	16'h000A:
		(adr==5'h04)?	16'h000F:
		(adr==5'h05)?	16'h4000:
		(adr==5'h06)?	16'h007D:
		(adr==5'h07)?	16'h02D0:
		(adr==5'h08)?	16'h0036:
		(adr==5'h09)?	16'h0010:
		(adr==5'h0A)?	16'h0DFF:
		(adr==5'h0B)?	16'h0011:
		(adr==5'h0C)?	16'h0007:
		(adr==5'h0D)?	16'h0076:
		(adr==5'h0E)?	16'h0013:
		(adr==5'h0F)?	16'h0022:
		(adr==5'h10)?	16'h0000:
		(adr==5'h11)?	16'h000E:
		(adr==5'h12)?	16'h000B:
		(adr==5'h13)?	16'h0079:
		(adr==5'h14)?	16'h03FF:
		(adr==5'h15)?	16'h004E:
		(adr==5'h16)?	16'h003C:
		(adr==5'h17)?	16'h0003:
				16'hxxxx;
endmodule


А синтезируется это безобразие в 57 ЛЭ - многовато на мой вкус. Такого понятия, как максимально допустимая тактовая частота, для этого модуля не существует (он чисто комбинаторный), а вот tpd (Propagation Delay) составляет 30 нс, что при одном периоде тактовой частоты в 40 нс как-то подозрительно...


Как всегда, "должок" нас нагнал! Надо всё-таки стиснуть зубы и сделать нормальный синтез таблицы непосредственных значений, в соответствии с этим, а не так, как сейчас...

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

UPD. Попробовал снизить ширину аккумулятора с 32 бит до 19 бит "на первое время" - число ЛЭ упало с 991 до 952, но всё равно завалили тайминги, причём если раньше было 2 failed paths, то теперь их 16. Ниже 19 бит пока сделать нельзя - мы ожидаем, что 1 бит будет старший, ещё 16 "основных", ещё один, в который помещается единичка для округления, и ВСЕ ОСТАЛЬНЫЕ, каковых должен быть хотя бы один.

А с шириной аккумулятора 24 бита (понадеялся на "магию чисел" - каждые 8 ЛЭ объединены в LAB, Logic Array Block, и иногда при синтезе это предпочтительнее, чем откусывать отдельные ЛЭ) - получилось 967 ЛЭ, но в этот раз максимальная частота вообще 22 МГц и свыше 2600 failed paths. Вот уж действительно магия!

Любое изменение настроек фиттера почему-то ухудшает тайминги, даже если по описанию должно их улучшать. Вот жеж...
Tags: ПЛИС, программки, работа, странные девайсы
Subscribe

  • Нахождение двух самых отдалённых точек

    Пока компьютер долго и упорно мучал симуляцию, я пытался написать на ассемблере алгоритм захвата на ближней дистанции. А сейчас на этом коде можно…

  • Слишком общительный счётчик

    Вчера я чуть поторопился отсинтезировать проект,параметры не поменял: RomWidth = 8 вместо 7, RamWidth = 9 вместо 8, и ещё EnableByteAccess=1, чтобы…

  • Балансируем конвейер QuatCore

    В пятницу у нас всё замечательно сработало на симуляции, первые 16 миллисекунд полёт нормальный. А вот прошить весь проект на ПЛИС и попробовать "в…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 2 comments