nabbla (nabbla1) wrote,
nabbla
nabbla1

Category:

FIFO уходит в себя - окончание

Начало

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




Логическая схема в левом углу должна "откликаться" на команду ACQ или TRK. У первой адрес DestAddr = 0x20, у второй: 0x30, и по сути весь диапазон 0x20..0x3F отдан им.

По любой из этой команд, содержимое шины данных должно заноситься в буфер. Если же он полон, то процессор должен остановиться (за счёт SrcStallReq=1) и стоять пока буфер не освободится.

Для моделирования работы видеопроцессора хватило обычного делителя частоты. Ну, у меня он не совсем обычный, поставил свой FastFreqDivider хотя можно было так не заморачиваться. Каждые 100 тактов он даёт запрос rdreq=1, т.е на прочитывание "задания" из буфера.

Логическая схема наверху "откликается" на команды GPUL / GPUH / GPUPL / GPUPH, в общем на запрос результатов работы видеопроцессора. Мы декодируем, что это команда на видеопроцессор (кстати, не совсем корректно, надо было ещё SrcDiscard туда включить), и если это так - просто "вешаем" процессор наглухо!

И дальше нам просто надо посмотреть, какие команды будут извлекаться из буфера, есть ли среди них ACQ WholeRow, та единственная из занесённых, на которую видеопроцессор обязан выдать результат.

Вот код FIFO_on_LE_for_GPU, с которого мы начали отладку:
//укуренная версия 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 & (AllowRWwhenFull | ~nfull))? CR[ElemCount-2] : 1'b1;
		//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


Запускаем симуляцию. Вообще, я несколько "рисковал": та программа, что сейчас "зашита", опрашивает UART (дожидается оттуда одного байта), а затем проверяет, что же это за байт такой, различая 3 значения. "+" означает "добавить яркость ИК подсветки", "-" означает "убавить яркость ИК подсветки" (см. программа для регулирования яркости на QuatCore), и, наконец, "I" (Image) означает "получить изображение".

Вот только UART-то я здесь не подключал, и на него ничего не подаётся! Можно было программу всё-таки поменять, выкинуть оттуда всё лишнее. Можно было всё-таки поставить в параметрах процессора enableIO = 1, а на соответствующий вход подключить константу "I". Но по счастью, ничего из этого не понадобилось: за неимением IO на шину пришёл-таки правильный "мусор" со второй попытки, и мы вышли куда надо.

И сразу увидели что-то неладное:


В буфер должно вмещаться 10 элементов (мы выставили параметр ElemCount=10). Как видно в начале "осциллограммы", буфер был пуст. Мы начали туда запихивать значения одно за другим (wrreq=1), ну да, один раз значение было извлечено (rdreq=1), но тем не менее после 11 посылок wrreq=1 процессор должен был остановиться и ждать освобождения буфера. Но он и не думал останавливаться, моментально "отстреляв" все 13 заданий VSync, затем 29 заданий HSync, а затем ещё WholeRow и ещё один HSync, и потом "завис" на получении результатов, как мы и хотели.

Похоже, мы окончательно "сняли тормоза"!

Хорошо, вернём на место нашу правку, вместо строки

CR[ElemCount-1] <= (wrreq & (AllowRWwhenFull | ~nfull))? CR[ElemCount-2] : 1'b1;


(ближе к концу кода FIFO_on_LE_for_GPU) поставим старую:

CR[ElemCount-1] <= wrreq? CR[ElemCount-2] : 1'b1;


Ещё раз запускаем. В этот раз всё правильно:


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

Вот только дальше нас ждёт облом:


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

Ну да, это было ожидаемо, мы уже сообразили, что оно так происходит, но теперь увидели "воочию".

Остаётся понять, в чём проблема исправленной строки.

Всё просто до обидного: лишнее отрицание "~", которого там быть не должно! Вот корректная строка:

CR[ElemCount-1] <= (wrreq & (AllowRWwhenFull | nfull))? CR[ElemCount-2] : 1'b1;


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

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

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


Пришёл запрос на извлечение из буфера (rdreq=1), и на следующем такте убрался сигнал о переполнении (wr_stall=1), за счёт чего процессор завершил одну итерацию цикла, начал следующую, попытался передать очередное задание - и снова остановился на 100 тактов, дожидаясь освобождения буфера.

И наконец, посмотрим, все ли значения, которые заносились в буфер, под конец из него вышли:


Ну да, 0x4106 - это код для "ACQ HSync" (флаг 0x4000 значит ждать строчного синхроимпульса, 0x0106 - количество тактов между сихнроимпульсом и началом видимой строки), а 0x03FF - это "ACQ WholeRow", где 0x3FF = 1023 - как раз координата, до которой обрабатывать строку. Затем снова идёт HSync, а потом буфер и вовсе пустеет, а на выходе появляется "мусор", только что взятый с шины данных.


И наконец, испытаем "на железе", попытаемся получить изображение:


Кстати, качество получше стало: практически правильный уровень чёрного, нет такого "перенасыщения", как раньше. Это я две детали из схемы выкинул нафиг (одну выпаял, другую закоротил) :) Как так вышло, напишу отдельным постом.
Tags: ПЛИС, программки, работа, странные девайсы
Subscribe

Recent Posts from This Journal

  • Тестируем atan1 на QuatCore

    Пора уже перебираться на "железо" потихоньку. Решил начать с самого первого алгоритма, поскольку он уже был написан на ассемблере. В программу внёс…

  • Формулы приведения, что б их... (и atan на ТРЁХ умножениях)

    Формулу арктангенса на 4 умножениях ещё немножко оптимизировал с помощью алгоритма Ремеза: Ошибка уменьшилась с 4,9 до 4,65 угловой секунды, и…

  • Алгоритм Ремеза в экселе

    Вот и до него руки дошли, причина станет ясна в следующем посте. Изучать чужие библиотеки было лениво (в том же BOOSTе сам чёрт ногу сломит), писать…

  • atan на ЧЕТЫРЁХ умножениях

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

  • Ай да Пафнутий Львович!

    Решил ещё немного поковыряться со своим арктангенсом. Хотел применить алгоритм Ремеза, но начал с узлов Чебышёва. И для начала со своего "линейного…

  • atan(y/x) на двух умножениях!

    Чего-то никак меня не отпустит эта тема, всё кажется, что есть очень простой и эффективный метод, надо только его найти! Сейчас вот такое…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments