nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

Доработка FIFO под QuatCore

В прошлой записи мы добавили в наш буфер First-In-First-Out (FIFO) выход stall, чтобы можно было "напрямую" поставить этот буфер между QuatCore и входом какого-то устройства (например, видеопроцессор), и если мы начнём подавать данные слишком быстро, так что устройство не будет успевать их обработать, рано или поздно этот выход stall заставит нас остановиться и подождать.

Но надо подумать ещё и об использовании этого буфера "на вход" QuatCore, где мы должны остановиться, если все данные закончились, и нам больше нечего обрабатывать. А ещё мы обмолвились когда-то, что обработка строчного синхроимпульса должна "обнулить" все ранее поступившие данные, чтобы каким-то неведомым образом попавший в буфер ЛИШНИЙ элемент (ТЗЧ, не иначе! Или бросок напряжения из-за офигительной работы нашего буфера) всё-таки оттуда убрался подобру-поздорову! Ведь если дальше всё будет работать корректно, QuatCore будет забирать ровно столько элементов, сколько за это время генерит видеопроцессор, и всё время будет сохраняться сдвижка на один. Пугают меня такие штуки, пусть хоть по прошествии строки нумерация заведомо восстанавливается.

Вообще халява:
module FIFO_on_LE (input clk, input [DataWidth-1:0] D, input rdreq, input wrreq, input sclr, output empty, output nfull, output [DataWidth-1:0] Q, output wr_stall, output rd_stall);

parameter DataWidth = 10;
parameter ElemCount = 4;

reg [DataWidth-1:0] DR [ElemCount-1:0]; //Data Regs - регистры для данных
reg [ElemCount-1:0] CR = {ElemCount{1'b1}};//Count Reg - регистр для "унарного счёта"
//пусть самый правый будет с индексом 0, а самый левый с индексом ElemCount-1

assign empty = CR[0];
assign nfull = CR[ElemCount-1];
assign Q = DR[0];

wire [ElemCount-1:0] ena;				//для регистров
wire cnt_ena = (rdreq & (~empty)) ^ (wrreq & nfull);	//для унарного счётчика

assign ena = {ElemCount{rdreq}} | ({ElemCount{wrreq}} & CR);	//"почленно" находим

integer i;
always @(posedge clk) begin
	for(i=0; i<ElemCount-1; i = i + 1)		//все, кроме последнего элемента
		if (ena[i])
			DR[i] <= CR[i+1]? D : DR[i+1];			
	if (ena[ElemCount-1])
		DR[ElemCount-1] <= D;
	if (cnt_ena) begin
		CR[0] 		<= sclr? 1'b1 : wrreq? 1'b0 : CR[1];		
		for (i=1; i<ElemCount-1; i = i + 1)
			CR[i]	<= sclr? 1'b1 : wrreq? CR[i-1] : CR[i+1];		
		CR[ElemCount-1] <= sclr? 1'b1 : wrreq? CR[ElemCount-2] : 1'b1;
	end;			
end

assign wr_stall = (~nfull) & wrreq & (~rdreq);
assign rd_stall = empty & rdreq;

endmodule



Старый stall (ну как старый, пара часов ему от роду) заменяем на wr_stall, т.е остановка при записи в буфер. И сейчас его дополняет rd_stall, который попросит QuatCore остановиться при чтении из буфера, пока там не появятся данные.

Между ними существует некоторая асимметрия, это нормально. wr_stall устанавливается в ноль, стоит лишь возникнуть запросу на чтение, а вот rd_stall не сбрасывается при поступлении запроса на запись wrreq. Это правильно: ведь значение на выходе всё равно появится только на следующем такте, и нам необходимо его дождаться, потому как сейчас на выходе лежит мусор.

Давайте посмотрим на симуляции, как это сработает - ожидание прихода хоть каких-то данных:



Смотрим на участок, обведённый синим. Пришёл запрос rdreq, но буфер пуст - выдаётся rd_stall=1. Затем приходит и запрос на запись числа 0x53, но по-прежнему на выходе "мусор" и rd_stall=1. Наконец, к следующему такту rd_stall сбрасывается в ноль, на выходе появляется это самое число 0x53. В то же самое время мы записываем число 0x54 - и к следующему такту именно оно оказывается на выходе.

Всё верно. Мне нравится, когда все эти "граничные случаи" сами собой верно обрабатываются, без необходимости какого-то специального кода :)

Ну а что касается sclr (синхронный сброс), то он лишь обнуляет наш "унарный счётчик", а сами значения никуда не исчезают, и это нормальная ситуация. Главное, что вся управляющая логика теперь делает вид, что там "ничего нет".

К сожалению, добавление sclr несколько "утяжелил" модуль: теперь он в конфигурации 4х10 (4 элемента по 10 бит) занимает целых 53 ЛЭ, тогда как без них занимал 50. Один лишний ЛЭ на rd_stall - это куда ни шло, но вот sclr, похоже, добавляет по 1 ЛЭ на каждый "промежуточный" регистр унарного счётчика, то бишь N-2 лишних ЛЭ, где N-число элементов. То есть, в конечном счёте на FIFO размером NxW (N элементов по W бит) будет уходить Nx(W+3)+1 бит. Чем шире будет буфер - тем меньше накладных расходов в относительном выражении :)

Неприятно, но по-прежнему это поменьше, чем 77 ЛЭ, которые занимает штатный альтеровско-интеловский FIFO на ЛЭ со сходной функциональностью (но без выходов stall).


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

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments