nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

QuatCore+GPU, строка 10 (дубль 3)

Ну теперь-то должно получится, Нео же любит Тринити?

В этот раз длина входного буфера GPU 8, выходного: 5. Смотрим самое начало 10-й строки, когда только-только началась информативная её часть:


В кои-то веки мы застряли не на отправке заданий (отправили все, они дожидаются очереди в буфере), а на ПРИЁМЕ, а тут мы просто обязаны застрять, поскольку данные ещё НЕ ПОЯВИЛИСЬ! Так что мы на низком старте...


Закончился Back porch ("полочка" между синхроимпульсом и началом полезной части строки), счётчик в GPU сбросился в ноль - началась полезная часть строки. Но мы по-прежнему ждём. Доходим до пикселя 0x0B - именно до него было "первое задание", но по-прежнему стоим, так как на этом такте данные "защёлкиваются" в выходной буфер. Наступает 0x0C - и мы по-прежнему ждём, поскольку сейчас данные из выходного буфера "защёлкиваются" на шину данных. А уже к 0x0D мы наконец-то переходим к следующей команде.

Мы получаем яркость 0xBD9 и координату 0x05. Сравниваем яркость с порогом и понимаем, что порог превышается, поэтому не прыгаем в @@MidCycle, вместо этого решаем добавить новую точку.


Но сначала проверяем, не слишком ли она близко к уже обнаруженным? Берём X-координату, 5, вычитаем X-координату "фиктивной левой точки", 0x8000, результат берём по модулю, на всякий случай сохраняем в [SP], потом вычитаем половинки диаметров каждой из точек, и поскольку результат заведомо выходит неотрицательным, не прыгаем в @@DoMerge, вместо этого присваиваем Z адрес точки справа от обнаруженной, 0x15.
Ещё запоминаем, что счётчик GPU у нас досчитал до 0x29 - и снова сбросился в ноль по приходу строчного импульса. Это нужно будет, чтобы определить время выполнения различных ветвей программы.



Вторая итерация, теперь сравниваем с точкой справа. Здесь более осмысленные вычисления: из 5 вычитаем 0x0E = 14, получаем -9, берём модуль, выходит 9, вычитаем половину диаметров (два раза по 3), выходит 3.


Выходит, что и эти точки разнесены, поэтому успешно завершаем цикл, присваиваем Y = Heap, то есть 0x12 - адрес в памяти, где лежит указатель на свободные ячейки для списков. И вызываем ListOp. Там успешно "перецепляем" одну свободную ячейку в список ActivePoints, попутно убеждаясь, что Heap ещё не опустел (иначе прыгнули бы в OutOfMemory). Возвращаемся на то же место.

Присваиваем X = Z = 0x1F, т.е он указывает на только что добавленный элемент.
Записываем диаметр обнаруженной точки, 6 (это наше значение по умолчанию), а потом заносим в стек нужный нам адрес возврата, на метку @@MidCycle, после чего вызываем процедуру AcqQueue.


Тут всё хорошо: инициализируем только что добавленный элемент списка: яркость 0xBD9, Y-координата 0x0A (то есть 10), X-координата 0x05. Достаточно смотреть на шину данных, когда DestAddr = 0xC6, что означает [X+2j+i].

Затем готовим отрезки к отправке: берём координату 5, вычитаем половину диаметра (6), получая 2, эту двойку и отправляем:


О ЧУДО, мы выдали задание, и от прочтения данных прошло всего-то 112 тактов... Затем, ещё спустя 6 тактов, мы отправляем второе задание, до пикселя 8. И ещё спустя 13 тактов, которые ушли на возврат из процедуры и проверку, остались ли ещё яркие точки в списке, мы наконец-то запрашиваем следующий отрезок у GPU. (запоминаем, что счётчик GPU в этот момент показывал 0x66)

Он оказывается пустым (яркость 0xFFF), но разумеется мы должны из этой яркости вычесть яркость точки из списка, "под которой" мы сейчас находимся.


Запрашиваем и X-координату (она нулевая), и прыгаем на @@NotSoBright. Там проверяем, не пора ли удалить эту точку из ActivePoints, поскольку она уже слишком "высоко", и новая строка никоим образом её не "зацепит". Берём Y-координату точки из списка, 7, прибавляем половинку диаметра (6), получаем 10, вычитаем текущую Y-координату (10) и получаем ноль - неотрицательное число, и мы решаем пока точку не удалять! (в прошлой, "более медленной" реализации, точка удалялась уже на этой строке из-за асимметрии дополнительного кода). Поэтому как ни в чём не бывало прыгаем в @@QueueAnyway, где задаём адрес возврата @@ActPointsStart и вызываем процедуру AcqQueue.

Там цикл пропускается (обновлять запись не нужно!), и мы сразу выходим на выдачу заданий:


Выдаём отрезок до 0x0B, и от получения данных по выдачи у нас прошло 43 такта. Да, гораздо меньше возни, чем с добавлением новой точки - не нужно списки перецеплять, не нужно проверять расстояние до соседей. Ещё спустя 6 тактов, выдаём ещё одно задание, до 0x11,а ещё спустя 4 (!) такта - запрашиваем очередной отрезок у GPU. Да, здесь пошустрее дела пошли...

Отрезок оказался пустым: яркость 0xFFF (нулевая) и координата 0. Как обычно, сравниваем с порогом.


И сразу же прыгаем в @@MidCycle, где переходим на следующую точку из списка, убеждаемся что за ней есть по крайней мере ещё одна (фиктивная правая), и сразу же запрашиваем очередной отрезок у GPU. Между двумя запросами к GPU прошло 20 тактов. Отрезок опять оказался пустым: яркость 0xFFF, координата 0. Сравниваем его яркость с яркостью текущей точки из списка.


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


Здесь мы видим, как выдаются задания до координат 0x12 и 0x18, после чего мы выпрыгиваем в начало цикла по точкам списка и запрашиваем последний отрезок на строке. Кординату мы забираем, когда счётчик GPU показывал 0xE5, тогда как в 0xEE он бы сбросил выходной буфер! То есть, задержись мы на лишние 9 тактов - и опять бы не успели!



Перепрыгнули в @@MidCycle, там перешли на следующую точку и поняли, что вслед за ней точек уже не осталось, значит она "правая фиктивная", и пора закругляться. Прыгаем в @@FinalRange, где отправляем два последних задания по строке. Предыдущее задание шло до точки 0x18, а когда мы отправили задание до 0x1F, GPU уже находился на точке 0x0C, то есть и сейчас до провала оставалось 12 тактов!

Впрочем, и сейчас мы ещё в опасности: появился комбинаторный "выброс" на OFLO, который говорит: выходной буфер уже полон, и если туда попытаемся сунуть ещё что-нибудь, оно уже не влезет, и мы потеряем данные. А ведь следующий отрезок идёт до 0x18, к тому времени нужно успеть считать целиком данные из буфера!

С замиранием сердца проматываем окно симуляции дальше:


АААА! (классическая реакция на переполнение буфера, 0x41414141) Зараза, не хватило 5 тактов!!!


Ну, формально мы 10-ю строку обработали :) Правда, сразу же влетели в переполнение буфера на 11-й - не успели вовремя прочитать данные из GPU, как он весь забился! Но через 10-ю мы всё-таки пробились.

Сейчас, правда, ещё один "финт ушами" хочется опробовать, как сократить код на 1 команду (с 83 до 82 слов) и в то же время сократить время выполнения на десяток кадров на строку изображения...
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