nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

Максимум и сумма - "в одном флаконе"

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

Может показаться, что это принципиально разные задачи, но как ни странно, их можно возложить на один модуль.


Нарисуем схему для нахождения суммы по всем пикселям:


Собственно, её мы уже реализовали, ровно в таком виде. Используется сумматор, специальный режим работы ЛЭ, где одна половинка LUT формирует текущий разряд, а вторая - перенос в более старший разряд. Благодаря этому на один бит приходится по одному ЛЭ, весьма экономично. Далее, используется 12 ЛЭ "общего назначения" в качестве регистра, который можно ещё и сбрасывать, и в общем-то он получился совсем "недогруженным": из 4 доступных входов задействовано лишь два: данные и синхронный сброс.

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

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


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

Тот же cout управляет ещё одним регистром, в который заносится текущая координата.

Этот модуль я тоже реализовывал "отдельно", как-то так:
module PixelMaximum (input clk, input sclr, input [PixelWidth-1:0] Pixel, input [XregWidth-1:0] X, output reg [PixelWidth-1:0] PMax = 1'b0, output reg [XregWidth-1:0] XMax = 1'b0);

parameter PixelWidth = 12; 
parameter XregWidth = 8; 

//wire NewMax = (Pixel > PMax);
wire NewMax;
	lpm_add_sub Adder (	.dataa (Pixel),
				.datab (PMax),
				.cin (1'b1),
				.result (),
				.cout (NewMax));
	defparam
		Adder.lpm_direction = "SUB",
		Adder.lpm_hint = "ONE_INPUT_IS_CONSTANT=NO,CIN_USED=NO",
		Adder.lpm_representation = "UNSIGNED",
		Adder.lpm_type = "LPM_ADD_SUB",
		Adder.lpm_width = PixelWidth;

always @(posedge clk) begin
	PMax <= sclr? 1'b0 : NewMax? PMax : Pixel;
	if (NewMax)
		XMax <= X;
end

endmodule



Как видно, простая строчка
wire NewMax = (Pixel > PMax);


закомментирована, и вместо неё поставлен сумматор. Так выходит самую чуточку компактнее (33 ЛЭ вместо 36 ЛЭ) и заметно шустрее: 88,5 МГц максимально допустимая частота вместо 50,76 МГц. Как водится, цепи переноса в сумматоре работают шустрее, чем "логика общего назначения", которая синтезируется для "оператора сравнения".

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

module PixelSumMax (input clk, input sclr, input isSum, input [PixelWidth-1:0] Pixel, input [XregWidth-1:0] X, output [PixSumWidth-1:0] Q);

parameter PixelWidth = 12;	
parameter PixSumWidth = 20;	
parameter XregWidth = 8;

localparam CounterWidth = PixSumWidth - PixelWidth;

	wire [PixelWidth-1:0] Sum;
	reg [PixelWidth-1:0] LowerQ = 1'h0;
	wire AdderCout;
	wire PlusOne = ~AdderCout;
	lpm_add_sub Adder (	.dataa (LowerQ),
				.datab (Pixel),
				.cin (1'b1),
				.result (Sum),
				.cout (AdderCout));
	defparam
		Adder.lpm_direction = "SUB",
		Adder.lpm_hint = "ONE_INPUT_IS_CONSTANT=NO,CIN_USED=NO",
		Adder.lpm_representation = "UNSIGNED",
		Adder.lpm_type = "LPM_ADD_SUB",
		Adder.lpm_width = PixelWidth;	
		
	always @(posedge clk) if (isSum | PlusOne | sclr)
		LowerQ <= sclr? 1'h0 : isSum? Sum : Pixel;
		
	wire [CounterWidth-1:0] CounterOut;
	lpm_counter UpperBits (	.clock (clk),
				.cnt_en (PlusOne&isSum),
				.sclr (sclr),
				.data (X),
				.sload (PlusOne&(~isSum)),
				.Q (CounterOut));
	defparam
		UpperBits.lpm_direction = "DOWN",
		UpperBits.lpm_port_updown = "PORT_UNUSED",
		UpperBits.lpm_type = "LPM_COUNTER",
		UpperBits.lpm_width = CounterWidth;
		
	assign Q = {CounterOut, LowerQ};
endmodule


Мы добавили новый 1-битный вход, isSum. Поначалу я такие входы "выбора режима" называл mode, но тогда их значение ничего не значило без поясняющей таблички. С таким названием, isSum, можно хоть догадаться, что единица означает "суммируем", а нолик, по методу исключения - "ищем максимум" :)

Такой модуль синтезируется в 49 ЛЭ, что на 15 больше, чем исходный "сумматор". Из них 12 ЛЭ - что-то вроде мультиплексора, чтобы коммутировать на регистр либо результат суммирования, либо исходный пиксель. Ну или просто "количество входов" нарастили, поскольку их теперь нужно 5:
- исходный пиксель,
- результат суммирования,
- выбор между одним и другим,
- синхронный сброс
- разрешение работы

Всё верно, при всём желании то же количество ЛЭ мы сохранить не могли (по кр. мере я сходу не вижу, как).

Зато 8-битный счётчик показал себя замечательно: к нему добавился вход параллельной загрузки, чтобы использовать его в качестве регистра, запоминающего координату максимума, и это не потребовало удвоения ЛЭ.

И ещё 3 ЛЭ пошли на "управляющую логику" - очень даже неплохо.

Максимальная частота вышла 67,11 МГц - меньше, чем у каждого модуля по отдельности, из-за этого дополнительного мультиплексора (или цепей каскадирования, не знаю точно, как оно синтезировалось). Но всё равно недурно. Если мне удастся всё это безобразие на 50 МГц запустить - будет уже очень хорошо, это номинальная частота для фотоприёмной матрицы 1205ХВ014.

Посмотрим этот модуль в работе. Сначала в режиме нахождения максимума:


(мы взяли 4-ю строчку из нашего "тестового изображения" 32х32, только сейчас посылаем её задом наперёд)

Старшие две цифры (т.е старшие 8 бит) указывают на X-координату (см. нижнюю строку), на которой был найден максимум, а младшие 3 (т.е 12 бит) - показывают этот максимум. Всё работает. Как и следовало ожидать, результат "защёлкивается" к следующему такту.

Теперь посмотрим режим суммирования:


Тут небольшая особенность: он суммирует со знаком "минус", в дополнительном коде. Дело в том, что на этих ПЛИС (функциональных аналогах Flex10k) возможность выбора "на лету", складывать или вычитать требует ещё линейки ЛЭ, по одному на каждый бит. А мне жалко - какая разница, в какую сторону считать, просто в QuatCore вместо ADD поставлю SUB - вот и всех делов.

Если бы я не стал эти модули объединять в один, то ушло бы 34 для нахождения суммы, 33 для нахождения максимума, а потом ещё 20 для мультиплексирования результата, т.е чтобы на шину данных подавать выход то одного модуля, то другого. То есть, вышло бы 87 ЛЭ. А с этим "совмещённым" модулем - всего лишь 49 ЛЭ, почти вдвое меньше. Возможно, это "ловля блох", но мне до сих пор страшно: скорее всего видеообработчика придётся поставить два, на верхний и нижний полукадры (такова специфика матрицы 1205ХВ014), плюс процессор, плюс контроллер МКО (за него ещё толком не брался) - и моргнуть не успею, как ресурсы закончатся. А коммутационные ресурсы могут закончиться ещё раньше, когда "по логике" должно влезть, но соединить всё между собой не получается...


Дальше на очереди: "вторая" сумма (яркостный центр: способ получше) и "обвязка" для QuatCore...
Tags: ПЛИС, математика, программки, работа, странные девайсы
Subscribe

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

    Вчера опробовал "сценарий", когда варьируем дальность от 1 метра до 11 метров. Получилось, что грамотное усреднение - это взять с огромными весами…

  • Потёмкинская деревня - 2

    В ноябре 2020 года нужно было сделать скриншот несуществующей программы рабочего места под несуществующий прибор, чтобы добавить его в документацию.…

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

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

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 7 comments