nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

Генератор тестовых сигналов для QuatCore+GPU

31 июля я "нарисовал" эту связку QuatCore+GPU "в сборе", 3 августа откомпилировал программу нахождения ярких точек, потом неделю, до 10 августа, "отдавал должок", делал генерацию хорошей, компактной таблицы непосредственных значений QuatCoreImm. На неделю отвлекли на всякую хрень, и продолжают отвлекать на перекладывание бумажек, как будто эти бумажки перекладывать больше некому, но сейчас хоть на день-другой надо вернуться к своим баранам...

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


Вариантов исполнения здесь множество. Можно было бы автоматически сгенерировать Vector Waveform File (.vwf), он вполне себе текстовый, но не самый приятный. Там сначала идут основные параметры, потом описание каждого сигнала (его разрядность, количество состояний каждого провода, вход это или выход или двунаправленный), затем описание всех переходов (переключений), которые этот сигнал претерпевает, а потом ещё и настройки отображения всего этого добра на экране.

Другой вариант - взяться за бихевиориальные модели на верилоге :) То бишь, не ограничиваться синтезируемым подмножеством, а запихать туда и всякие #10 и всякие циклы, которые исполняются именно во время симуляции. Это целая наука, специально обученные люди этим занимаются, собственно, именно отсюда есть и пошёл верилог в своё время (от слова verify, то бишь проверка правильности). По идее, на самом верилоге можно довольно сложные штуки делать - загружать файл с изображением, прочитывать его и по его содержимому формировать все импульсы для симуляции, но ровно сейчас в это дело погружаться ой как не хочется. В особенности учитывая, что Квартус наверняка поддерживает далеко не все директивы, поэтому нужно не только учить сам язык, но ещё бодаться с квартусом, и убеждаться, что мы с ним друг друга понимаем...

А потому сделаю вполне себе синтезируемый генератор тестовых сигналов, почему бы и нет! ПЗУ с картинкой 32х32 у меня уже есть, осталось только к этому делу добавить синхроимпульсы (строчные и кадровые) и соответствующие паузы в выдаче сигнала.

Нужно ввести параметры:
- HorWidth - количество бит счётчика по горизонтали, сейчас будет 5 (т.е 32 пикселя),
- VertWidth - количество бит счётчика по вертикали, сейчас также будет 5, т.е "полезная область" 32х32. Так у нас есть шансы, что симуляция всё-таки завершится в разумный срок, а потом мы сможем её рассмотреть от начала до конца. Именно такая ситуация, когда все точки сосредоточены на "маленьком пятачке", кажутся мне наиболее сложными, так как времени на "подумать" почти не остаётся.
- FrontPorch - количество тактов между окончанием строки и выдачей строчного синхроимпульса. Сейчас поставлю 12 тактов, в соответствие с сигналом CVI см. аналоговые сигналы высокой чёткости.
- BackPorch - количество тактов между строчным синхроимпульсом и началом строки. Сейчас поставлю 237 тактов, тоже по CVI. Очень странная получается строка: 32 такта длится полезная её часть, и ещё 249 тактов - "обратный ход", но пускай пока так будет, чтобы было хоть какое-то время на обработку, особенно поначалу, когда прочитывается вся строка целиком, и за время обратного хода нужно сообразить, что делать на следующей строке. Тут я не пытаюсь делать широкий синхроимпульс, пусть он будет длительностью в 1 такт.
- TopRows - количество "пустых" строк сверху, после подачи кадрового синхроимпульса, у меня пусть будет 10
- BottomRows - количество "пустых" строк снизу, после окончания наших 32 полезных строк. Пусть будет 20.

А вот шибко напрягаться с реализацией совсем не хочется, это исключительно тестовая штука, и если она не будет реализована минимально возможным числом ЛЭ - я шибко не расстроюсь. Поэтому вместо составления конечного автомата, переключающегося между 3-4 режимами по строкам, со счётчиком, который отсчитывает разные интервалы в зависимости от текущего режима, и пр., попробуем просто "нарисовать" схему из некоторого количества моих стандартных компонентов. Один из них: FastFreqDivider - быстрый делитель частоты. Подробно о нём здесь. С помощи него можно получить строчные синхроимпульсы, а затем и кадровые синхроимпульсы:


По счастью, в редакторе "схем" можно в качестве параметра указать сумму нескольких параметров. Поначалу я туда попытался всунуть и вовсе (1 << HorWidth), но на это получил ругань. Поэтому не стал заморачиваться пока и ввёл отдельно параметры HorPixels и VertPixels, тем более что это позволит задать размеры более произвольно, например 1280х720.

На всякий случай сразу запустил на симуляцию, посмотреть, действительно ли эта сумма правильно сработала (мало ли, он сделал конкатенацию строк, когда 10+20 = 1020). Как будто бы работает, но сдвоенный импульс VSync несколько напрягает:


При увеличении видно, что один из импульсов - это лишь очень короткий комбинаторный "пичок":


Жить можно, а задолбает - поставим регистр-защёлку на выходе, кадровый синхроимпульс задержать на один такт вообще не проблема.

Ещё одна особенность работы FastFreqDivider - первый интервал получается удлинённым. В нашем случае, счётчик, выдающий строчные синхроимпульсы, вместо 281 такта отсчитал 512 тактов, и только потом "вышел на режим". Та же история с тактовыми синхроимпульсами, поначалу вместо 62 строк он сосчитал 64, а то и 65. Плевать! Всё равно, пока самый первый кадровый синхроимпульс не придёт - видеопроцессор свою работу не начнёт, а за первым синхроимпульсом уже всё верно.

По-моему, ещё ни разу не делал симуляцию до 2 миллисекунд при тактовой частоте 25 МГц. Но ничего так, пока довольно быстро справились

Дальше воспользуемся ещё одним модулем, который я написал больше года назад, называется DelayLine, хотя название может несколько сбить с толку. Я себе так представлял: входной сигнал, везде нули с одинокими единичками строчных синхроимпульсов, а на выходе - всё то же самое, но задержанное на указанное количество тактов. Но ровно так оно работает только если между входными импульсами проходит время, бОльшее времени задержки, то есть на самом деле это "ждущий мультивибратор" - его пнули, он подумал-подумал - и выплюнул импульс на выход. Собственно, так оно и устроено:

//when start=1 arrives, we count 'Duration' ce impulses, then finished = 1 for 1 pulse
//and we wait new impulse.
//if next start=1 arrives before we counted previous one, it resets and counts from beginning
`include "math.v"

module DelayLine (input clk, input start, input ce, input sclr, output reg finished = 1'b0, output reg working = 1'b0);

parameter Duration = 456;

localparam Width = `CLOG2(Duration);
localparam MaxInt = 1 << Width;
localparam InitVal = MaxInt - Duration; //

wire [Width - 1 : 0] Count;
wire TC;	//Terminal Count

lpm_counter	lpm_counter_component (
				.clock (clk),
				.cnt_en (working & ce),
				.sset (start | sclr),
				.cout (TC));
	defparam
		lpm_counter_component.lpm_direction = "UP",
		lpm_counter_component.lpm_port_updown = "PORT_UNUSED",
		lpm_counter_component.lpm_type = "LPM_COUNTER",
		lpm_counter_component.lpm_width = Width,
		lpm_counter_component.lpm_svalue = InitVal;

always @(posedge clk) begin
	working <= start? 1'b1 : (ce&TC | sclr)? 1'b0 : working;
	if (ce | sclr)
		finished <= sclr? 1'b0 : TC;
end


endmodule


Довольно полезная штука. Можем одну такую поставить для строк: пусть после подачи синхроимпульса выжидает BackPorch тактов, и только после этого у нас начнёт работать счётчик пикселей по горизонтали. Вторую поставим для кадров: пусть отсчитывает TopRows строк, и только после этого начинает работать счётчик "видимых" строк.

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

В результате получаем такую вот схему:


Верхний ряд модулей отвечает за строку, нижний ряд - за кадр.

Начнём с верхнего. DelayLine никогда не сбрасывается (вход sclr "заземлён"), её работа всегда разрешена (на вход ce подаётся "vcc", то бишь логическая единица), запускается эта "линия задержки" по приходу строчного синхроимпульса. Как только она начинает своё ожидание, появляется единица на выходе working, которая непрерывно сбрасывает счётчик пикселей. Когда интервал BackPorch закончится, working сбрасывается в ноль, и счётчик начинает работать - прибавлять единицу каждый такт, пока не досчитает до упора. В этот момент появляется TC=1 (Terminal Count), который пройдя через инвертор, запрещает счёт, поэтому он так и остаётся в максимальном положении.

Нижний ряд чуть хитрее. DelayLine снова никогда не сбрасывается, но считает она только при наличии строчного синхроимпульса (вход ce присоединён именно к нему), поэтому задержка измеряется не в тактах, а в строках. Запускается задержка по кадровому синхроимпульсу, и своим выходом working заставляет обнуляться счётчик строк, пока задержка наконец не закончится. Счёт разрешается только во время прихода строчных синхроимпульсов, и только если мы не досчитали до упора (TC=1).

Остаётся собрать 5 бит одного счётчика и 5 бит другого - и сформировать из них адрес для ПЗУ, где хранится наше изображение 32х32. К счастью, если провод, поступающий на адрес ПЗУ, обозвать Y[4..0],X[4..0] - это и будет объединение двух шин в одну, притом 5 бит Y будут старшими, а 5 бит X - младшими.

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

Синтезируется эта штука успешно, в 58 ЛЭ (а не так уж и плохо), с предельной частотой 97 МГц - красота, да и только. Запустим симуляцию:


Ещё ни один синхроимпульс не сформировался (до этого далеко), а счётчик пикселей побежал вперёд. Дошёл до 31=0x1F - и остановился. Тем временем счётчик строк стоит как вкопанный, а пиксели все сплошь нулевые, такова уж наша картинка.

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


Счётчик строк прибавляет единицу, а счётчик пикселей спустя такт сбрасывается в ноль, после чего идёт очень длительное затишье. Засечём: синхроимпульс пришёл при T=20,48 мкс. Посмотрим, а где же началась "движуха":


Началась она при T=30,04 мкс, то есть спустя 239 тактов. Точнее, переключение с нуля на единицу произошло через 239 тактов после синхроимпульса, но последний ноль был вполне себе корректным - именно это начало строки. Нормально. И как и прежде, счёт идёт от 0 до 31, застреваем на 31, но уже совсем скоро приходит новый синхроимпульс, который прибавляет единицу к номеру строки, а ещё спустя такт обнуляет счётчик пикселей. Строки пока что полностью чёрные.

Что-нибудь отдалённо интересное начинается на строке с индексом 4 (то бишь, на пятой):


Здесь мы видим два "пятна", одно длиной 4 пикселя (координаты 0x0F..0x12 = 15..18) и второе длиной 2 пикселя (координаты 0x17..0x18 = 23..24). Если посмотреть наши заметки, там мы утверждали, что самый яркий пиксель должен иметь X-координату 14. Всё верно, мы внесли задержку в 2 такта в наше ПЗУ: сначала "защёлкивается" адрес, а потом ещё и результат, поэтому на самом деле координаты наших пятен 13..16 и 21..23.

Пусть так и будет - настроим работу, подобрав правильный BackPorch, притом уже в программе нахождения точек.

Наконец, глянем, что происходит по кадровому синхроимпульсу. Для начала, с высоты птичьего полёта:


Видно, как счётчик строк доползает до 31 = 0x1F, и застревает там до прихода кадрового синхроимпульса, после чего сбрасывается в ноль, некоторое время стоит в нуле - и снова начинает ползти. Если точнее, спустя 10 строчных импульсов счётчик строк остаётся в нуле, а на 11-м строчном импульсе переключается в единицу. Именно это мы и ожидали.

Убедимся, что всё выглядит прилично "вблизи":


Всё нормально.

Пожалуй, нас это устраивает. Осталось собрать схему для тестирования:
SyncGen.png

Она синтезируется в 1006 ЛЭ (впервые я преодолел "психологическую отметку" в 1000 ЛЭ) и 20 480 бит внутренней памяти (20% от того, что есть у меня на 5576ХС4Т, и 50% от того, что будет на 5576ХС6Т). Предельная частота 24,27 МГц - чуть-чуть не дотянули до своих 25 МГц. Так и быть, симуляцию буду запускать на 24 МГц.


Теперь отступать совсем некуда. Завтра ещё переправлю константы в программе обнаружения точек, с 1280х720 на наши тестовые 32х40 (40 вместо 32, чтобы проверить, как видеопроцессор "щёлкает" строки "заходящие за поля" за 1 такт, выдавая нули, что необходимо для корректной работы нашего алгоритма), выставлю величину BackPorch, откомпилю повторно, отсинтезирую повторно - и после этого начнётся ВЕЛИКАЯ И УЖАСНАЯ ОТЛАДКА ВСЕГО! Если оно хотя бы через 2 недели заработает корректно - я уже буду счастлив.

PS. Фрэнк, ты КОЗЁЛ! Твой новый редактор - редкостное говно! И очень трогательно, что если сначала открылась вкладка с новым редактором, потом по нажатию "вернуться к старому редактору" открывается новая вкладка со старым, и там ты жмёшь "опубликовать" - вместо публикации меня выкидывает на пустые поля нового редактора, а всё что я писал - уходит в небытие. По счастью, все козлиные повадки я знаю и сохраняю то же самое в "блокнот". Одно плохо, забыл отключить "перенос по словам". Исправляюсь...
Tags: ПЛИС, программки, работа, странные девайсы
Subscribe

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

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

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

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

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

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

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments