Запускаю компиляцию проекта в квартусе, Analysis&Synthesis проходит нормально, запускается Fitter (Place&Route), доходит до 96% - и натурально крашится:
*** Fatal Error: Access Violation at 0X08DB3606 Module: quartus_fit.exe Stack Trace: 0x1D3606 : VPR10K20K_QI_FACADE::vpr_qi_main + 0x51B76 (fitter_vpr10k20kmain) 0x1D35CA : VPR10K20K_QI_FACADE::vpr_qi_main + 0x51B3A (fitter_vpr10k20kmain) End-trace
причём делает это на двух разных компьютерах - один домашний (Intel Quad Core Q8200, Win7 x64), другой на работе (Intel Atom D510, WinXP 32 бита).
Начальное содержимое памяти вообще на это дело не должен оказывать влияния - по-моему он недостаточно умён, чтобы "прочухать" - "нам тут по старшему биту положили сплошные нули во всех адресах, а модуль сделан как ПЗУ, без линий записи, значит, можно половину логики выкинуть, она никогда не включится". По крайней мере, не замечал, чтобы простейшая программа, явно не использующая и половины команд, приводила к уменьшению количества ЛЭ.
Значит, единственное, что поменялось - QuatCoreCallTable.
Встречался с таким поведением и раньше - адреса в этой таблице чуточку сменились - и ранее синтезировавшийся проект перестал это делать - Could not find fit - пока я не выкинул отладочный выход MemContents. Можно заметить - здесь он есть нижней строкой, а здесь - уже нет. Когда вызываемых функций стало чуть больше - внезапно оно перестало влезать.
Такое поведение меня не сильно удивляло, я и в прошлых "опытах" видел, что стоит попытаться подключить все 32 бита результата - и Place&Route не может "развести дорожки", всё-таки в стареньких ПЛИС и межсоединения не очень мощные.
Но там он хотя бы честно признавался, что не может развести, даже при желании можно было посмотреть, какие соединения он уже сделал, и где возник затык. (можно ли попытаться после этого "вручную" потыркаться - не пробовал ещё)
А тут просто "аварийное завершение" - и делай что хошь.
Вот он, ВЕЛИКИЙ И УЖАСНЫЙ МОДУЛЬ, переломивший хребет квартусу:
//адреса для вызова процедур, "вшитые" в модуль QuatCorePC module QuatCoreCallTable (input [7:0] SrcAddr, output [7:0] addr); wire [1:0] index = SrcAddr[1:0]; assign addr = (index==0)? 8'd8: //AffineAlgorithm (index==1)? 8'd140: //NormSiCo (index==2)? 8'd132: //ShiftOrigin (index==3)? 8'd125: //SwapPoints 8'bxxxxxxxx; endmodule
Мне подумалось - может он очень обижается, что мы на полном серьёзе ожидаем, что 2-битное число index может принять какое-то значение, отличное от 0,1,2,3 - и напрочь потеряв веру в человечество убивает себя об стену? Чуть поменял эту штуку:
module QuatCoreCallTable (input [7:0] SrcAddr, output [7:0] addr); wire [1:0] index = SrcAddr[1:0]; assign addr = (index==0)? 8'd8: //AffineAlgorithm (index==1)? 8'd140: //NormSiCo (index==2)? 8'd132: //ShiftOrigin 8'd125; //SwapPoints endmodule
Как ни странно, количество ЛЭ не изменилось, и опять возник Fatal Error на 96% выполнения Place&Route, но уже другой:
*** Fatal Error: Access Violation at 0X052BD8E0 Module: quartus_fit.exe Stack Trace: 0x1D8E0 : TDC_ACCESSORIES_PATH_TIMING::update_path_requirements + 0x30 (TSM_TDC) 0x37CF5 : clock + 0x41 (MSVCR90) 0x19B089 : VPR10K20K_QI_FACADE::vpr_qi_main + 0x195F9 (fitter_vpr10k20kmain) 0x98D2A : VPR10K20K_QI_FACADE::operator= + 0x97CDA (fitter_vpr10k20kmain) 0x1B90B1 : VPR10K20K_QI_FACADE::vpr_qi_main + 0x37621 (fitter_vpr10k20kmain) End-trace
Хотя не уверен, что другая ошибка вызвана внесёнными изменениями - по-моему они просто чередуются, что само по себе странно, процесс, казалось бы, должен быть детерминированным!
А ведь модуль очень простой. Мы лишь хотим получить такие двоичные выходы в зависимости от 2-битного входного адреса:
0: 0000_1000 1: 1000_1100 2: 1000_0100 3: 0111_1101
Видно, что на первом разряде (самый младший мы называем нулевым, и далее до седьмого) вообще всегда ноль должен быть, а нулевой, 4-й, 5-й и 6-й совпадают! Именно на их формирование идёт один ЛЭ, и ещё по одному на второй, третий и седьмой, вот они четыре.
И вот наверное эта подача одинакового сигнала на разные дальнейшие элементы каким-то образом убивает Place&Route. По крайней мере, общая компоновка на удивление чувствительна к одному этому вроде бы безобидному модулю!
В данный момент, мы могли бы обойтись без ЛЭ вообще - ведь у нас выделено 16 адресов SrcAddr для вызова функций, т.е младшие 4 бита как раз задают один из 16. А у нас тут как раз всего 4 "нетривиальных" выхода, так что можно было бы соединить их напрямую:
module QuatCoreCallTable (input [7:0] SrcAddr, output [7:0] addr); assign addr[0] = SrcAddr[0]; assign addr[1] = 1'b0; assign addr[2] = SrcAddr[1]; assign addr[3] = SrcAddr[2]; assign addr[4] = SrcAddr[0]; assign addr[5] = SrcAddr[0]; assign addr[6] = SrcAddr[0]; assign addr[7] = SrcAddr[3]; endmodule
Такая схема синтезируется в 0 ЛЭ, что логично. А весь процессор в сборе - в кои-то веки синтезируется, без Fatal Error'ов, в 435 ЛЭ.
Правда, программа не заработает ни разу, потому что в коде прописаны адреса 0..3 для вызова процедур, а их нужно заменить на 0x4, 0xE, 0xA и 0x7, соответственно.
Но в целом забавно - у меня всё роятся разные идеи, как автоматизировать очень "экономную" генерацию этих адресов, учитывая, что их можно переставлять как угодно, но я их старательно задвигал - "ну сэкономишь сейчас 4 ЛЭ, это вообще ни о чём, даже в масштабах 5576ХС6Т, там их 2880, лучше за видеообработку берись!". А вот как будто бы всё-таки надо - почему-то когда этот модуль генерируется автоматически, "в лоб" - потом не работает ни разу!