Возможны две "исключительные ситуации". Первая: QuatCore не успел вовремя "озадачить" видеопроцессор - и тот не знает, что ему делать с УЖЕ ПОСТУПАЮЩИМ сигналом. Остановить его нельзя, и даже если я всё-таки заполучу в свои загребущие лапы отечественную фотоприёмную матрицу 1205ХВ014, её остановить тоже будет нельзя. Мы могли бы, конечно, остановить подачу тактовой частоты, но в документации про такое ничего не пишется - могут полезть очень неприятные вещи. У нас эта ситуация названа Underflow, или UFLO, то бишь "исчерпание".
Вторая ситуация: QuatCore не успевает обработать результаты работы видеопроцессора - и те накапливаются в выходном буфере, пока не заполнят его до предела. В итоге новым данным поступать некуда - и они пропадают. Опять же, мы не можем приказать фотоприёмной матрице (или аналоговой камере) "горшочек, не вари!". У нас эта ситуация названа Overflow, или OFLO, то бишь "переполнение".
И с тем, и с другим я уже сталкивался на симуляции, когда жадничал с размерами входного и выходного буферов, ну скорее не сразу понял, какого размера они должны быть.
Но разумеется, на реальном изображении могу столкнуться ещё не раз - и надо будет знать, что происходит, хотя бы какой из буферов нам нужно нарастить!
Можно было с этой задачей малость повременить, для начала бы экспозицию настроить и попробовать "с нахрапу" запустить обнаружение пятен. Но надо как можно быстрее научить компилятор составлять вектор прерываний внутри QuatCoreCallTable.v. Сейчас-то я "ручками" вдолбил адрес метки NMI, для проверки, но пора это дело автоматизировать. И лучше сразу сделать правильно, чем поставить костыль на 1 прерывание и потом мучительно его переделывать, когда их станет побольше!
Начнём традиционно с "железа".
Оставим вход NMI (Non-Maskable Interrupt), только теперь разные прерывания надо будет объединять по "ИЛИ". Но чтобы узнать, какое именно прерывание случилось, сделаем шифратор, который сигнал по "нулевому" входу преобразит в код "00", по "первому" - в "01", по "второму" - в "10".
Всё это запихаем в один модуль, причём не будем пытаться сделать его слишком уж "общим" (параметризованным). Я хочу, чтобы QuatCore подстраивался под конкретную задачу, и эти прерывания - это одна из наиболее специфических вещей. Конкретный код куда проще для понимания, так что имена дам понятные, а не просто NMI0, NMI1, NMI2:
module QuatCoreCVinterruptEncoder (input OFLO, input UFLO, input WDT, output StallEn, output NMI, output [1:0] intN); assign NMI = OFLO | UFLO | WDT; assign StallEn = ~NMI; assign intN = WDT? 2'b00: OFLO? 2'b01: 2'b10; endmodule
Как водится, чем мельче модуль - тем мудрёнее его имя! Здесь оно означает: "шифратор прерываний для технического зрения (Computer Vision, CV) на основе QuatCore".
И впихивается оно как-то так, между видеопроцессором и QuatCore:

Но, как видно, "номер прерывания" (intN) пока привести некуда, нет подходящего входа у QuatCore. Пора его добавить, сначала в QuatCore, затем уровнем ниже, в QuatCorePC (program counter), и, наконец, в QuatCoreCallTable.
Вот в QuatCore можно ввести параметризацию на количество бит, требующихся для "номера прерывания". Сейчас нам хватает 2 бит, но аппетит приходит во время еды, может ещё что-нибудь захотеться.
Так теперь выглядит QuatCorePC:

В кои-то веки удалил ненужный параметр RelAddrWidth (сколько бит занимает "относительный адрес" - нет их у нас больше!), вместо него всунул nmiWidth. Не самое удачное название, но пока так.
Уровнем выше, в QuatCore, ничего интересного, просто ещё один проводок:

Ну и параметр nmiWidth объявили и здесь, чтобы не приходилось каждый раз лезть "в потроха".
Наконец, теперь и на уровне QuatCore+GPU+периферия также появился нужный вход, так что можно завершить схему...

При этом ушло два вывода на "самом верхнем уровне", тех самых Underflow и Overflow, которые я первоначально планировал вывести через регистры-защёлки на какие-нибудь светодиодики :) Как результат, все выводы на схеме "поехали" - ковырялся возвращал их на место. Но это и хорошо: штука потихоньку приобретает законченный вид, избавляясь от "отладочных" выводов.
Синтезируется оно успешно, даже каким-то чудом получилось на 1 ЛЭ меньше, чем в прошлый раз, Fitter "живёт своей жизнью".
Далее, допишем программу. Не буду сейчас приводить её всю (она есть в предыдущем посте), только кусок, посвящённый обработке прерываний:
;Обработка немаскируемых прерываний (а других пока и нет :)) .data CommonError dw 0x101,'Ошибка: ',0 NoVideoSignal db 'нет сигнала',0 UnderflowInt db 'исчерпание ','заданий GPU',0 OverflowInt db 'переполнение','результатов GPU',0 .code GPU_WDT: Y NoVideoSignal JMP IntHandler GPU_UFLO: Y UnderflowInt JMP IntHandler GPU_OFLO: Y OverflowInt IntHandler: SIO LCD X CommonError CALL print X Y CALL print @@endless: JMP @@endless
Жадность традиционно зашкаливает: первая посылка, поступающая на ЖК-экранчик, хранится по 16-битным словам, чтобы можно было вместе с обычными символами иметь команду "очистка экрана". А уже сообщения, специфичные для каждой ошибки хранятся по байтам.
В будущем может захотеться ещё и получить "дамп памяти" - уже картинка, приведшая к ошибки - уже интересно. Но не всё сразу.
"Железо" почти готово. В последний момент сообразил, что OFLO и UFLO включаются "надолго", не на один такт, поэтому надо будет добавить формирование одиночного импульса, а вот "сторожевого пса" можно будет слегка упростить. Но даже тогда я ожидаю, что UFLO "зажжётся" СРАЗУ ЖЕ, потому как при включении на видеопроцессоре нет никаких заданий. Нужен и здесь дополнительный регистр, чтобы "ловить" именно прорехи во время обработки кадра. Как только кадр закончился, а новый мы не заказали - можно и отдохнуть с чистой совестью.
Программа готова.
Остался один маленький пустячок - компилятор. Но это уже завтра...