nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

Алгоритм обнаружения на симуляции

На удивление долго эту симуляцию получал - сразу несколько подводных камней выползло. Уже не раз и не два замечаю: Quartus очень легкомысленно относится к инициализации регистров! Иногда инициализирует как написано - а зачастую НЕТ. Вот и сейчас, в первый раз процессор тупо "не запустился" - вижу, что висит SrcStall=1 и DestStall=1 - да так и висит, и мы остаёмся в PC=0 до скончания времён. Стал искать, каким образом так получилось, повтыкал побольше "выводов" - и тут-то всё запустилось как надо!

Потом пришлось вспоминать "особенность" моего модуля FastFreqDivider. Всем он хорош, но при "первом включении" отсчитывает больше, чем надо. И в генераторе тестового изображения он "превзошёл сам себя" - там было задано количество ПОЛЕЗНЫХ строк изображения: 128, и ещё 28 строк сверху (как в реальном сигнале CVI) и 10 строк снизу. Я посчитал, сколько времени должен занять один кадр - 2,67 мс, и задал время симуляции в 4 мс. Какое же было моё удивление, когда к окончанию отрезка первый кадр так и не закончился, и кадровый синхроимпульс не пришёл!

Оказалось: у меня для формирования кадровых синхроимпульсов стоит этот самый несчастный FastFreqDivider, для деления в 166 раз в нём используется 8-битный счётчик, который первый раз успевает досчитать до 256! Вот поэтому и выходит вместо 2,67 мс уже 4,12 мс - вот оно и вышло.

Теперь вот я забыл, как работает регистрация кадрового синхроимпульса, как так выходит, что только после СТРОЧНОГО импульса, идущего за кадровым, у нас хоть что-то сдвинулось с места:
QuatCoreAndGPU_for_simul.png


Кажется, вспомнил. Вот строка, по которой "засчитывается выполнение задания":

wire start_normal = (~SyncOut[1])&(~SyncOut[0])&(BufOut[XregWidth-1:0] == X);


По приходу кадрового синхроимпульса у нас сбрасывается выход SyncOut[1], но вот регистр X успевает убежать вперёд от нуля (он сбрасывается только строчным синхроимпульсом), поэтому задание ещё "не засчитывается".

И только когда строчным синхроимпульсом сбрасывается счётчик X (номер пикселя в строке) - тут-то мы и переходим к следующему заданию, а именно ACQ HSync. И таковых у нас 28 штук, по числу "верхних" строк, выходящих за пределы экрана.

Вот листинг соответствующего места:
1B  200C      @@topRows:  ACQ     HSync
1C  AA0D                  kLOOP   @@topRows
1D  FE0E                  [SP+2j] -1              ;в [SP+2] храним номер строки.
1E  B00F                  JMP     @@FinalRange    ;чтобы СНАЧАЛА отправить запрос, а потом уже смотреть результат, так почему-то удобнее


Как видно, мы очень надолго застряли на DestAddr = 0x20 (ACQ) и SrcAddr = 0x0D (@@topRows), так как буфер заданий GPU уже заполнен, и мы ждём, когда же он освободится.

Синхроимпульс словили, одно место в буфере появилось, и мы наконец перешли к следующей команде, kLOOP, при этом на шину данных хотели посадить "-1" для следующей команды, но это оказалось ненужным.

Далее PC=0x1B - прыжок произведён, но пока DestAddr = 0xFE (команда [SP+2j]) и SrcADdr = 0x0F (@@FinalRange), и одновременно DestStall=1 и SrcDiscard=1, чтобы эти команды не выполнялись, они здесь по ошибке.

Далее DestAddr = 0xB0 (JMP), но DestStall=1 не даёт выполниться, а вот SrcAddr = 0x0C (HSync) корректен - на шину данных заносится значение 0x4106.

И наконец, мы снова пытаемся выполнить ACQ - и застреваем ещё на одну строку изображения!

Подобное происходит около 20 раз (число "верхних строк" минус размер входного буфера), тут ничего интересного, давайте сразу перейдём к тому месту, где мы выйдем из цикла:


Интересующие фрагменты из листинга:

1B  200C      @@topRows:  ACQ     HSync
1C  AA0D                  kLOOP   @@topRows
1D  FE0E                  [SP+2j] -1      ;в [SP+2] храним номер строки.
1E  B00F                  JMP     @@FinalRange    ;чтобы СНАЧАЛА отправить запрос, а потом уже смотреть результат, так почему-то удобнее                  
1F  FE80      @@newRow:   [SP+2j] Acc
20  CD10                  X       ActivePoints    ;начинаем цикл по всем активным точкам


и

8D  2023      @@FinalRange:   ACQ     WholeRow
8E  200C                      ACQ     HSync
8F  80FE                      Acc     [SP+2j]


На этот раз мы дошли до JMP @@FinalRange, потом "сбрасываем конвейер" (попавшие туда команды объявляются недействительными) и, наконец, выполняем ACQ WholeRow (получить самый яркий пиксель на первой строке изображения), и СНОВА ЗАСТРЕВАЕМ.

Ещё спустя одну строку (пока ещё бесполезную, "верхнюю"):


Ну да, теперь застряли на строке ACQ HSync, ещё на одну "верхнюю" строку изображения. И вот наконец:


Листинг:

8E  200C                  ACQ     HSync
8F  80FE                  Acc     [SP+2j]
90  8324                  SUB     ImgHeight
91  8225                  ADD     ImgHeightP1
92  DD10                  Y       ActivePoints    ;это заведомо фиктивная, и указывает либо на другую фиктивную, то ли на реальную
93  CD0B                  X       AllPoints
94  DDD8                  Y       [Y+k]           ;это уже может быть реальная точка, а может быть правой фиктивной
95  FC26                  [SP]    @@Transfer                                                                              
96  B827                  JL      @@newRow
97  84D8      @@Transfer: ABS     [Y+k]
98  B828                  JL      ListOp      ;если [Y+k]=NULL, значит пора закругляться 


и затем

1F  FE80      @@newRow:         [SP+2j]     Acc
20  CD10                        X           ActivePoints    ;начинаем цикл по всем активным точкам
21  8A09      @@ResetPending:   C           0               ;уж явно не 0x8000          
22  F288      @@ActPointsStart: [SP+2j+1]   GPUL            ;яркость самой яркой точки на отрезке
23  F08A                        [SP+1]      GPUH            ;соотв. координата


Идёт строка Acc [SP+2j], заносим в аккумулятор текущую строку, "-1" (0xFFFF). Вычитаем ImgHeight=720=0x2D0. Прибавляем ImgHeightP1=721=0x2D1.

Затем в регистр Y заносим адрес ActivePoints. Вот фрагмент листинга памяти:

ActivePoints:     09B  0x009D
AllPoints:        09C  0x8000
APHeadDummyX:     09D  0x8000
Heap:             09E  0x00A0
APTailDummyX:     09F  0x7FFF


Т.е в Y должно пойти 0x009B - да, так оно и есть.
Затем в регистр X заносится адрес AllPoints, это должно быть 0x009C - всё верно.
Далее в Y заносится теперь уже значение из ActivePoints, 0x009D - всё верно. Сейчас список ActivePoints состоит из двух "фиктивных" элементов, один левый, другой правый. Вот сейчас перешли на правый фиктивный.

Затем ОЧЕНЬ ВНЕЗАПНО в [SP] заносится метка @@Transfer = 0x0097 - да, видим это в шине данных.

И наконец, идёт JL @@NewRow, т.е если при вычитании 720 из текущего номера строки у нас получалось отрицательное значение - значит мы ещё не закончили, и нужно прыгнуть в @@NewRow = 0x001F. Видим подгрузку этого значения в шину данных, и затем прыжок.

Это мы так здорово сэкономили 1 строку кода, введя внутрь цикла 4 строки, которые нужны уже ПО ОКОНЧАНИИ ЦИКЛА, решив, "не утянет". Здесь можно увидеть, что выполняются они за 5 тактов, или 200 нс. Вроде ничему не противоречит...

Понятно, во время прыжка мы прочищаем конвейер, и начинается работа по строке изображения. Первым делом инициализируем цикл по всем активным пятнам.

[SP+2j] Acc
- возвращаем на место обновлённый номер строки, сейчас это ноль.

X ActivePoints
- заносим в регистр X уже знакомый адрес 0x009B. Да, смотрим шину данных - всё нормально.

C  0
- убираем флаг "TaskPending" ("за нами должок"). Видно, что один такт есть на выполнение этой операции, а затем зажигается DestStall=1.

Это мы начинаем ждать результата GPUL - яркости самого яркого пикселя на строке. И вот здесь мы застреваем очень основательно, поскольку должны закончиться все "верхние" строки, и быть полностью обработана нулевая "полезная" строка изображения. Смотрим, сейчас метка времени 2,476 мс, и теперь находим продолжение работы.


Первый фрагмент листинга:
22  F288      @@ActPointsStart:   [SP+2j+1]   GPUL            ;яркость самой яркой точки на отрезке
23  F08A                  [SP+1]  GPUH            ;соотв. координата
24  8011                  Acc     Threshold       ;поставили задом наперёд, чтобы избежать Hazard'а   
25  83F2                  SUB     [SP+2j+1]       ;вычитаем порог, положит. значение свидетельствует о новом найденном "пятне"                        
26  FC12                  [SP]        @@MidCycle
27  B813                  JL      TaskPending ;пятна нет, не торопимся заказывать отрезок
28  80CA                  Acc     [X+2j+k]
29  83F0                  SUB     [SP+1]


Итак, максимальную яркость мы получили 0xFF, только не забываем об инверсии, т.е на самом деле она нулевая. Всё верно, вот наше тестовое изображение:


Верхняя строка в нём чёрная от начала до конца.

[SP+1] GPUH
- получить X-координату самого яркого пикселя. И тут мы видим странность, значение 0x106. Это число - X-координата предыдущего отрезка, идущего от синхроимпульса до полезной части строки. Как бы самое его окончание вошло в наш отрезок. Если у нас будет пятно непосредственно слева, даже до конца не влезающее в кадр, можем огрести по полной! Опять, из-за сочетания факторов - на самом деле на строке у нас должно быть 1280 пикселей, а мы решили взять только 1024 посерединке, поскольку именно столько будет в "штатной" фотоприёмной матрице. Так что на этом предыдущем отрезке мы вполне можем словить очень яркий пиксель, и координата X=262 добавленная "бездумно" в параметры пятна наделает делов! Надо будет это исправить, но пока не знаю, как именно... Напомню, это мы совсем недавно внесли изменения в видеопроцессор, чтобы он ни одного пикселя не "терял", вот и появилась новая "особенность"...

Но конкретно сейчас ошибки не будет, так что продолжим наблюдения.

Acc     Threshold
- занесли значение 1, это довольно зверский порог, соответствующий 254, или 0xFE.

SUB     [SP+2j+1]
- вычитаем из него только что найденную яркость пикселя (инвертированную), то есть 0xFF.

[SP]    @@MidCycle
- заносим "адрес возврата" @@MidCycle = 0x71.

JL  TaskPending
- если яркого пикселя (превышающего порог) не было - проверяем свой "должок" - и оттуда возвращаемся прямиком в @@MidCycle. Тут интересно, что на шину данных пришло 0x0434, хотя адрес TaskPending: 0x0034. Это отработал наш весёлый компилятор. Он заметил, что адресация ПЗУ у нас 8-битная, поэтому на старший байт здесь наплевать, а вот чтобы добраться до строки NoVideoSignal, да ещё и в "байтовом режиме", нужно как раз 0x434:

0x0434  FFFF JL TaskPending/JMP TaskPending/C NoVideoSignal 


правда, здесь всё же компилятор не доработал самую малость. Поскольку мы загружали этот адрес в 16-битный регистр C, он "на всякий случай" поставил 16-битную маску, FFFF, хотя на самом деле хватило бы 11 бит (9 бит ширина ОЗУ + 2 бита на режим доступа). Но скорее всего, никакого выигрыша мы бы от этого не получили.

Ладно, поехали дальше. Вот мы и прыгнули в TaskPending, приведём его листинг:

    TaskPending proc
34  8483              ABS     C
35  BEFC              JNO     [SP]    ;вот в чём прелесть стека без инкремента!
36  80CA  AcqNoCheck: Acc     [X+2j+k]
37  8FC0              DIV2S   [X+1]   ;теперь в аккмуляторе у нас X - Ceil(D/2)
38  8E11              DIV2A   1       ;чтобы всё-таки было X-Floor(D/2)
39  2080              ACQ     Acc     ;первый отрезок
3A  82C0              ADD     [X+1]   ;а вот теперь X + Floor(D/2)
3B  2080              ACQ     Acc     ;второй отрезок
3C  B0FC              JMP     [SP]
    TaskPending endp    


ABS C
- проверяем на "специальное" значение "-32768" (0x8000) - только оно даст переполнение при взятие абсолютного значения! У нас появляется таинственное 0x8000 на шине данных, причём отыскать его происхождение не так-то просто. Вроде как оно появляется по команде [SP+1], где должна лежать X-координата найденной яркой точки, но там мы ожидаем 0x106. Другое дело, для правильной выборки из памяти надо 2 такта, а мы 1 такт "пожадничали", ведь команда всё равно недействительная! Так что 0x8000 пришёл очевидно из памяти, но с предыдущего запроса, [X+2j+k], это X-координата "левой фиктивной точки". И да, именно "-32768" там и лежит, чтобы заведомо к ней ничего не "прилипало". Ладно, а из регистра C мы действительно ПОЛУЧАЕМ НОЛИК, как и должно быть.

JNO  [SP]
- переполнения у нас не случилось, поэтому сразу же и прыгаем по "адресу возврата", ранее положенному туда @@MidCycle = 0x0071.

Листинг фрагмента @@MidCycle:

71  CDC8  @@MidCycle:     X           [X+k]
72  DDCD                  Y           X
73  84C8                  ABS         [X+k]
74  BA0F                  JO          @@FinalRange
75  F288  @@EndOfCycle:   [SP+2j+1]   GPUL            ;яркость
76  F08A                  [SP+1]      GPUH            ;пока не забыли - сразу второе слово запрашиваем (коорд точки), чтобы не сбить весь FIFO 


Посмотрим, что здесь происходит.
 X   [X+k]
- переходим на следующий элемент списка активных пятен. Приведём всё тот же листинг памяти:

ActivePoints:     09B  0x009D
AllPoints:        09C  0x8000
APHeadDummyX:     09D  0x8000
Heap:             09E  0x00A0
APTailDummyX:     09F  0x7FFF


Было 0x009B, должно стать 0x009D. Действительно, так и есть.

Y       X
- тут, как ни странно, мы заносим СТАРОЕ значение. Если заглянуть в код программы, мы увидим:

		%CheckHazards OFF
@@MidCycle:	X	[X+k]
		Y	X
		%CheckHazards ON


И да, на шине данных "старое" значение 0x009B. Всё по плану.

ABS [X+k]
- вот тут уже поступает -32768 (0x8000), которое приведёт к переполнению.

 JO      @@FinalRange
- срабатывает. Так что переходим в самый конец цикла, где мы уже однажды были.

Отправляем задания ACQ WholeRow (сейчас это до 127) и ACQ HSync, сейчас эти команды выполняются ЗА ОДИН ТАКТ, поскольку буфер заметно опустел. Мы же всё ждали результатов работы GPU, а все "верхние строчки" никаких результатов не выдавали, и осталось там только одно задание - дождаться строчного синхроимпульса. Мы можем видеть на осциллограмме тот самый синхроимпульс, т.е уже началась следующая строка, но задание ещё не вышло, оно ещё отсчитает 262 такта после него. И сейчас во входном буфере GPU оказывается 3 задания.

Дальше кусок, который не попал на скриншот - снова прибавляется единичка к номеру строки изображения (попутно проверяя, надо ли сделать ещё одну итерацию), затем 4 строки, которые нужны уже ПОСЛЕ цикла, прыгаем в начало цикла, заносим новый номер строки - и снова ждём результатов работы GPU.

Результатом опять становится 0xFF, что означает яркость НОЛЬ. И так у нас повторяется много строк подряд, абсолютно идентично, никаких проблем, пока мы, наконец, не приходим к верхушке ПЯТНА:


Вот тут мы получаем яркость 0x0000 (что означает МАКСИМАЛЬНУЮ, 255) и координату 0x3D = 61 - похоже на правду. На тестовом изображении это вообще-то пиксель (60;13), ну да ладно, где-то в этих back_porch и выборке памяти в генераторе тестового изображения на единичку ошиблись, с кем не бывает.

Загружаем в аккумулятор порог, 1, вычитаем 0, заносим в [SP] адрес возврата для процедуры TaskPending, но условный переход JL TaskPending не срабатывает. Начинаем проверки с пятнами слева и справа от нашего пикселя:

28  80CA                  Acc     [X+2j+k]
29  83F0                  SUB     [SP+1]
2A  8E01                  DIV2A   D1
2B  8EC0                  DIV2A   [X+1]
2C  EDC8                  Z       [X+k]
2D  BC14                  JGE     @@LeftMerge
2E  80EA                  Acc     [Z+2j+k]
2F  83F0                  SUB     [SP+1]
30  8F15                  DIV2S   D1p1
31  8FE0                  DIV2S   [Z+1]
32  B816                  JL      @@RightMerge
33  FC17                  [SP]    @@NewBlob


Пока у нас слева "левое фиктивное пятно" с X-координатой "-32768" (0x8000), вот это число мы и загружаем, вычитаем 0x3D, потом добавляем 3/2 (D1/2), и диаметр "фиктивной точки", делённый на 2 (здесь это тоже 0x8000), затем в Z заносим адрес точки правее нас, 0x009D.

Условный переход JGE @@LeftMerge не срабатывает, всё верно, по фиктивным точкам он не должен срабатывать никогда!

Поэтому теперь проверяем пятно справа от себя, на данный момент тоже фиктивное. Загружаем его X-координату, 32767 (0x7FFF). Вычитаем текущую координату, 0x3D. Следующий слайд:


Далее вычитаем (D1+1)/2, и вычитаем половину диаметра "правого фиктивного пятна", которое здесь совпадает с указателем Heap, 0x00A0.

Условный переход JL @@RightMerge также не выполняется, что не может не радовать, так и должно быть.

В [SP] ("адрес возврата") заносим @@NewBlob = 0x3D, после чего "своим ходом" выходим на "процедуру" TaskPending, чтобы выдать задания на обработку до того, как добавим новое пятно.

Но там мы надолго не задерживаемся, поскольку "долгов за нами нет". В этом мы быстро убеждаемся по отсутствию переполнению, и выходим на метку @@NewBlob:

3D  DD18  @@NewBlob:      Y       Heap                                    
3E  FC19                  [SP]    @@AfterListOp
3F  8900                  NOP     0           ;на следующей строке ListOp нам нужен и Y уже готовый, и доступ к памяти, так что Hazard по-любому будет какой-то...
    ListOp proc         
40  EDD8                  Z       [Y+k]
41  84D8                  ABS     [Y+k]
42  D8E8  MergeBlobCase:  [Y+k]   [Z+k]
43  BA1A                  JO      OutOfMemory                                 
44  E8C8                  [Z+k]   [X+k]
45  C8ED                  [X+k]   Z
46  8900                  NOP     0       ;избежать Memory hazard (одновременный доступ на чтение и запись)
47  B0FC                  JMP     [SP]
    ListOp endp                     
48  80EA  @@LeftMerge:    Acc     [Z+2j+k]        ;взяли X-координату правого пятна
49  83CA                  SUB     [X+2j+k]        ;вычитаем X-координату левого


Y Heap
- заносим указатель на "кучу", 0x009E.

[SP] @@AfterListOp
- помещаем "адрес возврата", @@AfterListOp = 0x6C.

NOP 0
- "ничего не делаем". Снова компилятор делает своё чёрное дело, он понимает что NOP не нуждается в операнде вообще, поэтому "сливает" нолик справа с самым-самым первым непосредственным значением в программе, а это 2 (ETH, т.е выбор Ethernet-контроллера устройством ввода-вывода, чтобы настроить тактовую частоту). Вот и оказывается эта двойка на шине данных, почему бы и нет :)

"Своим ходом" заходим в процедуру ListOp, чтобы добавить в список пятен ещё одно (перецепить его из списка Heap).
Z   [Y+k]
- нас интересует не сам указатель Heap, а первый свободный элемент, это получится 0x00A0.
ABS [Y+k]
- если Heap указывает на 0x8000, значит все элементы мы израсходовали, и нужно выругаться на закончившуюся память. Вполне поэтично это вызовет переполнение.
[Y+k] [Z+k]
- пусть Heap указывает на следующий элемент своего списка, т.е элемент с адресом [Z] перестаёт ему принадлежать.

Снова приведём листинг памяти:
ActivePoints:     09B  0x009D
AllPoints:        09C  0x8000
APHeadDummyX:     09D  0x8000
Heap:             09E  0x00A0
APTailDummyX:     09F  0x7FFF
Elem0Next:        0A0  0x00A4
Elem0D:           0A1  ????
Elem0X:           0A2  ????
Elem0Y:           0A3  ????
Elem1:            0A4  0x00A8
Elem1[1]:         0A5  ????
Elem1[2]:         0A6  ????
Elem1[3]:         0A7  ????


Да, мы видим, как Elem0, на который ссылается Heap, сам ссылается на Elem1 (0x0A4), и именно этот адрес отправляется в [Y], т.е в Heap.

JO OutOfMemory
- не срабатывает, памяти у нас ещё хоть отбавляй!

[Z+k] [X+k]
- этот новый элемент должен ссылаться на пятно справа от нас, её адрес 0x009D.

[X+k] Z
- ну а левое пятно должно теперь ссылаться на наш новый элемент, 0x0A0. Всё верно!

NOP 0
- вставлено из-за чрезмерно активной работы по шине памяти (она не может работать одновременно на чтение и запись).

JMP [SP]
- возврат из процедуры по адресу @@AfterListOp = 0x6C.

Пока всё верно... Поехали дальше:


Листинг:
6C  EAF0      @@AfterListOp:  [Z+2j+k]    [SP+1]  ;X-координата точки
6D  CDED                  X       Z       ;для дальнейшей работы (после MidCycle)... Чтобы мы не взялись за только что добавленную точку!
6E  E2FE                  [Z+2j+1]    [SP+2j]
6F  E001                  [Z+1]       D1      ;гориз. размер точки (будет расти при слияниях, а поначалу D, т.к мы сюда включаем и )
70  FCB3                  [SP]        CALL(AcqNoCheck)
71  CDC8      @@MidCycle:     X       [X+k]


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

[Z+2j+k]   [SP+1]
- координата X, 0x3D.
X          Z
- переходим с "левой фиктивной точки" на только что добавленную, иначе мы уже с неё начали бы требовать результатов работы, но ведь на текущей строки мы их не заказывали! Знакомый адрес 0x0A0.
[Z+2j+1]    [SP+2j]
- заносим Y-координату, 0x0B = 11. Да, с количеством "верхних строк" тоже немножко просчитались, на 2, но это не страшно.
[Z+1]       D1
- диаметр пятна пока что оцениваем по-минимуму, 3 (ниже не может быть просто из-за функции рассеяния точки!).
[SP]        CALL(AcqNoCheck)
- "почти нормальный" вызов процедуры. Совсем нормальный записывается "CALL AcqNoCheck", и компилятором преобразуется в "[SP++] AcqNoCheck". Вся разница, что мы не хотим делать инкремент, обнаружилось, что без него здесь удобнее.

Ещё разок приведём листинг AcqNoCheck, хотя это просто кусочек процедуры TaskPending:
36  80CA  AcqNoCheck: Acc     [X+2j+k]
37  8FC0              DIV2S   [X+1]   ;теперь в аккмуляторе у нас X - Ceil(D/2)
38  8E11              DIV2A   1   ;чтобы всё-таки было X-Floor(D/2)
39  2080              ACQ     Acc     ;первый отрезок
3A  82C0              ADD     [X+1]   ;а вот теперь X + Floor(D/2)
3B  2080              ACQ     Acc     ;второй отрезок
3C  B0FC              JMP     [SP]
    TaskPending endp    


Поглядим:
Acc   [X+2j+k]
- заносим в аккумулятор X-координату центра только что добавленного пятна, 0x3D.
DIV2S [X+1]
- вычитаем половинку диаметра этого пятна, 3.
DIV2A 1
- прибавляем 1/2, что на моделировании оказалось абсолютно необходимым.
ACQ Acc
- заказываем обработать следующую строку изображения до X=0x3C = 60. То есть, от первый отрезок будет [0;59].
ADD [X+1]
- прибавляем полный диаметр пятна, 3.
ACQ Acc
- заказываем обработать отрезок до 0x3F = 63. То есть, второй отрезок будет [60;62], как раз 3 пикселя, есть в этом определённая логика...

Сейчас у нас в буфере было одно задание, ACQ HSync, и вот добавилось ещё два. Всего мест 8, поэтому команда ACQ выполняется за 1 такт (не нужно ждать, пока освободится).

JMP [SP]
- возвращение из процедуры, на адрес 0x71. Приведём листинг:

70  FCB3                  [SP]    CALL(AcqNoCheck)
71  CDC8  @@MidCycle:     X       [X+k]
72  DDCD                  Y       X
73  84C8                  ABS     [X+k]
74  BA0F                  JO      @@FinalRange


Ну да, всё верно - мы вернулись аккурат на строку, идущую после CALL(AcqNoCheck). Тут мы переходим на следующее пятно.

Этот код уже "проверенный", попадаем на "правое фиктивное пятно", понимаем, что пора завершать итерацию, прыгаем на @@FinalRange, и там отправляем ещё 2 задания на обработку, WholeRow (т.е [62;127]) и HSync. Прибавляем единичку к номеру строки, переходим в начало цикла, и снова замираем в ожидании получения заданий...


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

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

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

  • О вытягивании себя из болота по методу Мюнхгаузена

    Всё готовлюсь к встрече с представителями РКК Энергия, нужно убедить их в моём способе определения положения ВидеоИзмерителя Параметров Сближения на…

  • Ремонт лыжных мостиков

    Вернулся с сегодняшнего субботника. Очень продуктивно: отремонтировали все ТРИ мостика! Правда, для этого надо было разделиться, благо народу…

  • Гетто-байк

    В субботу во время Великой Октябрьской резни бензопилой умудрился петуха сломать в велосипеде. По счастью, уже на следующий день удалось купить…

  • А всё-таки есть польза от ковариаций

    Вчера опробовал "сценарий", когда варьируем дальность от 1 метра до 11 метров. Получилось, что грамотное усреднение - это взять с огромными весами…

  • Так есть ли толк в ковариационной матрице?

    Задался этим вопросом применительно к своему прибору чуть более 2 недель назад. Рыл носом землю с попеременным успехом ( раз, два, три, четыре),…

  • Big Data, чтоб их ... (4)

    Наконец-то стряхнул пыль с компьютерной модели сближения, добавил в неё код, чтобы мы могли определить интересующие нас точки, и выписать…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments