nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

Обнаружение точек "вживую" - отладка продолжается

Если подумать, в прошлый раз ничего заработать и не могло, и опять по причине излишней нашей "шустрости".

Процесс запуска сейчас выглядит так: я поворачиваю тумблер (подаю питание) - и в ПЛИС загружается старая прошивка, та, в которой ИК подсветка отключена. Камера получает питание - и настраивает экспозицию худо-бедно.

Когда я прошиваю ПЛИС с компьютера, примерно секунду все её выводы переходят в режим входа с "подтяжкой к +3,3 вольта" резисторами порядка 12 кОм. Этого недостаточно, чтобы включить ИК-подсветку, поэтому и эту секунду она отключена, и камера, как не в чём не бывало продолжает выдавать картинку.

И вот, наконец, начинается работа - первым же делом ИК подсветка включается, и по первому же кадровому синхроимпульсу мы начинаем обрабатывать изображение. И тут никуда не денешься - оно получается ПЕРЕСВЕЧЕННЫМ! Практически весь "экран" белый, любая точка проходит порог обнаружения.

По нашему алгоритму, на первой строке мы "обнаруживаем" одну точку (скорее всего в левом углу), и для второй строки даём уже 2-3 задания на обработку, на каждом из них снова "обнаруживая" по точке, и тут уж действительно либо мы "закопаемся" в обработке каждой из них (а новый пиксель идёт КАЖДЫЙ ТАКТ, тогда как обработка "обнаруженных точек" на процессоре куда медленнее) и тем самым не успеваем "озадачить" видеопроцессор, либо всё-таки успеваем - но быстро забиваем те 32 позиции, которые были выделены в памяти, и получаем Out of heap memory.

"Кто виноват" - понятно. Осталось понять, "что делать"...


На прошивку этой ПЛИС уходит секунда, и всё это время ИК подсветка отключена. То есть, даже если записать ту же программу в конфигуратор ПЛИС (во флэш-память), всё равно будет эта секунда отключённой подсветки, за которую экспозиция увеличится, и при дальнейшем включении получим пересвет.

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

		j	12
@@WaitMenu:	ACQ	VSync	;дождаться кадрового синхроимпульса
		jLOOP	@@WaitMenu


Там мы это делали, чтобы камера успела отработать "нажатие на джойстик" - и вывести экранное меню. Здесь дождёмся отработки изменившейся освещённости:
		j	24
@@StartupWait:	ACQ	VSync	;дождаться кадрового синхроимпульса
		jLOOP	@@StartupWait


Заодно чуть-чуть поменял отработку OutOfMemory, добавив эту метку в обработчик прерываний:

	;обработка прерываний
	OutOfMemory:	C	OutOfMemStr
			JMP	IntHandler
	GPU_WDT:	C	NoVideoSignal
			JMP	IntHandler
	GPU_UFLO:	C	UnderflowInt
			JMP	IntHandler
	GPU_OFLO:	C	OverflowInt
	IntHandler:	SIO	LCD
			X	CommonError
			CALL	print
			X	C
			CALL	print
	endless:	JMP	endless	


Теперь ошибка выводится чуточку пристойнее:


Чуть лучше... Батарейка и динамик - это символы 13 (0x0D) и 10 (0x0A), которые обычно означают CRLF (Carriage Return - Line Feed), то бишь перевод строки. Но ЖК-экранчик так их не воспринимает (есть специальные команды для установки позиции курсора), зато у него много полезных пиктограммок: разный заряд батареи, разная громкость или отключённый звук и пр. К следующему разу уберу.

Тем не менее, пока ничего серьёзно не поменялось! Да, теперь отчётливо видна секундная задержка между "приветственным сообщением" ЛОИ ВИПС v 0.01 и выводом одной из ошибок. Но ошибка возникает неизбежно, либо Out of heap memory, либо "исчерпание заданий GPU".

И главное, мы практически бродим впотьмах. Самое время всё-таки выдать какую-то информацию на компьютер, даже если обнаружение прервалось на пол-пути какой-то ошибкой.

Во-первых, скопипастим обнуление статической памяти, чтобы не задумываться, что было получено на текущем кадре, а что сохранилось ещё с предыдущего:
		;обнуление статической памяти, в отладочных целях
		ERL	0
		ERH	0
		j	29
@@OuterCLR:	Acc	24575		;24576 * 30 = 1024 * 720 
@@InnerCLR:	[ER++]	0
		SUB	1
		JGE	@@InnerCLR
		jLOOP	@@OuterCLR				


Во-вторых, вместо бесконечного цикла endless, в который сейчас приводила любая из ветвей исполнения программы (будь то штатная работа или одна из исключительных ситуаций), поставим ожидание ввода по UART, с последующей передачей либо картинки, либо (чуть позже) - дампа памяти:

	;рано или поздно мы попадаем сюда...				
	;endless:	JMP	endless	
	DebugLoop:	NOP	IN
	;чуть позже будем определять, какой символ нам дали, а пока тупо отправим картинку
			ERL	0
			ERH	0
				
			;самое смешное: у нас в 16-битный регистр не влезет то число, до которого делать итерации!
			;либо применить FMA, либо тупо 2 вложенных цикла
			j	29
	@@OuterLoop:	Acc	24575		;24576 * 30 = 1024 * 720 
	@@InnerLoop:	OUT	[ER++]
			SUB	1
			JGE	@@InnerLoop
			jLOOP	@@OuterLoop
			
			JMP	DebugLoop


Программа стремительно растёт в размерах, уже 205 слов кода, или 410 байт :) Но что интересно, при синтезе получилось чуть меньше ЛЭ, чем в прошлый раз: 1572 против 1588. А главное, синтезировалось, и в тайминги уложились. Это уже радостно.

Запускаем.


Тьфу, забыл SIO UART (выбрать устройство ввода-вывода UART) перед всей этой штукой. Как результат, мы "дожидаемся" ввода от ЖК-экранчика, вот только здесь "молчание-знак согласия", в смысле что если он АКТИВНО не выставил нам stall, значит, данные уже есть, любой мусор что очутился на шине данных :) И всю картинку мы протаскиваем через несчастный экранчик, и это даже имеет некую отладочную ценность: сложно было не заметить целые вереницы букв "я", что означает 0xFF, то бишь пересвеченную картинку. Может, ему и секунды не хватает для счастья?

Но всё-таки поставим SIO UART - и попробуем ещё разок...


Нда, чего-то странное. Одну ошибку уже нашёл: надо было после обнуления памяти снова выставить указатель на ноль. Попробуем:


О! Это уже куда интереснее. Во-первых, имеем серые строки на самом верху. Это мы гордо ввели константу
TopRows		EQU	28


но забыли в коде переправить число "9" на TopRows. Вот и результат - нам на экран попали неинформативные строки "выходящие за пределы экрана" (приходящиеся на обратный ход кадровой развёртки).

Вторая особенность: все эти всполохи внизу экрана. Я-то надеялся, что те области, до которых обработка так и не дошла, останутся полностью чёрными, поскольку статическую память мы в самом начале обнулили.

Похоже, это конфликт на шине. Мы только ВЫДАЛИ ЗАДАНИЯ видеопроцессору на ожидание кадровых синхроимпульсов - а сами тут же полезли обнулять память, доступ к которой идёт по той же самой 8-битной шине данных. Совсем "перетягивания каната" (когда включены несколько выходных каскадов и меряются силами) не получилось - в нашем "контроллере статической памяти" стоит блокировка:

//обращение к внешней памяти
//чуть потеснили IOselector в плане адресов.
//наши DestAddr: 01xx_xxxx
//наши SrcAddr:  1001_1xxx

//DestAddr:
//0100_xxxx - задать младшие 16 бит адреса, ERL (External memory Register Low)
//0101_xxxx - задать старшие 16 бит адреса, ERH (External memory Register High)
//011x_xxxx - записать в память и сделать инкремент, [ER++]

//SrcAddr:
//1001_1xxx - чтение из памяти и инкремент, [ER++]

//пока ReadEnable = 1, SRAM работает только от QuatCore и либо "забирает себе" шину данных, либо активирует один из чипов памяти на чтение, так что он "забирает" шину
//когда ReadEnable = 0, на шину влезает АЦП. Чтение из памяти блокируется
module QuatCoreFastSRAM (input clk, input [7:0] DestAddr, input [7:0] SrcAddr, input DestStall, input SrcStall, input SrcDiscard, input [15:0] D,
			input ReadEnable, input WriteFromADC,
			output [18:0] RAMaddr, output RAM_CE0, output RAM_CE1, output RAM_RW,
			inout [7:0] RAM_data);
						
wire IsOurDest = (~DestStall)&(~DestAddr[7])&DestAddr[6];
wire IsOurSrc = (~SrcStall)&(~SrcDiscard)&(SrcAddr[7:3] == 5'b1001_1)&ReadEnable;

wire LoadERL = IsOurDest & (~DestAddr[5]) & (~DestAddr[4]);
wire LoadERH = IsOurDest & (~DestAddr[5]) & DestAddr[4];
wire WriteMem = IsOurDest & DestAddr[5];

wire DoWriteFromADC = WriteFromADC & (~ReadEnable);

wire DoIncrement = WriteMem | IsOurSrc | DoWriteFromADC ;

wire [19:0] ER; //External memory Register
wire TC; //Terminal Count

lpm_counter ERL (	.clock (clk),
			.cnt_en (DoIncrement),
			.sload (LoadERL),
			.data (D),
			.Q (ER[15:0]),
			.cout (TC));
defparam
	ERL.lpm_direction = "UP",
	ERL.lpm_port_updown = "PORT_UNUSED",
	ERL.lpm_type = "LPM_COUNTER",
	ERL.lpm_width = 16;
	
lpm_counter ERH (	.clock (clk),
			.cnt_en (TC & DoIncrement),
			.sload (LoadERH),
			.data (D[3:0]),
			.Q (ER[19:16]));
defparam
	ERH.lpm_direction = "UP",
	ERH.lpm_port_updown = "PORT_UNUSED",
	ERH.lpm_type = "LPM_COUNTER",
	ERH.lpm_width = 4;
	
assign RAMaddr = ER[19:1];
assign RAM_CE0 = ~(ER[0]&(ReadEnable | WriteMem | DoWriteFromADC));
assign RAM_CE1 = ~(~ER[0]&(ReadEnable | WriteMem | DoWriteFromADC));
assign RAM_RW = ((~WriteMem) & (~DoWriteFromADC)) | clk;

assign RAM_data = (WriteMem&ReadEnable)? D[7:0] : 8'bzzzz_zzzz;

endmodule


Когда включается АЦП (для оцифровки изображения), ReadEnable=0. И пока обрабатываются кадровые синхроимпульсы, также WriteFromADC=0. Как видно, в такой ситуации QuatCore "освобождает шину" (ставит Z-состояние с высоким выходным импедансом), но выполнение операции не приостанавливает. Собственно, пока статическая память была "в нашем полном распоряжении", мы и подумать не могли, что запись в неё может вызвать заминку!

Решений, как всегда, несколько.
Мы могли бы добавить недостающие выходы SrcStallReq и DestStallReq, так что если мы обращаемся к статической памяти со стороны QuatCore, программа останавливается, пока шина не освободится.

Либо разобраться "программно". Наверное, выберем последнее. Вот так примерно будет выглядеть наша "подготовительная работа" по ожиданию 1 секунды и обнулению памяти:
		;секундная задержка, чтобы камера настроила экспозицию
		;когда "зациклим" нашу обработку - можно будет пропустить
		j	24
@@StartupWait:	ACQ	VSync	;дождаться кадрового синхроимпульса
		jLOOP	@@StartupWait
		;ожидаем полного завершения работы с видеопроцессором
		ACQ	WholeRow	;какая-нибудь команда, которая даст результат (VSync/Hsync сбрасывают очередь результатов, они нам не подойдут)
		NOP	GPUH		;ждём результатов!
			
		;обнуление статической памяти, в отладочных целях
		ERL	0
		ERH	0
		j	29
@@OuterCLR:	Acc	24575		;24576 * 30 = 1024 * 720 
@@InnerCLR:	[ER++]	0
		SUB	1
		JGE	@@InnerCLR
		jLOOP	@@OuterCLR				
			
		;и вернуть указатель статической памяти на ноль
		ERL	0
		ERH	0


Что ж, попробуем и ещё разок. Программа разжирела аж до 210 слов кода (420 байт).


Эх, вот только тебя и не хватало...

Это опять проделки Place&Route - иногда он хорошо разводит, а иногда не очень. А поскольку сейчас поменялись только модули QuatCoreImmTable.v и QuatCoreCallTable.v (таблица непосредственных значений и таблица адресов процедур), то дело может быть в них. Так-то "проваленные" пути указаны другие, но так часто бывает - понимая, что нужно укоротить путь в одном месте, он удлиняет другие пути. Глянем, во что нынче превратился QuatCoreImmTable.v:

//таблица непосредственных значений, сгенерированная под конкретный исполняемый код
module QuatCoreImmTable (input [7:0] SrcAddr, output [15:0] Q);
	wire[6:0] adr = SrcAddr[6:0];
	assign Q[0]=adr[6];
	assign Q[1]=adr[5];
	assign Q[2]=adr[4];
	assign Q[4]=adr[3];
	assign Q[6]=adr[2];
	assign Q[11]=adr[5];
	assign Q[12]=adr[1];
	wire [1:0] adr15={adr[1],adr[0]};
	assign Q[15]=
		(adr15==2'b00)?	1'b0:
		(adr15==2'b10)?	1'b1:
		(adr15==2'b01)?	1'b0:
			1'b0;
	wire [1:0] adr14={adr[4],adr[1]};
	assign Q[14]=
		(adr14==2'b00)?	1'b0:
		(adr14==2'b10)?	1'b0:
		(adr14==2'b01)?	1'b0:
			1'b1;
	wire [1:0] adr13={adr[1],adr[0]};
	assign Q[13]=
		(adr13==2'b00)?	1'b0:
		(adr13==2'b10)?	1'b1:
		(adr13==2'b01)?	1'b0:
			1'b0;
	wire [5:0] adr10={adr[6],adr[5],adr[3],adr[2],adr[1],adr[0]};
	assign Q[10]=
		(adr10==6'b000000)?	1'b0:
		(adr10==6'b001000)?	1'b0:
		(adr10==6'b000010)?	1'b0:
		(adr10==6'b010010)?	1'b0:
		(adr10==6'b110010)?	1'b1:
		(adr10==6'b011010)?	1'b1:
		(adr10==6'b111010)?	1'b1:
		(adr10==6'b111110)?	1'b1:
		(adr10==6'b000001)?	1'b0:
		(adr10==6'b100001)?	1'b0:
		(adr10==6'b010001)?	1'b1:
		(adr10==6'b001001)?	1'b1:
		(adr10==6'b011001)?	1'b1:
		(adr10==6'b000101)?	1'b1:
		(adr10==6'b100101)?	1'b1:
		(adr10==6'b001101)?	1'b0:
		(adr10==6'b101101)?	1'b0:
		(adr10==6'b111101)?	1'b1:
		(adr10==6'b100011)?	1'b0:
		(adr10==6'b110011)?	1'b0:
		(adr10==6'b001011)?	1'b1:
		(adr10==6'b101011)?	1'b0:
		(adr10==6'b111011)?	1'b0:
		(adr10==6'b100111)?	1'b1:
		(adr10==6'b010111)?	1'b0:
		(adr10==6'b001111)?	1'b1:
			1'b1;
	wire [6:0] adr9={adr[6],adr[5],adr[4],adr[3],adr[2],adr[1],adr[0]};
	assign Q[9]=
		(adr9==7'b0000000)?	1'b0:
		(adr9==7'b0100000)?	1'b0:
		(adr9==7'b0001000)?	1'b0:
		(adr9==7'b0000010)?	1'b0:
		(adr9==7'b0100010)?	1'b0:
		(adr9==7'b1100010)?	1'b0:
		(adr9==7'b0010010)?	1'b0:
		(adr9==7'b0101010)?	1'b1:
		(adr9==7'b1111010)?	1'b0:
		(adr9==7'b1111110)?	1'b1:
		(adr9==7'b0000001)?	1'b0:
		(adr9==7'b1000001)?	1'b0:
		(adr9==7'b0100001)?	1'b0:
		(adr9==7'b0001001)?	1'b1:
		(adr9==7'b0101001)?	1'b0:
		(adr9==7'b0011001)?	1'b0:
		(adr9==7'b0000101)?	1'b0:
		(adr9==7'b1000101)?	1'b0:
		(adr9==7'b1010101)?	1'b1:
		(adr9==7'b0001101)?	1'b1:
		(adr9==7'b1001101)?	1'b1:
		(adr9==7'b1111101)?	1'b1:
		(adr9==7'b0000011)?	1'b0:
		(adr9==7'b1000011)?	1'b0:
		(adr9==7'b1010011)?	1'b0:
		(adr9==7'b0110011)?	1'b0:
		(adr9==7'b1110011)?	1'b0:
		(adr9==7'b1001011)?	1'b0:
		(adr9==7'b1101011)?	1'b0:
		(adr9==7'b0011011)?	1'b0:
		(adr9==7'b0000111)?	1'b0:
		(adr9==7'b1000111)?	1'b1:
		(adr9==7'b0110111)?	1'b0:
		(adr9==7'b0001111)?	1'b0:
			1'b1;
	wire [5:0] adr8={adr[6],adr[4],adr[3],adr[2],adr[1],adr[0]};
	assign Q[8]=
		(adr8==6'b000000)?	1'b0:
		(adr8==6'b001000)?	1'b0:
		(adr8==6'b000010)?	1'b0:
		(adr8==6'b100010)?	1'b0:
		(adr8==6'b010010)?	1'b0:
		(adr8==6'b001010)?	1'b0:
		(adr8==6'b111010)?	1'b0:
		(adr8==6'b111110)?	1'b1:
		(adr8==6'b000001)?	1'b0:
		(adr8==6'b100001)?	1'b0:
		(adr8==6'b001001)?	1'b0:
		(adr8==6'b011001)?	1'b0:
		(adr8==6'b000101)?	1'b0:
		(adr8==6'b100101)?	1'b0:
		(adr8==6'b110101)?	1'b0:
		(adr8==6'b001101)?	1'b0:
		(adr8==6'b101101)?	1'b0:
		(adr8==6'b111101)?	1'b1:
		(adr8==6'b000011)?	1'b1:
		(adr8==6'b100011)?	1'b0:
		(adr8==6'b010011)?	1'b1:
		(adr8==6'b110011)?	1'b0:
		(adr8==6'b101011)?	1'b0:
		(adr8==6'b011011)?	1'b0:
		(adr8==6'b000111)?	1'b1:
		(adr8==6'b100111)?	1'b0:
		(adr8==6'b010111)?	1'b1:
		(adr8==6'b001111)?	1'b0:
			1'b1;
	wire [6:0] adr7={adr[6],adr[5],adr[4],adr[3],adr[2],adr[1],adr[0]};
	assign Q[7]=
		(adr7==7'b0000000)?	1'b1:
		(adr7==7'b0100000)?	1'b0:
		(adr7==7'b0001000)?	1'b0:
		(adr7==7'b0111000)?	1'b1:
		(adr7==7'b0000010)?	1'b0:
		(adr7==7'b1000010)?	1'b0:
		(adr7==7'b0100010)?	1'b1:
		(adr7==7'b1100010)?	1'b0:
		(adr7==7'b0010010)?	1'b1:
		(adr7==7'b1001010)?	1'b1:
		(adr7==7'b0101010)?	1'b0:
		(adr7==7'b0111010)?	1'b1:
		(adr7==7'b1111010)?	1'b0:
		(adr7==7'b0000110)?	1'b0:
		(adr7==7'b0110110)?	1'b0:
		(adr7==7'b1111110)?	1'b1:
		(adr7==7'b0000001)?	1'b0:
		(adr7==7'b1000001)?	1'b0:
		(adr7==7'b0100001)?	1'b0:
		(adr7==7'b1100001)?	1'b1:
		(adr7==7'b0010001)?	1'b1:
		(adr7==7'b0001001)?	1'b0:
		(adr7==7'b1001001)?	1'b1:
		(adr7==7'b0101001)?	1'b0:
		(adr7==7'b0011001)?	1'b0:
		(adr7==7'b0111001)?	1'b1:
		(adr7==7'b1111001)?	1'b1:
		(adr7==7'b0000101)?	1'b0:
		(adr7==7'b1000101)?	1'b0:
		(adr7==7'b1010101)?	1'b0:
		(adr7==7'b0110101)?	1'b1:
		(adr7==7'b0001101)?	1'b1:
		(adr7==7'b1001101)?	1'b1:
		(adr7==7'b0011101)?	1'b0:
		(adr7==7'b1111101)?	1'b1:
		(adr7==7'b0000011)?	1'b1:
		(adr7==7'b1000011)?	1'b1:
		(adr7==7'b0100011)?	1'b0:
		(adr7==7'b1100011)?	1'b0:
		(adr7==7'b0010011)?	1'b0:
		(adr7==7'b1010011)?	1'b0:
		(adr7==7'b0110011)?	1'b0:
		(adr7==7'b1110011)?	1'b0:
		(adr7==7'b1001011)?	1'b1:
		(adr7==7'b0101011)?	1'b0:
		(adr7==7'b1101011)?	1'b1:
		(adr7==7'b0011011)?	1'b0:
		(adr7==7'b1011011)?	1'b0:
		(adr7==7'b0111011)?	1'b0:
		(adr7==7'b1111011)?	1'b0:
		(adr7==7'b0000111)?	1'b1:
		(adr7==7'b1000111)?	1'b0:
		(adr7==7'b0100111)?	1'b0:
		(adr7==7'b0010111)?	1'b0:
		(adr7==7'b0110111)?	1'b0:
		(adr7==7'b1110111)?	1'b0:
		(adr7==7'b0001111)?	1'b0:
		(adr7==7'b1001111)?	1'b0:
		(adr7==7'b0011111)?	1'b0:
		(adr7==7'b1011111)?	1'b0:
			1'b1;
	wire [6:0] adr5={adr[6],adr[5],adr[4],adr[3],adr[2],adr[1],adr[0]};
	assign Q[5]=
		(adr5==7'b0000000)?	1'b1:
		(adr5==7'b0100000)?	1'b0:
		(adr5==7'b0001000)?	1'b1:
		(adr5==7'b0111000)?	1'b1:
		(adr5==7'b0000010)?	1'b0:
		(adr5==7'b1000010)?	1'b1:
		(adr5==7'b0100010)?	1'b1:
		(adr5==7'b1100010)?	1'b1:
		(adr5==7'b0010010)?	1'b1:
		(adr5==7'b1001010)?	1'b0:
		(adr5==7'b0101010)?	1'b0:
		(adr5==7'b0111010)?	1'b0:
		(adr5==7'b1111010)?	1'b0:
		(adr5==7'b0000110)?	1'b1:
		(adr5==7'b0110110)?	1'b0:
		(adr5==7'b1111110)?	1'b1:
		(adr5==7'b0000001)?	1'b0:
		(adr5==7'b1000001)?	1'b0:
		(adr5==7'b0100001)?	1'b1:
		(adr5==7'b1100001)?	1'b1:
		(adr5==7'b0010001)?	1'b0:
		(adr5==7'b0001001)?	1'b1:
		(adr5==7'b1001001)?	1'b1:
		(adr5==7'b0101001)?	1'b0:
		(adr5==7'b0011001)?	1'b1:
		(adr5==7'b0111001)?	1'b1:
		(adr5==7'b1111001)?	1'b1:
		(adr5==7'b0000101)?	1'b0:
		(adr5==7'b1000101)?	1'b1:
		(adr5==7'b1010101)?	1'b0:
		(adr5==7'b0110101)?	1'b0:
		(adr5==7'b0001101)?	1'b0:
		(adr5==7'b1001101)?	1'b0:
		(adr5==7'b0011101)?	1'b1:
		(adr5==7'b1111101)?	1'b1:
		(adr5==7'b0000011)?	1'b0:
		(adr5==7'b1000011)?	1'b1:
		(adr5==7'b0100011)?	1'b0:
		(adr5==7'b1100011)?	1'b0:
		(adr5==7'b0010011)?	1'b1:
		(adr5==7'b1010011)?	1'b0:
		(adr5==7'b0110011)?	1'b0:
		(adr5==7'b1110011)?	1'b0:
		(adr5==7'b1001011)?	1'b0:
		(adr5==7'b0101011)?	1'b1:
		(adr5==7'b1101011)?	1'b0:
		(adr5==7'b0011011)?	1'b0:
		(adr5==7'b1011011)?	1'b1:
		(adr5==7'b0111011)?	1'b0:
		(adr5==7'b1111011)?	1'b0:
		(adr5==7'b0000111)?	1'b0:
		(adr5==7'b1000111)?	1'b1:
		(adr5==7'b0100111)?	1'b0:
		(adr5==7'b0010111)?	1'b0:
		(adr5==7'b0110111)?	1'b0:
		(adr5==7'b1110111)?	1'b0:
		(adr5==7'b0001111)?	1'b0:
		(adr5==7'b1001111)?	1'b1:
		(adr5==7'b0011111)?	1'b0:
		(adr5==7'b1011111)?	1'b0:
			1'b1;
	wire [6:0] adr3={adr[6],adr[5],adr[4],adr[3],adr[2],adr[1],adr[0]};
	assign Q[3]=
		(adr3==7'b0000000)?	1'b0:
		(adr3==7'b0100000)?	1'b1:
		(adr3==7'b0001000)?	1'b0:
		(adr3==7'b0111000)?	1'b1:
		(adr3==7'b0000010)?	1'b0:
		(adr3==7'b1000010)?	1'b1:
		(adr3==7'b0100010)?	1'b0:
		(adr3==7'b1100010)?	1'b1:
		(adr3==7'b0010010)?	1'b0:
		(adr3==7'b1001010)?	1'b1:
		(adr3==7'b0101010)?	1'b1:
		(adr3==7'b0111010)?	1'b1:
		(adr3==7'b1111010)?	1'b1:
		(adr3==7'b0000110)?	1'b1:
		(adr3==7'b0110110)?	1'b1:
		(adr3==7'b1111110)?	1'b1:
		(adr3==7'b0000001)?	1'b0:
		(adr3==7'b1000001)?	1'b0:
		(adr3==7'b0100001)?	1'b0:
		(adr3==7'b1100001)?	1'b0:
		(adr3==7'b0010001)?	1'b1:
		(adr3==7'b0001001)?	1'b0:
		(adr3==7'b1001001)?	1'b0:
		(adr3==7'b0101001)?	1'b1:
		(adr3==7'b0011001)?	1'b0:
		(adr3==7'b0111001)?	1'b0:
		(adr3==7'b1111001)?	1'b1:
		(adr3==7'b0000101)?	1'b0:
		(adr3==7'b1000101)?	1'b1:
		(adr3==7'b1010101)?	1'b0:
		(adr3==7'b0110101)?	1'b0:
		(adr3==7'b0001101)?	1'b0:
		(adr3==7'b1001101)?	1'b0:
		(adr3==7'b0011101)?	1'b1:
		(adr3==7'b1111101)?	1'b1:
		(adr3==7'b0000011)?	1'b0:
		(adr3==7'b1000011)?	1'b0:
		(adr3==7'b0100011)?	1'b0:
		(adr3==7'b1100011)?	1'b0:
		(adr3==7'b0010011)?	1'b0:
		(adr3==7'b1010011)?	1'b1:
		(adr3==7'b0110011)?	1'b0:
		(adr3==7'b1110011)?	1'b1:
		(adr3==7'b1001011)?	1'b0:
		(adr3==7'b0101011)?	1'b0:
		(adr3==7'b1101011)?	1'b1:
		(adr3==7'b0011011)?	1'b1:
		(adr3==7'b1011011)?	1'b1:
		(adr3==7'b0111011)?	1'b0:
		(adr3==7'b1111011)?	1'b0:
		(adr3==7'b0000111)?	1'b0:
		(adr3==7'b1000111)?	1'b0:
		(adr3==7'b0100111)?	1'b1:
		(adr3==7'b0010111)?	1'b1:
		(adr3==7'b0110111)?	1'b0:
		(adr3==7'b1110111)?	1'b0:
		(adr3==7'b0001111)?	1'b1:
		(adr3==7'b1001111)?	1'b0:
		(adr3==7'b0011111)?	1'b0:
		(adr3==7'b1011111)?	1'b0:
			1'b1;
//Непосредственные значения и их адреса:
// Значение (dec) Значение (hex) Маска Адрес Где используется                                                                                    
// 2              0002           00FF  0023  SIO ETH/jLOOP SetClock::@EthWord/NOP 0/i 2/NOP 0/i++ 0/i++ 0                                        
// 3              0003           00FF  0063  kLOOP SetClock::@EthByte/ZACC RoundZero/ZACC RoundZero/ZACC RoundZero/ZACC RoundZero/ZACC RoundZero 
// 326            0146           07FF  0037  SP Stack/JGE ListOp::@OutOfMem                                                                      
// 145            0091           07FF  004B  SIO LCD/X InitLCD/SIO LCD                                                                           
// 1112           0458           07FF  000F  X Init9to18/j 24                                                                                    
// 384            0180           03FF  0003  OUT Init19/ERH 0/ERH 0/SIO UART/i 0/SIO UART/ERH 0                                                  
// 1633           0661           07FF  0047  X Row02/j 1/i 1                                                                                     
// 448            01C0           03FF  0007  OUT SetRow1                                                                                         
// 1129           0469           07FF  0045  X Row13                                                                                             
// 0              0000           FFFF  0001  ERL 0/[ER++] 0/ERL 0/[SP+2j] 0/ERL 0/[SP+1] 0/[SP+1] 0                                              
// 61             003D           00FF  005B  j 29/JL ListOp/j 29                                                                                 
// 24575          5FFF           FFFF  007F  Acc 24575/Acc 24575                                                                                 
// 1              0001           FFFF  0041  SUB 1/Acc Threshold/SUB 1                                                                           
// 23             0017           00FF  007B  JGE main::@InnerCLR                                                                                 
// 22             0016           00FF  003B  jLOOP main::@OuterCLR                                                                               
// 32768          8000           C3FF  0002  ACQ VSync/ACQ VSync                                                                                 
// 1052           041C           07FF  001B  jLOOP main::@StartupWait/k TopRows/X XStr                                                           
// 160            00A0           07FF  0000  Z D1                                                                                                
// 16646          4106           C3FF  0033  ACQ HSync/ACQ HSync                                                                                 
// 36             0024           00FF  0013  kLOOP ProcessFrame::@topRows/k 4                                                                    
// 104            0068           00FF  0006  JMP ProcessFrame::@FinalRange/JGE ProcessFrame::@FinalRange                                         
// 161            00A1           07FF  0043  X ActivePoints/Y ActivePoints                                                                       
// 85             0055           00FF  005F  JL ProcessFrame::@MidCycle/[SP++] ProcessFrame::@MidCycle                                           
// 84             0054           00FF  001F  JL ProcessFrame::@DoMerge                                                                           
// 50             0032           00FF  002B  iLOOP ProcessFrame::@checkCycle                                                                     
// 164            00A4           07FF  0012  Y Heap/JGE @@InnerLoop                                                                              
// 71             0047           00FF  0077  [SP++] ProcessFrame::@AfterListOp                                                                   
// 76             004C           00FF  0017  iLOOP AcqAndUpdate::@updCycle                                                                       
// 1067           042B           07FF  0062  [SP++] ProcessFrame::@ActPointsStart/X SizeStr                                                      
// 74             004A           00FF  0027  JL AcqAndUpdate                                                                                     
// 78             004E           00FF  0036  JGE AcqNoUpd                                                                                        
// 162            00A2           07FF  0022  X AllPoints/X AllPoints/Z AllPoints                                                                 
// 4095           0FFF           FFFF  007D  ACQ WholeRow/Acc 0xFFF                                                                              
// 720            02D0           FFFF  000D  SUB ImgHeight                                                                                       
// 721            02D1           FFFF  004D  ADD ImgHeightP1                                                                                     
// 41             0029           00FF  0042  JL ProcessFrame::@newRow                                                                            
// 113            0071           00FF  004F  [SP++] ProcessFrame::@Transfer                                                                      
// 1562           061A           07FF  002A  X PointsStr                                                                                         
// 140            008C           00FF  0011  JMP main::@moveOn                                                                                   
// 1050           041A           07FF  0029  X NumSign                                                                                           
// 1055           041F           07FF  007A  X YStr                                                                                              
// 1058           0422           07FF  0021  X LumStr                                                                                            
// 124            007C           00FF  001D  JL main::@outLoop                                                                                   
// 158            009E           00FF  003A  JMP DebugLoop/JMP DebugLoop                                                                         
// 1584           0630           FFFF  0009  C OutOfMemStr                                                                                       
// 153            0099           00FF  004A  JMP IntHandler/JMP IntHandler/JMP IntHandler                                                        
// 1076           0434           FFFF  0019  C NoVideoSignal                                                                                     
// 1088           0440           FFFF  0005  C UnderflowInt                                                                                      
// 1605           0645           FFFF  0055  C OverflowInt                                                                                       
// 15             000F           07FF  0073  X CommonError                                                                                       
// 163            00A3           00FF  0061  jLOOP @@OuterLoop                                                                                   
// 13             000D           03FF  0053  OUT 13                                                                                              
// 10             000A           03FF  0020  OUT 10                                                                                              
// 182            00B6           00FF  0039  JGE print::@finish                                                                                  
// 177            00B1           00FF  0049  JMP print::@start                                                                                   
// 65535          FFFF           FFFF  007E  [SP+1] -1                                                                                           
// 155            009B           07FF  006B  Y BCDtable                                                                                          
// 191            00BF           00FF  0079  JGE BetterBin2Bcd::@sub                                                                             
// 198            00C6           00FF  0035  iLOOP BetterBin2Bcd::@proceed                                                                       
// 190            00BE           00FF  0038  kLOOP BetterBin2Bcd::@start/kLOOP BetterBin2Bcd::@start                                             
// 48             0030           FFFF  0008  Acc '0'                                                                                             
endmodule

(пришлось убрать выделение цветом - козёл Фрэнк не может пережевать, "запись слишком большая")

Душераздирающее зрелище!

Да, эвристики хорошо работали на маленьких программах, а тут мы уже умудрились "забить" 60 непосредственных значений из 128 доступных, причём каждое из них стало длиннее, чем прежде, из-за уширения адресов как кода, так и данных!

В итоге эта хреновина синтезируется в 87 ЛЭ, но самое паршивое, что эти ЛЭ сосредотачиваются только в нескольких битах, существенно увеличивая время распространения до 31 нс, при длительности такта в 40 нс.

Попробуем вернуться к старому варианту построения:

//таблица непосредственных значений, сгенерированная под конкретный исполняемый код
module QuatCoreImmTable (input [7:0] SrcAddr, output [15:0] Q);
	wire[6:0] adr = SrcAddr[6:0];
	assign Q = 
		(adr==6'h00)?	16'h0002:
		(adr==6'h01)?	16'h0003:
		(adr==6'h02)?	16'h0146:
		(adr==6'h03)?	16'h0091:
		(adr==6'h04)?	16'h0458:
		(adr==6'h05)?	16'h0180:
		(adr==6'h06)?	16'h0661:
		(adr==6'h07)?	16'h01C0:
		(adr==6'h08)?	16'h0469:
		(adr==6'h09)?	16'h0000:
		(adr==6'h0A)?	16'h003D:
		(adr==6'h0B)?	16'h5FFF:
		(adr==6'h0C)?	16'h0001:
		(adr==6'h0D)?	16'h0017:
		(adr==6'h0E)?	16'h0016:
		(adr==6'h0F)?	16'h8000:
		(adr==6'h10)?	16'h041C:
		(adr==6'h11)?	16'h00A0:
		(adr==6'h12)?	16'h4106:
		(adr==6'h13)?	16'h0024:
		(adr==6'h14)?	16'h0068:
		(adr==6'h15)?	16'h00A1:
		(adr==6'h16)?	16'h0055:
		(adr==6'h17)?	16'h0054:
		(adr==6'h18)?	16'h0032:
		(adr==6'h19)?	16'h00A4:
		(adr==6'h1A)?	16'h0047:
		(adr==6'h1B)?	16'h004C:
		(adr==6'h1C)?	16'h042B:
		(adr==6'h1D)?	16'h004A:
		(adr==6'h1E)?	16'h004E:
		(adr==6'h1F)?	16'h00A2:
		(adr==6'h20)?	16'h0FFF:
		(adr==6'h21)?	16'h02D0:
		(adr==6'h22)?	16'h02D1:
		(adr==6'h23)?	16'h0029:
		(adr==6'h24)?	16'h0071:
		(adr==6'h25)?	16'h061A:
		(adr==6'h26)?	16'h008C:
		(adr==6'h27)?	16'h041A:
		(adr==6'h28)?	16'h041F:
		(adr==6'h29)?	16'h0422:
		(adr==6'h2A)?	16'h007C:
		(adr==6'h2B)?	16'h009E:
		(adr==6'h2C)?	16'h0630:
		(adr==6'h2D)?	16'h0099:
		(adr==6'h2E)?	16'h0434:
		(adr==6'h2F)?	16'h0440:
		(adr==6'h30)?	16'h0645:
		(adr==6'h31)?	16'h000F:
		(adr==6'h32)?	16'h00A3:
		(adr==6'h33)?	16'h000D:
		(adr==6'h34)?	16'h000A:
		(adr==6'h35)?	16'h00B6:
		(adr==6'h36)?	16'h00B1:
		(adr==6'h37)?	16'hFFFF:
		(adr==6'h38)?	16'h009B:
		(adr==6'h39)?	16'h00BF:
		(adr==6'h3A)?	16'h00C6:
		(adr==6'h3B)?	16'h00BE:
		(adr==6'h3C)?	16'h0030:
			16'hxxxx;
endmodule


Такая штука синтезируется в 90 ЛЭ - чуть больше, зато максимальное время распространения 22,8 нс.

Тут можно было бы и третий вариант предложить теперь: просто модуль памяти выделить под эту таблицу, благо мы их пока экономили, и заняли лишь 3 из 24. Ну и четвёртый: доработать эвристику, чтобы для тех выходов, которые выражаются через логические элементы, и число входов стало больше 4, попытаться самостоятельно привести к каскадируемой форме, зная что далеко не все 128 вариантов в действительности имеют значение. Но это уже после нового года, сейчас на это отвлекаться совсем не хочется... А ещё вариант - тупо увеличить длину команды, с 2 до 3 байт, чтобы 16-битное "непосредственное значение" практически напрямую умещалось. Ладно, надо сначала нарастить программу до "рабочей длины", когда там будут и все алгоритмы - и посмотреть, что там лучше всего.


Фух, прошли.

Итак, запускаем ещё разок:


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

Хотя смысл есть: заинтересовавшись содержанием кадра, то бишь вызвав ACQ WholeRow, мы тем самым спустили с цепи прерывание - ещё и память обнулить не успели, как оно сработало, ибо начали кадр по-нормальному обрабатывать и вдруг передумали...

Ладно, сделаем по-другому: тупо очистим память ещё перед выжиданием 1 секунды, чтобы уж наверняка:



Уже что-то!

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

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

  • Великая Октябрьская резня бензопилой

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

  • Очередная несуразность в единицах измерения

    Когда-то я написал программу PhysUnitCalc - калькулятор, умеющий работать с размерностями. Мне казалось, что я наступил уже на все грабли, которые…

  • Big Data, чтоб их... (3)

    "В предыдущих сериях": мой прибор выдаёт 6 значений: 3 координаты и 3 угла, т.е все 6 степеней свободы твёрдого тела. Причём ошибки измерения этих 6…

  • Покрышки с взрывным характером

    Продолжаю кататься на велосипеде на работу и назад, а также время от времени в РКК Энергию. С 17 мая (когда решил записывать, сколько проехал,…

  • Big Data, чтоб их... (2)

    Вчера получил упоротое уравнение, чтобы найти, с какими весами нужно брать результаты измерений, чтобы получить наименее шумную и при этом…

  • Big Data, чтоб их...

    Решил всё-таки вывести оптимальную обработку N измерений x k, каждое по M компонент (т.е вектор M×1), и на каждое дана своя ковариационная…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 6 comments