nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

QuatCore+GPU, строка 11

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



И устраиваем "тотализатор": обработается ли всё до конца 11-й строки, или мы опять "выпадем из реального времени"?


Пора привести полный листинг программы, поскольку на ней "живого места не осталось" - прилично перелопатили. Зато теперь она компактная, всего лишь 81 слово!

    main proc
00  FD57          SP      Stack   ;инициализация стека.
        ProcessFrame proc
01  A143                          j           1   ;отныне и во веки веков!    
02  2001                          ACQ         VSync       ;"застревает" до тех пор, пока кадровый импульс не придёт
03  A249                          k           9
04  ED79                          Z           D1                                              
05  203A              @@topRows:  ACQ         HSync
06  AA5F                          kLOOP       @@topRows
07  8AE8                          C           [Z+k]           ;ожидаемый диаметр точки, всё-таки штука весьма нужная, лучше держать локальной перем.                                              
08  FE00                          [SP+2j]     0               ;в [SP+2] храним номер строки. начнём всё-таки с максимума... 
09  B049                          JMP         @@FinalRange    ;чтобы СНАЧАЛА отправить запрос, а потом уже смотреть результат, так почему-то удобнее                  
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  B813                          JL          @@MidCycle      ;пятна нет, не торопимся заказывать отрезок
11  EDCD                          Z           X       ;Чтобы в Merge всегда использовать Z, независимо от того, левая или правая точка оказались
12  A043                          i           1
13  80F0          @@checkCycle:   Acc         [SP+1]
14  83EA                          SUB         [Z+2j+k]
15  8480                          ABS         Acc
16  FC80                          [SP]        Acc     ;модуль разности - это и будет новый "диаметр" точки, если сработает слияние
17  8F83                          DIV2S       C       ;не будем мудрить пока что, всё "в лоб"
18  8FE0                          DIV2S       [Z+1]                       
19  B82D                          JL          @@DoMerge
1A  EDC8                          Z           [X+k]
1B  A837                          iLOOP       @@checkCycle
1C  DD65                          Y           Heap                                    
1D  F30A                          [SP++]      @@AfterListOp
    ListOp proc         
1E  8879                  ZAcc    RoundZero
1F  EDD8                  Z       [Y+k]
20  83D8                  SUB     [Y+k]
21  D8E8                  [Y+k]   [Z+k]
22  BC61                  JGE     @@OutOfMem                                  
23  E8C8                  [Z+k]   [X+k]
24  C8ED                  [X+k]   Z
25  8957                  NOP     0
26  B0FF                  JMP     [--SP]
27  FFB8      @@OutOfMem: [--SP]  Call(OutOfMemory)   ;SP возвращается на место, но выпрыгиваем в другое место, на обработку ошибки
    ListOp endp                     
28  CDED              @@AfterListOp:  X       Z       ;для дальнейшей работы (после MidCycle)... Чтобы мы не взялись за только что добавленную точку!
29  E083                              [Z+1]   C       ;гориз. размер точки (будет расти при слияниях, а поначалу D, т.к мы сюда включаем и )                                                                                                                      
2A  F336                              [SP++]  @@MidCycle      ;после вызова AcqQueue возвращаемся
    AcqAndUpdate proc   
2B  A023              i           2
2C  8957              NOP         0   ;избежать Hazard, ведь i участвует в следующей строке
2D  C6F4  @@updCycle: [X+2j+i]    [SP+i]
2E  A85F              iLOOP       @@updCycle
2F  80CA  AcqNoUpd:   Acc         [X+2j+k]
30  8FC0              DIV2S       [X+1]   ;теперь в аккмуляторе у нас X - Ceil(D/2)
31  2080              ACQ         Acc     ;первый отрезок
32  82C0              ADD         [X+1]   ;а вот теперь X + Floor(D/2)
33  2080              ACQ         Acc     ;второй отрезок
34  B0FF              JMP         [--SP]
    AcqAndUpdate endp   
35  E0FC          @@DoMerge:      [Z+1]       [SP]    ;пока так           
36  DDCD          @@MidCycle:     Y           X
37  CDC8                          X           [X+k]                               
38  8879                          ZAcc        RoundZero
39  83C8                          SUB         [X+k]
3A  BC59                          JGE         @@FinalRange    ;наткнулись на NULL, значит, все точки обработали...                        
3B  F288                          [SP+2j+1]   GPUL        ;яркость
3C  8088                          Acc         GPUL
3D  83CB                          SUB         [X+4j+k]                        
3E  F08A                          [SP+1]      GPUH        ;пока не забыли - сразу второе слово запрашиваем (коорд точки), чтобы не сбить весь FIFO 
3F  F319                          [SP++]      @@ActPointsStart    ;нам по-любому придётся вернуться именно сюда!
40  B84B                          JL          AcqAndUpdate    ;отсюда "выпрыгнем" сразу на метку @@ActPointsStart                     
41  80C2                          Acc         [X+2j+1]    ;координата точки (из списка)
42  8EC0                          DIV2A       [X+1]       ;прибавить половинку её диаметра
43  83F0                          SUB         [SP+1]  ;вычесть текущий номер строки
44  BC4B                          JGE         AcqNoUpd    ;здесь ОБЯЗАН быть именно JGE, т.к флаг знака является одним из "аргументов" для AcqQueue
45  CD45                          X           AllPoints
46  F3B4                          CALL        ListOp
47  CDDD                          X           Y       ;теперь [X] ссылается уже не на обработанную и удалённую точку, а на следующую, так что всё верно
48  B0FF                          JMP         [--SP]  ;ура...                 
49  207C          @@FinalRange:   ACQ         WholeRow
4A  203A                          ACQ         HSync
4B  80FE                          Acc         [SP+2j]
4C  830A                          SUB         ImgHeight
4D  824A                          ADD         ImgHeightP1
4E  B82F                          JL          @@newRow
        ProcessFrame endp
4F  8957      OutOfMemory:    NOP     0       
50  B004      @@endless:      JMP     @@endless
    main endp   


А остановились мы на том, как уже обработали первый отрезок, ничего там не нашли, убедились что точек ещё много - и запросили следующий отрезок:


На нём мы получили яркость 0x6BD. Из неё мы вычитаем яркость с прошлых строк, 0xBD9, и результат оказывается в пользу текущей строки. Также запрашиваем координату, 0x05, заносим в стек адрес @@ActPointsStart = 0x0C, и прыгаем в процедуру AcqAndUpdate, адрес 0x2B.

Там мы присваиваем i=2, пропускаем один такт (NOP), после чего:


обновляем параметры точки в списке: яркость 0x6BD, Y-координата 0x0B (т.е 11-я), X-координата 0x05.

После чего готовим задания для GPU: берём координату центра пятна, 0x05, вычитаем из неё диаметр (6), делёный на два.


Отправляем ACQ 0x02, как и в прошлый раз. Затем прибавляем диаметр, получается 8, и отправляем ACQ 0x08 - всё верно. Успешно возвращаемся из процедуры сразу в @@ActPointsStart (0x0C), где тут же запрашиваем у GPU следующий отрезок. Получаем яркость 0xFFF (нулевую) и координату 0.


После мучительного сравнения понимаем, что здесь ничего нет - и прыгаем в @@MidCycle. Там адрес текущей точки, 0x1F, отправляется в регистр Y, на всякий случай, который сейчас наконец-то представится! А в регистр X кладём адрес следующей точки, 0x15, и в аккумулятор - следующей за ней, 0x1A.

Раз это не NULL, то заканчивать строку ещё рано, и мы тут же запрашиваем очередной отрезок у GPU. Яркость 0xFFF (нулевая), координата 0. Сравниваем с яркостью нашей самой первой точки, 0x39A.



Заносим в стек адрес @@ActPointsStart, поскольку всё равно предстоит туда прыгнуть, хоть тушкой, хоть чучелом. Пропускаем вызов AcqAndUpdate, поскольку яркость "пошла на спад". Теперь смотрим, а не пора ли отправить эту точку "на покой", в список AllPoints. Чтобы это определить, берём её Y-координату (7), прибавляем половинку её диаметра (6), вычитаем текущую Y-координату (11=0x1B), и поскольку результат отрицательный, на AcqNoUpd мы тоже не прыгаем. Вместо этого в регистр X помещаем указатель AllPoints, 0x11, где пока что лежит Null, т.к список пустой. И вызываем процедуру ListOp!


Люблю процедуры, которые в один скриншот помещаются! Значение из [Y], 0x15, вычитается из обнулённого аккумулятора, убедиться что это не NULL, и оно же помещается в регистр Z.

Дальше [Y]=[Z]=0x1A, т.е предыдущая точка (которую мы совсем недавно обнаружили) будет дальше ссылаться не на текущую (левую верхнюю), а на следующую за ней (правую верхнюю).
В OutOfMem не перешли - и то радость.
Дальше [Z]=[X]=0x8000, т.е только что отцепленная точка теперь ссылается в NULL, указывая, что в списке пока всего один элемент.
И наконец, [X] = Z = 0x15, то есть указатель AllPoints, который раньше указывал в NULL, теперь указывает в эту точку - перецепка завершена, и мы с почётом возвращаемся из процедуры.

Наконец, присваиваем X = Y, то есть возвращаемся к предыдущей точке, поскольку текущая уже "удалена" из списка ActivePoints.


И сразу же запрашиваем очередной отрезок. И о чудо - там опять яркость 0xFFF (нулевая) и координата 0. Прыгаем в @@MidCycle, где переходим на следующую точку 0x1A.


Между прочим, это уже предпоследний отрезок на строке. Неплохо идём, ещё больше 100 тактов есть в запасе!
Сравнив яркость 0xFFF с ранее записанной 0x6BD, решаем точку не "обновлять". Вместо этого берём её Y-координату, 7, прибавляем её диаметр (6), делённый на 2, вычитаем текущую Y-координату (11), получаем отрицательное значение - и принимаем решение удалить и эту точку!


Адрес удаляемой точки, 0x1A, заносится в регистр Z и вычитается из аккумулятора, проверить на NULL.
Затем [Y]=[Z]=0x12, т.е предыдущая точка теперь будет указывать на "правую фиктивную точку".
[Z]=[X]=0x15, т.е удаляемая сейчас точка будет указывать на ранее удалённую, а та, в свою очередь, на NULL.
[X] =Z=0x1A, то есть указатель AllPoints указывает на только что "удалённую" точку. Всё отлично!


Вернулись из процедуры, поставили X=0x1F (ШО, ОПЯТЬ? Это самая первая точка на строке!). И переместились в начало цикла по активным точкам.

Там мы запрашиваем последний отрезок на строке! Он оказывается столь же скучным, яркость 0xFFF (нулевая) и координата 0. Но теперь сброс буфера до того, как мы его успеем обработать, УЖЕ НЕ ГРОЗИТ!.


Успешно прыгаем в @@MidCycle, где регистр X "перемещается" на следующую точку, 0x12, из аккумулятора вычитаем адрес точки, следующей за этой, 0x8000, тем самым обнаруживая что это NULL. Значит, мы всё обработали, осталось только выдать два последних задания на этой строке.

Переходим на метку @@FinalRange, где посылаем последние два задания ("до конца строки" и "синхроимпульс+back porch"). Они проходят очень быстро (каждый за такт), поскольку входной буфер теперь большой, а сейчас заданий стало очень мало. Пока сидит "текущее задание" на синхроимпульс, затем отрезки до 0x02 (поискать что-то слева от нашей точки), до 0x08 (уточнить координаты этой точки) и до 0x1F (до конца строки), и наконец на синхроимпульс на конце следующей строки, итого 5 штук.

Наконец, загружаем в аккумулятор номер строки, 0x0B,


Проверяем, не пора ли закругляться, прибавляем единичку, переходим в начало цикла по строкам, заносим номер строки в [SP+2] - и начинаем ждать первого отрезка новой строки.

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

  • Нахождение двух самых отдалённых точек

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

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

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

  • Балансируем конвейер QuatCore

    В пятницу у нас всё замечательно сработало на симуляции, первые 16 миллисекунд полёт нормальный. А вот прошить весь проект на ПЛИС и попробовать "в…

  • Огари разговаривают

    Сегодня по пути на работу встретил огарей прямо в Лосином острове, на берегу Яузы. Эти были на удивление бесстрашны, занимались своими делами, не…

  • Мартовское велосипедное

    Продолжаю кататься на работу и с работы на велосипеде, а также в РКК Энергию и на дачу. Хотя на две недели случился перерыв, очередная поломка,…

  • Обнаружение на новом GPU - первые 16 мс

    Закончилась симуляция. UFLO и OFLO ни разу не возникли, что не может не радовать. За это время мы дошли до строки 0x10F = 271. Поглядим дамп памяти:…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments