nabbla (nabbla1) wrote,
nabbla
nabbla1

Category:

QuatCore+GPU: начинаем отладку!

Да, ещё пара изменений. В верхних строках программы у нас были определены константы:
	VSync			EQU	0x8000	;ожидание кадрового синхроимпульса, значение для регистра произвольное
	HSync			EQU	0x4000	;ожидание строчного синхроимпульса, а затем ещё интервал front porch (между импульсом и началом картинки)
	WholeRow		EQU	1023	;для обработки всей строки целиком	
	ImgHeight		EQU	720	;720p пока что 
	Threshold		EQU	0xDFF	;минимальная яркость пятен, сейчас поставили 1/8 от "насыщения" (4096), но с инверсией, так надо!


Меняем их значения:
	VSync			EQU	0x8000	;ожидание кадрового синхроимпульса, значение для регистра произвольное
	HSync			EQU	0x40F0	;ожидание строчного синхроимпульса, а затем ещё интервал back porch (между импульсом и началом картинки) в 240 тактов (239+1 такт на сброс счётчика + 2 такта задержки на ПЗУ)
	WholeRow		EQU	31	;для проверки на симуляторе
	ImgHeight		EQU	40	;вообще, 32, но вводим дополнительные строки, чтобы перенести все точки из ActivePoints в AllPoints
	Threshold		EQU	0xDFF	;минимальная яркость пятен, сейчас поставили 1/8 от "насыщения" (4096), но с инверсией, так надо!


Чуть дальше была строка
k    10


Переправляем 10 на 9, так кажется правильнее, это сколько строк нужно пропустить после кадрового синхроимпульса. Тут счёт от 0 (т.е если k=0, то один раз выполнится всё равно), а мы, кажется, условились первые 10 пропускать.

В видеопроцессоре у нас можно задать ширину счётчика пикселей, по умолчанию было 10 бит, т.е от 0 до 1023. Здесь нам вроде как хватило бы 5 бит (от 0 до 31), но тот же самый счётчик будет отсчитывать BackPorch, а там у нас 240 тактов, так что пускай остаётся. К сожалению, в этом "вырожденном случае" (когда обратный ход гораздо длиннее чем сама строка) не сработает наше "переполнение" на конце строки. Хотя результат (по крайней мере для обнаружения) не должен серьёзно изменится: все крайние пиксели у нас нулевые, так что ответ будет выдан тот же, только с чуть большей задержкой. Надо не забыть об этой подлянке и вернуться к ней, когда всё остальное будет работать безукоризненно.

Как водится, когда изменились константы - изменилась и QuatCoreImmTable, и при новом синтезе получилось всё вместе 1004 ЛЭ - на 3 меньше, чем с предыдущими. Частоты опять слегка не хватило, аж 5 failed paths, которые не позволили достичь 25 МГц. Правда, потом я решил ещё несколько сигналов "вывести наружу": синхроимпульсы и 12-битную яркость пикселя - и предельная частота снизилась до 23,58 МГц и 258 failed paths. Что ещё раз подтверждает: чем больше сигналов пытаешься "вывести наружу", прямо-таки на ножки - тем хуже оно распихивается. Ладно, запустим пока на 20 МГц, хорошее круглое число 50 нс длительность одного такта. Понеслась!



Похоже на правду, но уже есть странности.


Для понимания, что происходит, приведём начало листинга программы:
    main proc
00  FD57          SP      Stack   ;инициализация стека.
        ProcessFrame proc
01  A140                          j       1   ;отныне и во веки веков!    
02  2001                          ACQ     VSync       ;"застревает" до тех пор, пока кадровый импульс не придёт
03  A24B                          k       9
04  ED79                          Z       D1                                              
05  2006              @@topRows:  ACQ     HSync
06  AA5F                          kLOOP   @@topRows
07  8AE8                          C       [Z+k]           ;ожидаемый диаметр точки, всё-таки штука весьма нужная, лучше держать локальной перем.                                              


Наверху у нас уже привычные шины QuatCore: 8-битная SrcAddr - команда - "источник данных", 8-битная DestAddr - команда - "получатель данных", PC - Program Counter - счётчик инструкций, DataBus - 16-битная шина данных.

На первом такте всё по нулям, затем появляется SrcAddr=0x57, и к следующему такту на шине данных значение 0xC1B5. Поскольку оперативной памяти у нас сейчас выделено 256 слов, то берётся лишь младший байт, B5, и это верное значение, см. кусок листинга памяти:

Elem1C:          A1  0x00A6
Elem1C[1]:       A2  ????
Elem1C[2]:       A3  ????
Elem1C[3]:       A4  ????
Elem1C[4]:       A5  ????
Elem1D:          A6  0x00AB
Elem1D[1]:       A7  ????
Elem1D[2]:       A8  ????
Elem1D[3]:       A9  ????
Elem1D[4]:       AA  ????
Elem1E:          AB  0x00B0
Elem1E[1]:       AC  ????
Elem1E[2]:       AD  ????
Elem1E[3]:       AE  ????
Elem1E[4]:       AF  ????
Elem1F:          B0  0x8000
Elem1F[1]:       B1  ????
Elem1F[2]:       B2  ????
Elem1F[3]:       B3  ????
Elem1F[4]:       B4  ????
Stack:           B5  ????
Stack[1]:        B6  ????
                 B7  ????
                 B8  ????
                 B9  ????


Тем временем по DestAddr идёт 0x89 - это команда NOP, затем 0xFD - запись в регистр SP. Так что надо полагать, стек инициализируется правильно.

Далее, SrcAddr=0x40 - очередное непосредственное значение, которое волшебным образом преобразуется в 0x0001 - просто единичка. Команда-получатель: 0xA1, регистр j.

Далее, SrcAddr=0x01 - и ещё непосредственное значение, которое превращается в 0x8000. Надеюсь, понятно теперь, почему я все дела бросил и занялся этой своей QuatCoreImmTable - без неё было бы очень грустно... Адрес получателя, тем временем: 0x20, это команда ACQ, то есть мы обратились к видеопроцессору, отправили туда число 0x8000.

И как не в чём не бывало, продолжили исполнение. SrcAddr=0x4B - опять непосредственное значение (всё от 0x00 до 0x7F - непосредственные), которое преобразуется в 0x8069. Но поскольку получателем является 5-битный регистр k, то из этого хитрого числа мы возьмём младшие 5 бит, а это просто 9. Чудесно :)

Следующая команда, SrcAddr=0x79 - и снова непосредственное значение, которое превращается в 0x8C0F. Получателем является 0xED, это регистр Z. Он возьмёт младший байт, 0x0F. Посмотрим, что там у нас лежит:
EthDisable:      00    0x0002
EthDisable[1]:   01    0x0003
EthDisable[2]:   02    0x0022
EthDisable[3]:   03    0x0054
EthDisable[4]:   04    0x0000
EthDisable[5]:   05    0x0001
EthDisable[6]:   06    0x0003
EthDisable[7]:   07    0x0022
EthDisable[8]:   08    0x0066
EthDisable[9]:   09    0x0000
EthDisable[10]:  0A   0x0018
EthDisable[11]:  0B   0x0002
EthDisable[12]:  0C   0x0022
EthDisable[13]:  0D   0x006F
EthDisable[14]:  0E   0x0002
D1:              0F   0x0003
ActivePoints:    10   0x0012
AllPoints:       11   0x8000
APHeadDummyX:    12   0x8000
Heap:            13   0x0015
APTailDummyX:    14   0x7FFF
Elem0Next:       15   0x001A


Там лежит диван (D1). Именно он нам и нужен (см листинг).

Следующая команда: SrcAddr=0x06 - непосредственное значение, превращающееся в 0x40F0. Получателем является 0x20 - это команда видеопроцессору ACQ (ACQuire). И посылается ровно то, что мы ожидали, причём опять мы управляемся за один такт.

Очередная команда: SrcAddr=0x5F - непосредственное значение, превращается в 0x81FD. Получателем является 0xAA, это команда kLOOP. Она принимает относительный адрес, который по результатам компиляции приобрёл ширину 7 бит (нашёлся ОЧЕНЬ ДЛИННЫЙ прыжок, более чем на 32 позиции, что требует числа со знаком 7 бит). Так что из 0x81FD откусываем 7 бит - и получаем 0x7D. Если 0x7F - это "-1", 0x7E - это "-2", значит наш 0x7D - это "-3". А учитывая, что PC убегает на 2 шага вперёд, мы осуществляем прыжок на предыдущую команду, всего навсего. При условии, что k>0. Ща глянем...

Да, PC=5, но SrcAddr=0x0A - это команда из PC=8, очередное непосредственное значение, превращающееся в 0x0028 = 40. Это наш ImageHeight, который мы хотели записать в [SP+2j] (команда 0xFE), но эти команды по сути не были исполнены.

Далее, PC=6, Src=0x06 - это у нас уже было, превращается в 0x40F0, и посылается на видеопроцессор.

Итого, мы уже поместили в видеопроцессор 1 команду VSync (0x8000) и 2 команды HSync (0x40F0). Размер буфера: 4 ячейки, так что пока всё влезает - и мы спокойно продолжаем свою работу.

Мы успеваем отправить ещё одну команду ACQ 0x40F0, тем самым забивая буфер до упора, и на пятой посылке застреваем намертво. Так и задумано - процессор забежал вперёд паровоза и теперь должен подождать, пока видеообработчик выполнит хоть одну из команд.

Теперь посмотрим со стороны видеообработки
На первом же такте образуется UFLO=1. Само по себе его появление вполне логично: видеопроцессор "увидел" самое первое своё задание "все нули", то есть, дожидаться синхроимпульсов не надо, нужно работать до X-координаты ноль. Но условие завершения оказалось выполненным прямо на этом такте, поэтому видеопроцессор сразу запросил себе следующую команду (rdreq = 1). А так как ни одной команды внутри на деле не лежит, мы и получили UFLO - Underflow.

Это бы и ладно, пущай будет. Но дальше ещё интереснее: к следующему такту счётчик X-координаты двинулся до единицы, поэтому теперь видеопроцессор наивно полагает, что его задание "посчитать до X=0" пока ещё не выполнено, следовательно, буфер он не теребит (rdreq = 0) и UFLO исчезло. Почему бы и нет...

Тем временем смотрим, что нам выдал "первый сумматор" на свой 22-битный выход Sum. А выдал он 0x000FFF. Читается оно так: младшие 12 бит - это максимальная яркость, которую он смог обнаружить, но инвертированная. Действительно, пока яркость была нулевая, инверсия и дала FFF. Старшие биты - это координата, где эта яркость была достигнута, то бишь ноль. Логично.

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

Вот мы видим, как приходит первый строчный синхроимпульс:

К тому времени счётчик X успел сосчитать лишь до 0x200 = 512, то есть далеко не до конца (он 10-битный, может до 1023), но по приходу синхроимпульса он обнулился.

Редкий момент: пришёл наконец-то КАДРОВЫЙ синхроимпульс:


И ничего не произошло, хотя по логике вещей мы ждали именно его.


Похоже, модуль управления видеопроцессором содержит ошибку, что немудрено - я ему "юнит-тест" так и не устроил в своё время, поленился. Что ж, самое время это сделать.

Зато работой QuatCore с новым модулем непосредственных значений я очень доволен :) Кстати, для данных констант модуль отсинтезировался всего в 4 ЛЭ. Это вам не 56. И есть что-то завораживающее в том, как в результате всех преобразований и отсечек мы получаем ровно те значения, что хотели!
Tags: ПЛИС, программки, работа, странные девайсы
Subscribe

Recent Posts from This Journal

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

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

  • Гетто-байк

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

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

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

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments