nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

QuatCore+GPU: продолжается самое интересное

Пока что мы добрались до строки, где яркость пикселей превысила порог обнаружения. Видеопроцессор искал самую яркую точку НА ВСЕЙ СТРОКЕ, и нашёл её (отмечена кружком):


В итоге, для следующей строки было заготовлено 3 задания видеопроцессору, они также показаны на рисунке. ACQ значит "Acquire" - "захват", "обнаружение". Число - попросту X-координата, до которой нужно вести обработку. На двух крайних отрезках мы ищем новые точки, а на центральном - уточняем координаты уже обнаруженной. Мы справедливо ожидаем, что пока прошлись по краю пятна, и на следующей строке увидим более яркие пиксели.

Давайте посмотрим, как это будет происходить...


Продолжаем практически с того места, на котором остановились в прошлый раз, в конце цикла по обработку строки. Мы послали команду ACQ WholeRow (обработать оставшиеся пиксели до конца строки), а на ACQ HSync основательно застряли - входной буфер GPU уже был полон. И вот мы отсчитали сколько надо тактов от синхроимпульса, вовсю началась строка номер 6, и QuatCore также сдвинулся с мёртвой точки.


Сейчас как раз проверяем самый "свежий" код: загружаем в аккумулятор текущий номер строки, 6, затем вычитаем 0x28, затем прибавляем 0x29, и прыгаем по страшному адресу 0x404C, из которого на самом деле берутся младшие 7 бит, в результате чего попадаем на 0x0A. Приводим листинг, чтобы ориентироваться в ситуации:

0A  FE80          @@newRow:       [SP+2j]   Acc
0B  CD05                          X         ActivePoints    ;начинаем цикл по всем активным точкам
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      ;пятна нет, не торопимся заказывать отрезок
11  EDCD                          Z         X       ;Чтобы в Merge всегда использовать Z, независимо от того, левая или правая точка оказались
12  A041                          i         1


Да, перешли куда надо, и занесли значение 0x07 назад в [SP+2], это наша координата Y. Затем X = 0x10 (старший байт игнорируется).
И получаем результаты по первому отрезку, который был обработан буквально пару тактов назад! Максимальная яркость оказывается 0xFFF, что у нас означает нули (инвертировано). Координата, на которой достигнута максимальная яркость: 0x00, что не шибко удивительно.

Помещаем в аккумулятор порог обнаружения, вычитаем только что полученную максимальную яркость - и прыгаем в @@MidCycle, пятна мы в левом отрезке не обнаружили... Но пока мы соображали с этой точкой, видеопроцессор времени зря не терял - он уже обработал второй отрезок (до 0x11) и взялся за последний на этой строке (до 0x1F). Да, буфер здесь явно не зря стоит!

Смотрим, что происходит в "середине цикла", @@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  F288                          [SP+2j+1]   GPUL        ;яркость
29  8088                          Acc     GPUL
2A  83C2                          SUB     [X+2j+1]    ;сравниваем по яркости                      
2B  F08A                          [SP+1]  GPUH        ;пока не забыли - сразу второе слово запрашиваем (коорд точки), чтобы не сбить весь FIFO 
2C  BC41                          JGE     @@NotSoBright   ;похоже, на спад пошло
2D  F3B8          @@QueueAnyway:  CALL        AcqQueue                        
2E  B019                          JMP     @@ActPointsStart    ;завершили цикл                 
2F  80C2          @@NotSoBright:  Acc     [X+2j+1]    ;Y-координата ранее обнар. точки. Сейчас 
30  83FE                          SUB     [SP+2j] ;текущий номер строки, чем меньше - тем НИЖЕ, идём от 719 (или от 1024) до нуля. При этом вычитании всегда получаем положительное значение
31  8FC0                          DIV2S       [X+1]       ;ну и размер точки, в кои-то веки уполовиненный


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

Тем временем, видеопроцессор уже завершил обработку строки и начал ждать синхроимпульс, так что в буфере лежит уже ДВА результата!

Поэтому команда GPUL исполняется "мгновенно", в смысле, за один такт. Мы получаем значение 0x39A. Инверсия даст 0xC65 = 3173, при том, что максимальная яркость: 4095 (АЦП у нас 12-битный. Будет когда-нибудь...). Заносим его в [SP+3] и сразу же в аккумулятор. Опять же убеждаемся, что GPUL можно вызывать несколько раз подряд, он будет давать один и тот же результат, не "извлекая" результаты из FIFO.

Далее, мы должны вычесть яркость точки с предыдущей строки. Увы, вместо этого мы вычитаем Y-координату. Дурная ошибка, я очень долго придумывал, в каком порядке хранить параметры точки: радиус, X,Y, яркость, и видать не всё переправил.

Надо на строке 0x2A заменить [X+2j+1] на [X+4j+k]...

Из-за этого огреха выходит, будто бы на прошлой строке был ОЧЕНЬ ЯРКИЙ ПИКСЕЛЬ. Логика работы нарушится, но давайте посмотрим, куда это нас приведёт...

Далее мы запрашиваем координату яркой точки и получаем снова 0x0E, всё верно.

И тут нас ждёт "замаскированный" прыжок. Смотрим на PC - он идёт себе ровненько, прибавляет единицу, но в действительности переход был осуществлён. Может, стоило вывести ещё наши любимые SrcDiscard / SrcStall / DestDiscard. Но здесь и так понятно: следующей командой был вызов процедуры AcqQueue, но никакого перехода очевидно не было! А затем идёт ещё прыжок, но и он "игнорируется", так что мы уверенно попадаем на метку @@NotSoBright.

Тут мы разбираемся: не пора ли эту точку, раз её яркость снижается, выкинуть из списка ActivePoints, отправив "на покой" в список AllPoints? Для этого берём Y-координату, которая была записана, 0x06...

Следующий слайд!


И вычитаем текущую Y-координату, 0x07. Затем ещё и половину диаметра этой точки, а пока мы его приняли за 6. Как результат, получается значение "-4", и здесь мы видим ещё одну ошибку! Мы явно ожидали, что счёт будет идти сверху вниз, а сейчас, когда переправили его на "снизу вверх", логика нарушена. Ведь здесь всегда будет получаться отрицательный результат. Половину диаметра надо было бы прибавить, и только потом вычесть текущую координату, тогда действительно сможем оценить, насколько далеко мы от центра пятна. Получили отрицательное значение - значит ушли далеко, это пятно пора переводить в AllPoints. Получили положительное - пусть ещё посидит!

А сейчас, в результате ДВУХ ОШИБОК ПОДРЯД, мы пришли к варианту: координаты точки не обновляем, но и перетаскивать её в AllPoints не торопимся. То есть, прыгаем на метку @@QueueAnyway. Там сразу же вызываем процедуру AcqQueue, а в ней "скрытно" перепрыгиваем через цикл, обновляющий координаты точки.

Далее, берём X-координату точки, 0x0E (найденную ещё на предыдущей строке, так как найденное на этой мы считаем не заслуживающим внимания), вычитаем половину её диаметра (6 пикселей), что даёт 0x0B, как и в прошлый раз.

Пока я всё это изучал, "на фоне" всё это хозяйство по-новой синтезировалось и симулировалось, с исправленными ошибками. А потом обнаружилось, что у меня везде, где только можно, нарушена логика вызова AcqQueue. Ведь я там сделал финт ушами: Флаг знака выступает одним из "аргументов" при вызове функции, в смысле что там внутри стоит JL, который в зависимости от предыстории либо сработает, либо нет. Но когда началась возня с инвертированием яркости (так уж устроен наш видеопроцессор) и с инвертированием Y-координаты, всё нахрен перепуталось, и всё пошло вкривь и вкось. Сейчас, надеюсь, всё правильно...

Так что вернёмся чуть-чуть назад, на строчку 0x28, где мы получили от видеопроцессора новую яркость и некорректно её сравнили. Вот новый листинг этого куска, команды пришлось несколько поменять местами:
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, если нет - меняем местами операнды!
2D  F3B8          @@QueueAnyway:  CALL      AcqQueue                        
2E  B019                          JMP       @@ActPointsStart    ;завершили цикл                                 


Смотрим симуляцию:



Да, теперь мы первым делом загружаем в аккумулятор "старую" яркость этой точки, 0x41F, а только потом запрашиваем текущую, 0x39A, запихиваем её в [SP+4], а потом вычитаем её из аккумулятора. Результат получается положительным, что указывает, что мы ПРИБЛИЗИЛИСЬ к центру пятна. Запрашиваем координату точки, получается 0x0E. Условный переход на @@NotSoBright не срабатывает, и снова это не так-то просто заметить. Офигительное место, где DestAddr=SrcAddr=B8. И то, и другое - команда перехода! Только DestAddr=B8 - это JL, а SrcAddr=B8 - это CALL(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 


Инициализируем i, затем идёт условный переход на @@queue, но "не срабатывает". На это указывает долгое исполнение, потому как на пару с JL выполняется [SP+i], это выборка из памяти и занимает 2 такта. Так что мы обновляем координаты яркой точки. Первой обновляется яркость (0x39A), затем Y-координата (0x0007). Смотрим следующий "слайд":



Обновляется X-координата: 0x000E. Всё верно.

Далее, эта же координата загружается в аккумулятор, вычитаем половинку диаметра (6), получаем 0x000B. Отправляем это как задание на GPU, это наш самый первый отрезок новой строки, такой же как был. Отправляется "мгновенно", поскольку во входном буфере осталось всего одно задание, с синхроимпульсом, ещё 3 ячейки вакантны. И на этом "гонка со временем" приостанавливается. Мы обязаны были дать первое задание для новой строки ДО ТОГО, как начнётся полезная область этой строки, так оно и случилось, и у нас ещё 209 тактов в запасе! Нам помогло, что мы начали работу в самом начале строки.

Далее, прибавляем диаметр и получаем 0x0011, отправляем это ещё одним заданием на GPU, и снова очень быстро.

Возвращаемся из процедуры на строчку 0x2E, а с неё тут же прыгаем на метку @@ActPointsStart. Это продолжение цикла по нашим активным точкам, адрес 0x0C. Это уже "знакомые места" - здесь мы были, поэтому пробежимся гораздо быстрее:


Запросили видеопроцессор по третьему отрезку на этой строке. Получили яркость 0x06BD, если инвертировать: 0x942 = 2370. Не так ярко, как соседняя точка, но явно выше порога. Получили координату 0x0015 = 21, всё верно.

Теперь надо проверить, что эта точка не слишком близка к соседям. Z=X=0x15 - это адрес единственной пока обнаруженной точки, координаты которой мы только что обновляли. Загружаем в аккумулятор текущую координату X, 0x15, вычитаем координату X точки слева от нас, 0x0E, получаем 0x07. От него берём модуль (получается то же самое), и вычитаем половинку расчётного диаметра, 6.



Затем половинку диаметра точки слева от нас, это тоже 6. Результат: 1. Раз он неотрицательный, слияние делать не надо, на @@DoMerge мы не прыгаем. Вместо этого в Z заносим адрес точки СПРАВА от нас, 0x12, это "правая фиктивная точка". Переходим в начало цикла проверки.

Опять загружаем текущую координату 0x15, вычитаем X-координату правой точки, 0x7FFF, получаем что-то сугубо отрицательное, 0x8016. Берём по модулю, получается 0x7FEA. Вычитаем половинку расчётного диаметра, 6.


Ещё вычитаем половинку диаметра правой точки, 0x1A (это вообще-то адрес ближайшей свободной ячейки, но нам похрен, 7FFFF забьёт всё!). Всё в порядке, вызываем ListOp, чтобы добавить ещё одну точку в наш список ActivePoints. Мы почти добили эту строку, но пора закругляться, пока Фрэнк не сжевал этот пост...


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

  • Хотел выпендриться

    Одно из замечаний к моему протоколу информационного обмена: ДОБАВЬ 16-битные заголовки к каждому сообщению! Нам могут прислать командное слово с…

  • Моделирование стыковки с помощью ВидеоИзмерителя

    "Нарисовал"-таки видеоролик, где мы начинаем с весьма неблагоприятных условий: вместо того, чтобы нас поставить ровно "напротив" стыковочного узла,…

  • Более тяжёлые тела падают быстрее!

    Увидел не так давно видео от Flammable Maths с таким заголовком, и подумал поначалу - он опять нас троллит. Это немецкий препод математики…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments