nabbla (nabbla1) wrote,
nabbla
nabbla1

Category:

Не было печали...

когда уже почти всё заработало, захотелось всё сделать совсем по-другому, ГОРАЗДО ПРОЩЕ. Поначалу этот подход казался мне очень расточительным, но может это лишь показалось!

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

Тут есть хитрость: размер пятен мы должны знать заранее, в отличие от нашего алгоритма, который и размер находил "заодно". Хотя нам вполне позволительно на этапе обнаружения опробовать разные размеры, пока, наконец, не обнаружим что надо.

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

И "разделение пятен" произойдёт фактически автоматом: мы будем искать максимумы на отрезках, примерно равных ожидаемому диаметру пятен, и тут уж заведомо двух рядом не будет.

Вопрос лишь в одном: насколько реально сделать двумерный фильтр с радиусом 50..60 пикселей на ПЛИС без аппаратных умножителей, малой скоростью и памятью в 12 килобайт?

Если здесь применять классическую свёртку - ни разу не влезет, ведь нам нужно сохранять в памяти под сотню строк, а это 100 килобайт сразу (а в "лётной" матрице и вовсе АЦП 12 бит,так что 150 килобайт), да и сотни сложений на одном такте реализовать не так-то просто...

Но вообще-то, если взять старые добрые рекурсивные фильтры (БИХ), они здесь сработают вообще в полпинка!

IMG20210324160509.jpg


Фильтр по строке у нас по сути уже есть, мы применяли его для устойчивой работы детектора синхроимпульсов, см. ФНЧ БИХ на ЭРИ ОП. Ему радиус надо выставлять в степенях двойки. Давайте попробуем level = 6, т.е "радиус 64". Просто пропустим сигнал через этот фильтр перед входом в видеопроцессор:


Попытался получить картинку - а она самая обычная! Тут-то я и вспомнил, что в статическую память изображение идёт вообще напрямую из АЦП, минуя ПЛИС. ПЛИС только адреса инкрементирует и даёт разрешение на запись.

Пожалуй, надо взять и перепаять эти 8 выводов :) Смогу ещё "нахаляву" один вариант осуществить: записать "тёмный кадр" в память (кадр, где выключена ИК подсветка), а потом при получении "светлого кадра" сразу же, на лету, вычитать значение из памяти, таким образом убирая блики и прочую паразитную засветку. Сказано-сделано. Всё разобрал, см "фото для привлечения внимания" в начале поста.

Слева плата оцифровки и питания, через неё же подсоединяются ЖК экранчик, SD-карточка, сюда же может втыкаться цифровая камера OmniVision, но ещё ни разу не пробовал её использовать. И ещё начал было делать самопальный приёмопередатчик МКО (потому как готовый хрен купишь как физлицо, и даже его компоненты, как-то трансформатор ТИЛ6В, микросхемки 5559ИН13У либо 5559ИН67Т), но обстоятельства поменялись, приоритетной задачей стало проверить всё остальное (а с МКО, он же Mil-Std 1553 уж как-нибудь управимся), так что эта часть ещё ждёт своего часу...

Сверху - плата статической памяти аж на 1 МБайт. Ну а в центре композиции - отладочная плата с ПЛИС 5576ХС4Т1, конфигуратором для неё аж в металлокерамике, и кучей всяких "пряников" вроде Ethernet-контроллера ENC624J600, приёмопередатчика RS485, 12-битной АЦП на 4 канала и 500 килосэмплов в секунду, преобразователя напряжения из 5 вольт в 3,3 и затем LDOшки из 3,3 в 1,8 вольта, а ещё 8 светодиодиков и 5 кнопок, которые сейчас, когда эта плата оказалась посередине "этажерки", да ещё внутри железного корпуса, стали резко не нужны.

Прежде чем перепаивать, надо было провести "инвентаризацию выводов" - вся эта конструкция соединяется сквозными цанговыми разъёмами, 4 штуки по 20 выводов, а почти все они уже заняты! По счастью, кое-какая документация у меня была, оставалось только слегка обновить её. В общем, вот просто обозначения выводов:



Те выводы, где стоит прочерк - "зарезервированы" разработчиками отладочной платы под другие цели. Например, контакты 13-20 - для подключения гнезда Ethernet (RJ-45): два светодиодика, трансформаторы передатчика и приёмника с отводом от центра. Собственно, и +3,3 вольта именно туда предназначались, но я их "стырил" :)

Контакты 61-64 - для подключения USB, но на эту отладочную плату они вообще не заходят. Контакты 79-80 - "самопальный" ЦАП в виде пассивного ФНЧ (4 подряд идущие RC-цепочки 1 кОм - 10 нФ и ферритовая бусина впридачу). Контакт 59 - VBat. Опять же, на эту отладочную плату он не подключён. Видимо, это к каким-нибудь микроконтроллерам, у которых есть отдельная фича "батарейного питания" для часов реального времени и хранения данных. Но это не наш случай.

А все универсальные I/O мы-таки забили! Больше всего (30 штук) досталось именно статической памяти (префикс SRAM), ещё 10 - на быстродействующую АЦП, которой мы оцифровываем видеосигнал. 6 штук - для подключения ЖК экранчика, 3 - для SD-карточки.

4 входа шли от селектора синхроимпульсов LM1881: Odd/Even (чёт-нечет), Burst (вспышка PAL), VSync (кадровый синхроимпульс) и Comp (композитный). Немножко наигравшись с этим селектором, я его отключил, теперь нахожу синхроимпульсы уже внутри ПЛИС, по оцифрованному сигналу. Но выпаивать пока не стал, мало ли захочется АЦП "подогнать" аккурат под диапазон от чёрного до белого, и тогда микросхемка снова станет нужна. Сейчас отпаял Odd/Even (на сигнале 720p он не нужен, ибо progressive, там нету чётных-нечётных полукадров) и на его место сунул SRAM_D0 - ну не хватало уже пинов!

И ещё 3 ножки "сами по себе": одна управляет подсветкой ЖК-экранчика через транзистор КТ608 :) Хотя сейчас он используется в полсилы, изначально там 2 пары диодов были соединены параллельно, давая ток 120 мА, а я их пересоединил последовательно, и запитал от 12 вольт, так что теперь ток лишь 60 мА.

Вторая - ИК-подсветкой камеры, причём по совместительству позволяет определить низкую освещённость в комнате, см. да будет свет.

И третья - управление экранным меню камеры (OSD), вместо "джойстика" на проводе.

В общем, как только определились, к каким ножкам теперь тянуть шину данных статической памяти (раньше они шли на те же 8 выводов, что и выход АЦП), было дело техники их перепаять.

После этого требовалось переделать проект на ПЛИС, в первую очередь модуль QuatCoreFastSRAM. Если раньше при работе с АЦП ему достаточно было переключать адреса и управлять входами разрешения, то теперь и данные должны через него проходить напрямую! Получилось как-то так:

module QuatCoreFastSRAM (input clk, input [7:0] DestAddr, input [7:0] SrcAddr, input DestStall, input SrcStall, input SrcDiscard, input [15:0] D,
			input ReadEnable, input WriteFromADC, input [7:0] GPU,
			output [18:0] RAMaddr, output RAM_CE0, output RAM_CE1, output RAM_RW,
			inout [7:0] RAM_data);
						
wire IsOurDest = (~DestStall)&(~DestAddr[7])&DestAddr[6]&(~DestAddr[5]);
wire IsOurSrc = (~SrcStall)&(~SrcDiscard)&(SrcAddr[7:3] == 5'b1001_1)&ReadEnable;

wire LoadERL = IsOurDest & (~DestAddr[4]) & (~DestAddr[3]);
wire LoadERH = IsOurDest & (~DestAddr[4]) & DestAddr[3];
wire WriteMem = IsOurDest & DestAddr[4];

wire DoWriteFromADC = WriteFromADC & (~ReadEnable);

wire DoIncrement = WriteMem | IsOurSrc | DoWriteFromADC ;

wire [19:0] ER; //External memory Register
wire TC; //Terminal Count

lpm_counter ERL (.clock (clk),
		.cnt_en (DoIncrement),
		.sload (LoadERL),
		.data (D),
		.Q (ER[15:0]),
		.cout (TC));
defparam
	ERL.lpm_direction = "UP",
	ERL.lpm_port_updown = "PORT_UNUSED",
	ERL.lpm_type = "LPM_COUNTER",
	ERL.lpm_width = 16;
	
lpm_counter ERH (.clock (clk),
		.cnt_en (TC & DoIncrement),
		.sload (LoadERH),
		.data (D[3:0]),
		.Q (ER[19:16]));
defparam
	ERH.lpm_direction = "UP",
	ERH.lpm_port_updown = "PORT_UNUSED",
	ERH.lpm_type = "LPM_COUNTER",
	ERH.lpm_width = 4;
	
assign RAMaddr = ER[19:1];
assign RAM_CE0 = ~(ER[0]&(ReadEnable | WriteMem | DoWriteFromADC));
assign RAM_CE1 = ~(~ER[0]&(ReadEnable | WriteMem | DoWriteFromADC));
assign RAM_RW = ((~WriteMem) & (~DoWriteFromADC)) | clk;

assign RAM_data = ReadEnable? (WriteMem? D[7:0] : 8'bzzzz_zzzz) : GPU[7:0];
//(WriteMem & ReadEnable)? D[7:0] : 8'bzzzz_zzzz;

endmodule


Закомментированная строка внизу - единственное, что изменилось (актуальная версия - строкой выше). Ну и дополнительный вход GPU, раньше нужды в нём не было.

ReadEnable идёт с выхода видеопроцессора, он равен единице, когда АЦП отключён (тогда SRAM может заниматься своими делами) и нулю, когда он включается. WriteFromADC равен единице, когда началась "полезная часть видеосигнала", которая должна записываться в память с автоинкрементом.

Попробовал "с нахрапу" отсинтезировать всё это безобразие с программой ImageTransfer (позволяет отрегулировать яркость ИК подсветки, побегать по меню аналоговой камеры и получать изображение на компьютер). Сразу Critical Warning: Timing constraints not met, предельная частота 24,6 МГц. Я решил: должно заработать, всё-таки температура не предельная, напряжение питания стабильное (самый худший случай с точки зрения быстродействия - напряжение на нижней границе допустимого и максимальная температура, при которой сопротивления открытых переходов МОП-транзисторов увеличено), уж как-нибудь должно заработать!

Ну, сообщение на ЖК-экранчике появилось, и по UART было прислано "Начинаем работу". На регулирование ИК-подсветки откликался, а вот при попытке получить изображение выскочило прерывание "исчерпание заданий GPU" - раньше здесь такого не было!

Ладно, давайте хотя бы убедимся, что статическая память действительно работает, всё правильно спаяли и правильно отметили ножки. Загрузил программу HelloSRAM.asm. Сообщение "Привет лунатикам! (через СОЗУ)" появилось, но начало идти бесконечным циклом, одно за другим, как будто бы простейшая команда iLOOP перестала правильно работать!

Стоп! А ведь эти же команды используются в самом начале, чтобы передать сообщения по SPI на Ethernet-контроллер и настроить нам тактовую частоту 25 МГц. Раз мы по RS485 получаем данные, значит частота правильная! Хотя... при подаче питания сначала запускается конфигурация ПЛИС из Flash-конфигуратора, а только потом мы по JTAG прошиваем свою. И если та, старая добрая версия (ещё на старом АЛУ) настроит тактовую частоту, она никуда не сбросится, ведь при прошивке по JTAG питание не прерывается!

Прошил этот несчастный HelloSRAM в ПЗУ (конфигуратор), и оппаньки - по RS485 пошёл мусор... А выставив на компьютере скорость передачи вместо 921 600 бод 147456 бод (это 921 600 * 4 / 25), получил всё то же "Привет лунатикам! (через СОЗУ)", но опять же зацикленные, чего быть не должно было!

Ну блин, а хоть Hello, World у меня заработает?
IMG20210323214647.jpg

Очень символично: HELL. Мы всё уронили, разрушили до основания. Времени уже было 21:50, пора бы домой идти, несолоно хлебавши. Но тут я увидел, что мой макет подключён к зарядке. Так-то он питается от встроенного повербанка, там 3 литий-ионных аккума последовательно дают чуть больше 12 вольт, а заряжается это дело от 5 вольт, и заряжается очень нервно - то 2 ампера начинает жрать,а потом резко гасит до нуля, а через некоторое время - снова 2 ампера. А такие резкие скачки по питанию у нас непостижимым образом сводят с ума USB Blaster, см. USB Blaster ловит наводку
И прямо при мне, когда ток скакнул на 2 ампера, увидел, как моргнула подсветка ЖК - перезагрузился!

Подумал: может хотя бы этот HELL не доработал из-за наводки, ну повезло мне как всегда. Прошил повторно - сработало нормально. И то радость, Hello, World работает...

Пока шёл к метро (велик пока не на ходу, это отдельная история), осенило, почему HelloSRAM зациклился: там прерывание срабатывает, а поскольку оно в этой программе не прописано (она гораздо раньше писалась), то по умолчанию прыжок идёт на нулевой адрес, и всё повторяется. И установка тактовой частоты в Hello, World же работала, то есть iLoop и прочие команды не виноваты, я же их наблюдал, когда испытывал новую АЛУ на аффинном алгоритме!

Нет, здесь всё дело именно в прерываниях - чего-то в них нарушилось...

Сегодня начал "локализацию проблемы". Первым делом просто отключил прерывания, хотя проще всего это было сделать, добавив параметр в модуль QuatCoreCVinterruptEncoder:

module QuatCoreCVinterruptEncoder (input clk, input OFLO, input UFLO, input WDT, output StallEn, output reg NMI=1'b0, output reg [1:0] intN = 2'b00);

parameter EnableNMI = 1'b1;

wire wNMI = OFLO | UFLO | WDT;
reg rNMI = 1'b0;

always @(posedge clk) begin
	rNMI <= wNMI;
	NMI <= (wNMI & (~rNMI)) & EnableNMI;
	intN <= WDT? 	2'b00:
		OFLO?	2'b01:
			2'b10;
end

assign StallEn = ~NMI;

endmodule


Иначе пришлось бы "отрывать" провод NMI (Non-Maskable Interrupt, других у нас пока нет) и соединять на землю, а провод StallEn (разрешение остановки конвейера) - напротив, на "плюс питания". А так выставил NMIenable = 0 - и "радуешься".

Итак, запускаю - и получаю 32 одинаковых символа 0x0A. Это был последний символ в строке, Line Feed (LF). Что,память уже отвалилась???

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

Приготовился уже доставать осциллограф, но тут заметил: уж больно маленький размер "проекта", менее 1000 ЛЭ. Полез во вкладку Analysis&Synthesis --- Parameter settings by entity instance - и увидел там, что "в глубине" выставлены параметры enableSRAM = 0, enableIO = 0,хотя наверху Я УСТАНАВЛИВАЛ ИХ В ЕДИНИЦУ.

ВОТ ЖЕ СВОЛОЧЬ! Есть у Quartus'а какой-то глюк, когда он не всегда параметры "распространяет" правильно сверху вниз! Не знаю до сих пор, как это "правильно" побороть, в общем, спустился уровнем ниже, там тоже выставил "ручками", отсинтезировал ещё разок, теперь размер стал существенно больше - и наконец я получил долгожданное сообщение, притом ВСЕГО ОДИН РАЗ:

IMG20210324150853.jpg

(выше видны прошлые неудачные попытки, где вместо сообщения одни сплошные LineFeed)

Ну а теперь давайте снова попробуем картинку получить. Прерывания пока отключены, но если всё правильно, они и не нужны, их задача как раз локализовать ошибку. Прошиваемся, снова Critical Warning - Timing constraints not met, 24,81 МГц вместо 25. Хрен с ним...

ИК работает, жму "получить изображение" - зависает намертво.

Что ж, пора доставать осциллограф. Проверил наличие видеосигнала - на месте. Стал смотреть тактовую частоту, идущую на АЦП, она есть - и тут с ошибкой закрылся "терминал" Termite. Ага, это значит, что сейчас он попытался переварить 500 КБ символов - и не смог. То есть, КОГДА Я ТКНУЛСЯ ОСЦИЛЛОГРАФОМ - ВСЁ ЗАРАБОТАЛО! Запустил свою "программу рабочего места" - работает зараза...
IMG20210324155051.jpg

Эх, значит и этот глюк никуда не ушёл. Уж сколько он мне крови попортил, а по-прежнему здесь. ТАК ЖИТЬ НЕЛЬЗЯ, пора уже в обход этого несчастного разъёма впаять провод!
IMG20210324160408.jpg

Всё собрал назад, запускаю - и...
IMG20210324161511.jpg

Ну, пока работает, а там видно будет.

Ладно, почти вернулись на ранее занятые позиции, если бы не две фигни:
1. Что-то "нечисто" с прерываниями - почему он перепутал "нет сигнала" с "исчерпанием заданий GPU"?
2. Опять тайминги нарушились, оно и понятно - если в QuatCore источником данных выбрать SRAM в тот момент, когда работает АЦП, то сигналу придётся пройти довольно длинный комбинаторный путь, через мультиплексор, выбирающий между шиной данных и GPU, а потом через основной мультиплексор (MEM / ALU / IMM / PC / IO / SRAM / GPU) и наконец в регистр. Видимо, надо ввести тут дополнительную защёлку, в конце концов мы можем лишний такт подождать, прежде чем записывать данные в память...


Вот прямо все грабли собрал, что были.
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 

  • 5 comments