nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

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

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

При повторном синтезе с этими параметрами, предельная частота получилась и вовсе 23,87 МГц, и такая вот простыня failed paths:


И в этой простыне всплывает очень странная вещь: счётчик пикселей из QuatCoreOddGPUInput комбинаторно соединён и с мультиплексором источников данных QuatCoreSrcMux, и с ПЗУ кода QuatCoreCodeROM, хотя, казалось бы, не должен.

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


В модуле QuatCoreOddGPUInput у нас есть такие строки кода:

wire start_neg = SyncOut[1]&SyncOut[0];	//если попало отрицательное число, выдаём незамедлительно!
wire start_oflo = (~SyncOut[1])&(~SyncOut[0])&(BufOut[XregWidth] & XCout);	//число положительное, но ООЧЕНЬ большое, больше нашей разрядности!
wire start_normal = (~SyncOut[1])&(~SyncOut[0])&(BufOut[XregWidth-1:0] <= X);	//нормальное число, и совпало с номером пикселя

assign rdreq = (~Disable)&(start_neg | start_oflo | start_normal | (Yfinished & UseRowCount & (~SyncOut[1])));	//хоть что-нибудь из этого выполнится - и мы стартуем


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

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

"Утечка" же произошла во ВХОДНОМ БУФЕРЕ. Сигнал rd_req, показанный выше, управляет также "выталкиванием" очередного элемента из этого буфера. Но наша жадность нас сгубила, КАК ВСЕГДА. Вот строка в буфере, определяющая, что нужно приостановить работу процессора, поскольку новый элемент уже не лезет:

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


То есть, должно выполниться 3 условия: мы собственно помещаем новый элемент (wr_req = 1), буфер полон (nfull = 0) И В ЭТОТ САМЫЙ МОМЕНТ НЕ ИДЁТ ВЫТАЛКИВАНИЯ (rd_req = 1) ИЗ БУФЕРА!

В своё время мы радовались, что буфер абсолютно корректно позволит произвести одновременное чтение и запись, будучи уже полным. Но увы, тем самым мы делаем сигнал SrcStallReq комбинаторно зависимым от весьма сложных выражений, включающих наш счётчик пикселей, а потом этот сигнал ещё должен объединиться со SrcStallReq с других модулей, и в итоге именно эти цепи ограничивают нашу предельную частоту!

Придётся свою жадность немножко умерить, и ввести в буфер новый параметр AllowRWwhenFull ("позволить одновременное чтение и запись, когда буфер заполнен):

//укуренная версия FIFO, где один выходной бит отдельным сигналом можно сбросить!
//используется в нашем GPU, т.к единичка означает "ждём синхроимпульс", а приход этого синхроимпульса должен единичку сбросить
//самый старший бит: Vsync,
//второй по старшинству: Hsync

module FIFO_on_LE_for_GPU (input clk, input [DataWidth-1:0] D, input rdreq, input wrreq, input Vsync, input Hsync,
			output [DataWidth-1:0] Q, output wr_stall, output rd_stall, output empty,
			input Dclr, output reg [ElemCount-1:0] MaxOcc);

parameter DataWidth = 10;
parameter ElemCount = 4;
parameter AllowRWwhenFull = 1'b0;
//если 0, то при одновременное чтение и запись при полном буфере будет запрещена, зато не будет комбинаторной связи rdreq и wr_stall

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];
wire 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
	//первый элемент, то бишь самый правый, тоже отдельно, чтобы сделать "сброс бита"
	if (ena[0] | Vsync)
		DR[0][DataWidth-1] <= Vsync? 1'b0 : CR[1]? D[DataWidth-1] : DR[1][DataWidth-1];
	if (ena[0] | Hsync)	
		DR[0][DataWidth-2] <= Hsync? 1'b0 : CR[1]? D[DataWidth-2] : DR[1][DataWidth-2];
	if (ena[0])
		DR[0][DataWidth-3:0] <= CR[1]? D[DataWidth-3:0] : DR[1][DataWidth-3:0];

	for(i=1; 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] 	<= wrreq? 1'b0 : CR[1];		
		for (i=1; i<ElemCount-1; i = i + 1)
			CR[i]	<= wrreq? CR[i-1] : CR[i+1];		
		CR[ElemCount-1] <= wrreq? CR[ElemCount-2] : 1'b1;
	end;			
end

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

//отладочные выводы
always @(posedge clk)
	MaxOcc <= Dclr? {ElemCount{1'b1}} : MaxOcc & CR;

endmodule




Теперь предельная частота составляет 27,4 МГц, уже неплохо.

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

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments