nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

QuatCore+GPU, строка 8

Увеличил размер выходного буфера GPU с 4 до 5 элементов и синтезировал схему по-новой. Её размер вырос с 1004 до 1055 ЛЭ. Увы, выходной буфер штука здоровенная: 12 бит для пикселя, ещё 10 бит для X-координаты, и ещё 26 бит на "вторую сумму", которую мы пока не использовали совсем, она для субпиксельной точности. Итого 48 ЛЭ только на память, ещё 1 ЛЭ - для расширения "унарного счётчика", и ещё 2 ЛЭ - логика (тут у нас есть вход flush, сброс, или скорее "слив", стало на 1 ЛЭ больше из-за него).

Похоже, ничего не изменилось, кроме того, что исчез сигнал OFLO, и последний отрезок на строке тоже нашёл себе место.

Мы пока здесь:


Видеопроцессор убежал немного вперёд и уже обработал все 5 отрезков, сейчас ждёт начала новой строки (синхроимпульс+238 тактов "back porch"), а QuatCore только-только закончил обработку прошлой строки и готовится прочитать результаты по текущей...




Там, где был "комбинаторный выброс", он исчез, а там где был уже "настоящий" сигнал OFLO, он сменился "комбинаторным выбросом". Логично: теперь именно здесь мы забили буфер целиком, но это нормальная ситуация.

Находим SrcAddr=0x88 и 0x8A, это команды GPUL и GPUH соответственно. На шине данных, со сдвижкой на один такт видим яркость 0xFFF и координату 0x00 - в левой части изображения мы опять ничего не нашли!

QuatCore за 7 тактов (280 нс) приходит к такому же выводу и переходит на @@MidCycle. Там текущая точка (фиктивная левая) 0x10 сохраняется в регистр Y, а регистр X прыгает на следующую точку, 0x15, а из обнулённого аккумулятора вычитается адрес следующей за ней точки, 0x1A. Раз это не Null, значит следующий отрезок будет для "уточнения координат текущей точки".

Загружаем в аккумулятор яркость текущей точки (из списка), 0x39A. Что-то знакомое :)

Поехали дальше:


Получаем яркость очередного отрезка из GPU: 0xCF4, это уже очень тускло по сравнению с 0x39A (у нас чем больше значение - тем тусклее). Сразу заносим куда надо, после чего получаем координату отрезка, 0x0E, логично, строго под ранее обнаруженным максимумом. И прыгаем по метке @@NotSoBright. Опять можно было прыжок не заметить, но раз следующие строки (что CALL, что JMP) не привели к прыжкам, значит они были проигнорированы, и мы действительно перешли на ранее неизведанный кусок кода:

2F  80FE          @@NotSoBright:  Acc     [SP+2j] ;текущий номер строки
30  83C2                          SUB     [X+2j+1]    ;вычитаем Y-координату  ранее обнар. точки. Ответ всегда положительный, т.к считаем по строкам "вверх"
31  8FC0                          DIV2S   [X+1]       ;ну и размер точки, в кои-то веки уполовиненный
32  B84F                          JL      @@QueueAnyway   ;здесь ОБЯЗАН быть именно JL, т.к флаг знака является одним из "аргументов" для AcqQueue
33  CD45                          X       AllPoints
34  F3B6                          CALL    ListOp
35  CDDD                          X       Y       ;теперь [X] ссылается уже не на обработанную и удалённую точку, а на следующую, так что всё верно
36  B019                          JMP     @@ActPointsStart    ;ура...                 

Собственно, это 8 из 9 строк, которые мы пока не наблюдали "в работе".

Загружаем в аккумулятор текущий номер строки, 8. Вычитаем Y-координату точки из списка, 7, получается 1. Теперь вычитаем диаметр (6), делёный на два, и получаем -2.

Поэтому на строке 0x32 мы осуществляем прыжок на @@QueueAnyway. Мы не хотим удалять эту точку из списка, поскольку мы ещё слишком близко к её центру, а раз она ещё в списке, мы должны сформировать такое же задание на следующую строку, как и прежде. А вот обновлять параметры точки в списке НЕ НАДО.

Опять начинается чехарда:
2D  F3B8  @@QueueAnyway:  CALL        AcqQueue                        
2E  B019                  JMP     @@ActPointsStart    ;завершили цикл                 

Только-только прыгнули на QueueAnyway, как вызываем процедуру, а только мы из неё возвращаемся - как прыгаем дальше.

Только сейчас сообразил: эти строки можно заменить на такие:
[SP++] ActPointsStart
NOP    Call(AcqQueue)

То есть, мы "ручками" задаём, куда мы должны "выпрыгнуть" из процедуры, а когда наконец вызываем её, "настоящий адрес возврата" выкидываем. Пару тактов сэкономим.


Поехали дальше:


Снова приведём листинг AcqQueue, благо он короткий:
    AcqQueue proc   
49  A023              i       2   ;даже если не нужно обновить точку, не навредит... (это мы Hazard убираем мучительно!)
4A  B841              JL      @@queue
4B  C6F4      @@updCycle: [X+2j+i]    [SP+i]
4C  A85F              iLOOP       @@updCycle
4D  80CA      @@queue:    Acc     [X+2j+k]
4E  8FC0              DIV2S       [X+1]   ;теперь в аккмуляторе у нас X - Ceil(D/2)
4F  2080              ACQ     Acc     ;первый отрезок
50  82C0              ADD     [X+1]   ;а вот теперь X + Floor(D/2)
51  2080              ACQ     Acc     ;второй отрезок
52  B0FF              JMP     [--SP]
    AcqQueue endp   


В этот раз срабатывает условный переход на @@queue, то есть, мы пропускаем цикл по обновлению параметров точки в списке (её координаты, яркость и размер). Всё верно: мы должны их обновлять, когда нашли более яркий пиксель, а раз он у нас тусклее, то обновлять не надо!

Так что нам остаётся, зная координату точки X и её диаметр D, найти её границы на строке: левую границу X-D/2 и правую X+D/2, и до каждой выдать задание. Опять убеждаемся, что все числа берутся правильные: координата 0x0E, диаметр 6, и получаем команды ACQ 0x0B и ACQ 0x11. Во входном буфере оказалось 3 задания: текущее по отработке синхроимпульса, и два новых. Успели с огромным запасом!

Возвращаемся из процедуры, после чего прыгаем в начало цикла по точкам в списке, 0x0C:

0C  F288      @@ActPointsStart:   [SP+2j+1]   GPUL            ;яркость самой яркой точки на отрезке
0D  F08A                          [SP+1]      GPUH            ;соотв. координата
0E  807E                          Acc         Threshold       ;поставили задом наперёд, чтобы избежать Hazard'а   
0F  83F2                          SUB         [SP+2j+1]       ;вычитаем порог, положит. значение свидетельствует о новом найденном "пятне"                        
10  B845                          JL          @@MidCycle      ;пятна нет, не торопимся заказывать отрезок


Сейчас обрабатывается отрезок длиной в 1 пиксель. Его яркость оказывается 0xFFF (это означает ноль), и координата ноль. Добавлять такую точку не надо, поэтому поскорее прыгаем в @@MidCycle.
23  DDCD          @@MidCycle:     Y       X
24  CDC8                          X       [X+k]                               
25  8000                          Acc     0
26  83C8                          SUB     [X+k]
27  BC39                          JGE     @@FinalRange    ;наткнулись на NULL, значит, все точки обработали...                        
28  80CB                          Acc     [X+4j+k]
29  F288                          [SP+2j+1]   GPUL        ;яркость
2A  8388                          SUB     GPUL
2B  F08A                          [SP+1]  GPUH        ;пока не забыли - сразу второе слово запрашиваем (коорд точки), чтобы не сбить весь FIFO 
2C  B841                          JL      @@NotSoBright   ;похоже, на спад пошло. ТУТ ОБЯЗАН СТОЯТЬ ТОЛЬКО JL, если нет - меняем местами операнды!


Текущую точку, 0x15, сохраняем в Y на всякий случай, а X прыгает на следующую, 0x1A. Проверяем ссылку с неё ещё дальше, там лежит 0x12, проверяем, что это не NULL (значит, это ещё не "правая фиктивная точка") - и идём дальше, не прыгаем пока на @@FinalRange.

В аккумулятор заносим яркость текущей точки из списка, 0x6BD...


Запрашиваем яркость у GPU, получаем 0xA25 - гораздо тусклее. Заносим в локальную переменную (вдруг пригодится), вычитаем из аккумулятора, запрашиваем координату этой "яркой" точки: 0x15 - неудивительно. Результат вычитания отрицательный, поэтому прыгаем на метку @@NotSoBright, исполняем этот небольшой код:
2F  80FE          @@NotSoBright:  Acc     [SP+2j] ;текущий номер строки
30  83C2                          SUB     [X+2j+1]    ;вычитаем Y-координату  ранее обнар. точки. Ответ всегда положительный, т.к считаем по строкам "вверх"
31  8FC0                          DIV2S   [X+1]       ;ну и размер точки, в кои-то веки уполовиненный
32  B84F                          JL      @@QueueAnyway   ;здесь ОБЯЗАН быть именно JL, т.к флаг знака является одним из "аргументов" для AcqQueue


Ровно такое, цифры 8-7-6 в DataBus, мы уже видели! Да, начинаем с текущей строки, вычитаем Y-координату точки из списка, затем вычитаем диаметр 6, делённый пополам, и если результат отрицательный (мы ещё не так далеко от центра пятна), прыгаем в @@QueueAnyway, а оттуда мы вызываем процедуру AcqQueue:


Тут мы пропускаем обновление параметров точки в списке (координаты, яркость, размер) и только вычисляем X-D/2 и X+D/2, и два новых отрезка отправляем в GPU.

Поскольку во входном буфере GPU уже лежит 3 отрезка, то ещё один туда вошёл (ACQ 0x12), а на ещё одном (ACQ 0x18) мы ЗАСТРЯЛИ.

Застряли аж на 141 такт! Это вдвое больше, чем в прошлый раз, видать, добавление новой точки и её инициализация заняли прилично времени в прошлый раз.


Мы вернулись из процедуры, вышли в начало цикла по точкам списка, запросили яркость с очередного отрезка - и ВНЕЗАПНО ЗАСТРЯЛИ ПОВТОРНО.

Это по завершении команды ACQ HSync, видеопроцессор сбросил выходной буфер! Ещё до того, как мы успели извлечь оттуда последний отрезок! Нда, придумал я себе проблем....

Самый простой выход из ситуации - ещё и входной буфер увеличить на единичку, благо он покомпактнее: 11 бит на координату, 1 бит на режим работы (ACQ/TRK) и 2 бита на синхроимпульсы/отрицательное "переполнение", итого 14 ЛЭ для данных и дополнительные 3 ЛЭ управляющих, т.е +17 ЛЭ. Весь "проект" растолстел до 1071 ЛЭ.

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


Ни дня без ошибок, но всё-таки потихоньку продвигаемся. Осталось всего 5 строк из 83, где мы "не были", т.е "покрытие 94%". Хотя это не касается "покрытия" некоторой логики, заложенной в модули на verilog, как-то: работу с "переполнениями" по X-координате (как в плюс, так и в минус), работу со строками, когда все "полезные" строки уже закончились, возможно, и ещё о чём-то забыл.

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

UPD. Всё-таки промотал "осциллограмму" чуть-чуть назад - и тут же нашёл корректное завершение этой длинной строки. Мы отправили ACQ 0x12 и 0x18, затем прочитали яркость последнего отрезка, яркость оказалась нулевой. Мы поняли, что отрезок последний, переместились в @@FinalRange, где начали отправлять ACQ WholeRow, но застряли, поскольку в буфере уже 5 заданий. Так что мы корректно обработали строку по восьмую включительно. Осталось проверить, как эти два "верхних" пятна будут перемещаться из списка активных точек ActivePoints в список обработанных точек AllPoints, ну и слияние тоже неплохо бы, но не на этой картинке.
Tags: ПЛИС, программки, работа, странные девайсы
Subscribe

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 3 comments