Интеграция IDE Code::Blocks с программным комплексом Altera Quartus II. Часть II.

4. Приближение третье. Ставим на поток.
Теперь у нас есть, простите за тавтологию, файл проекта среды C::B для нашего проекта. В принципе, из него мы можем довольно просто делать файлы для всех последующих проектов, но это требует некоторых лишних телодвижений — копировать, редактировать, переименовывать… А ещё ведь проект для квартуса новый создавать надо… В общем, лениво как-то этим заниматься, перевалим и это на плечи IDE.
Когда мы в C::B создаём новый проект через соответствующее меню, появляется диалог, предлагающий выбрать какого типа проект мы хотим создать. Далее появляется визард, который запрашивает параметры создаваемого проекта и создаёт новые файлы. Вот такой визард мы и свояем.
Все визарды полностью написаны на встроенном скриптовом языке. Что нам нужно знать, что бы написать свой скрипт? Ну в теории надо изучить синтаксис скриптового языка и его API. Если с первым всё легко и просто — язык очень похож на C++ и на php, и обладает неплохой документацией на оф. сайте [6] — то со вторым сложнее. Документация API C::B мягко говоря страдает… Даже не знаю чем, но явно чем-то нехорошим. В общем, вот раздел wiki, посвящённый скриптованию C::B [7]. Информация там весьма поверхностная, не достоверная и подразумевающая, что читатель знаком с внутренним устройством Code::Blocks. Как следствие, для освоения скриптового API надо ещё запастись документацией на C::B SDK [8], документацией на wxWidgets [9] и, самое главное, исходниками C::B [10]. Теперь всё есть? Тогда поехали.
Файлы, описывающие визарды, запрятаны в дебрях каталога share, а именно share/CodeBlocks/templates/wizard. Сам share в виндах находится в каталоге кодблокса (например, в C:\Program Files\CodeBlocks), во всех остальных системах он расположен там, где ему и положено. В каталоге wizard навалена куча каталогов с описанием, собственно, мастеров. Пройдёмся по этим каталогам и глянем, что там есть. В большинстве случаев директория визарда содержит четыре файла:
  • wizard.script — скрипт, описывающий работу визарда;
  • wizard.xrc — файл ресурсов, описывающий нестандартные странички мастера;
  • wizard.png — картинка, сопровождающая визард;
  • logo.png — иконка визарда в диалоге выбора.
Кроме того, часто встречается поддиректория files — там лежат дефолтные файлы, добавляемые в проект по умолчанию. Всё это хозяйство можно смело расценивать как набор примеров для написания собственного визарда.
Прежде чем приступать к написанию, надо хотя бы в общих чертах определиться, что должен делать наш визард. Я исходил из следующих соображений:
  • в первую очередь визард должен создавать проект для C::B — это его основное предназначение;
  • создаваемый проект не должен иметь всякого лишнего хлама, типа Debug и Release таргетов, не должен пытаться скомпилировать содержимое C-компилятором и тд.;
  • неплохо, если визард будет создавать заготовку для корневого модуля;
  • кроме проекта для C::B необходимо уметь создавать проект для QuartusII с базовыми настройками;
  • визард должен уметь не только СОЗДАВАТЬ проект для квартуса, но ещё и НЕ СОЗДАВАТЬ его, на случай если мы хотим работать с уже существующим проектом.
Так, стоп, надо уметь создавать проект для QuartusII. А как это сделать? Долго и упорно роемся в документации на систему и хелпах к консольным утилитам. Документация говорит, что проект создаёт команда
quartus_map proj_name --source=src_filename --family=FPGA_FAMILY …
но хелп по ней говорит, что quartus_map это «Quartus II Analysis & Synthesis», а вовсе не «Quartus II Project Manager», или что-нибудь в этом духе. Что из этого следует? Следует вот что: quartus_map действительно может создать файл проекта, но для этого должно выполняться много условий, например компилируемость всех подсунутых исходников, корректное указание имени семейства и тд. Кроме того, у этой команды есть ключики далеко не для всех настроек. В общем, муторно это, надо искать обходные пути. Хелп на TCL-скриптинг говорит, что проектом можно управлять с помощью команд tcl-скрипта, но тоже как-то мутно. Во всяком случае, я не смог найти всех интересующих меня команд. Размышляя на тему наиболее прямого решения поставленной задачи, я пришёл к мысли, что раз не получается заставить квартус сгенерить новый проект, надо создать его самостоятельно.
Итак, лезем в каталог с проектом квартуса и смотрим, что же там есть. А есть там много чего и, к моему счастью, почти всё это добро (кроме временных хеш-баз) хранится в текстовом формате. Даже файлы схем — .bdf — оказались написанными на лиспо-подобном языке (ага, здравствуй AutoLisp). Дальше дело техники — ряд нехитрых экспериментов выявил необходимый минимум для создания нового проекта и строчки, отвечающие за интересующие меня настройки. Всё оказалось весьма гуманно — для нового проекта нужно создать два файла — qpf и qsf. Файл проекта по сути состоит из трёх строк — версия квартуса, время создания и имя ревизии, которое, как мы договорились, будет совпадать с именем проекта — остальное комментарии. Файл настроек ревизии чуть сложнее, но, для только что созданного проекта, тоже состоит преимущественно из комментариев. Разбираться в нескончаемых тоннах параметров квартуса не нужно — в файле настроек указываются только явно заданные настройки, а много задавать нам и не надо. Все настройки, включая список файлов исходников, свалены в кучу, порядок не важен, одна настройка на одну строку. Вообще-то, каждая строка представляет собой команду tcl, устанавливающую тот или иной параметр, но это в данном случае не принципиально. Файл может содержать пустые строки и комментарии. Имена у настроек вполне внятные и читабельные/понимабельные, значения — тоже, к регистру, вроде бы, не чувствительны и, даже, в некоторых случаях (например для FAMILY) допускают вариативность. Значит решено — пару текстовых файлов нам сгенерирует C::B.

С технологией создания проекта для QuartusII мы определились, вернёмся к нашему мастеру создания проекта для C::B. Полистав API визардов [11] видим, что нам предлагают набор стандартных страничек для сбора базовой информации о проекте. Этого нам вполне хватит для выполнения нескольких первых пунктов, однако для сбора информации о базовых настройках проекта для квартуса нам понадобится дополнительный диалог. К счастью, в диалог можно запихнуть произвольное количество страниц с любыми контролами, да ещё и управлять порядком их отображения в рантайме. К не счастью, далеко не до всех этих контролов можно потом достучаться из скрипта. Перечень доступных нам элементов ограничивается текстовыми полями, чекбоксами, радиобоксами (не путать с радиобаттонами), и списками двух типов, а доступные операции над ними — установкой значения, получением значения и выключением/отключением. Кроме того, от чекбокса можно получить событие по нажатию. Описываются кастомные страницы в XML-based формате XRC [12], описание, как было сказано выше, должно находиться в файле wizard.xrc в каталоге нашего визарда (все страницы описываются в одном файле). Создавать XRC-файл можно в стиле «программирование в картинках», для этого подойдёт почти любой RAD для wxWidgets [13], например поставляемый в составе C::B wxSmith. Почесав репу и подумав, что мне может пригодиться, я нарисовал вот такую форму:
Диалог настройки проекта QuartusII
По хорошему, область ввода в середине должна гаситься при переключении радиобокса на первые два пункта, а список доступных семейств — заполняться в рантайме, в зависимости от конфигурации квартуса, но ни то ни другое не представляется возможным реализовать в визарде. Первое можно было бы обойти, вынеся настройки в отдельную страницу и просто либо показывать её, либо нет, в зависимости от состояния радиобокса, но мне было лень плодить сущности — меньше раз тыкать на Next (хотя, если припрёт добавить ещё опций, то всё-таки придётся так сделать, место на странице не бесконечное).
Итак, форма готова — пора писать скрипт (только не забудьте открыть окошко со скриптовыми логами — View->Script console). Скрипт визарда представляет собой набор callback-функций, которые C::B вызывает в определённом порядке. Работа визарда начинается с функции BeginWizard(), в который мы должны добавить все страницы визарда в и их базовом порядке (базовом — потому что потом этот порядок можно будет изменить). Далее C::B для каждой страницы вызывает функции OnEnter_<page_name>, OnLeave_<page_name>, OnGetPrevPage_<page_name> и OnGetNextPage_<page_name> (подробнее см. [14]), в которых происходит первичная обработка вводимых данных. После того как отображены все страницы визарда (нажата кнопка Finish), C::B начинает процедуру генерации проекта — вызывает функции GetFilesDir(), GetGeneratedFile(file_index) и SetupProject(project). Первая указывает на директорию, откуда надо скопировать все файлы в создаваемый проект, вторая, вызываемая в цикле, возвращает по очереди новые файлы в виде «название; содержимое», которые тоже надо добавить в проект, а третья даёт возможность выполнить финальную настройка проекта. Целиком весь скрипт я приводить не буду, кому интересно — посмотрят в прилагаемом архиве, а сосредоточусь на основных моментах.
Итак, сначала объявляем глобальные переменные, в которых будем хранить данные о проекте в процессе выполнения визарда. Кроме них я объявил две переменные, которые меняться не будут:

QuartusPath    <- _T("$(#quartus)");
QuartusPathBin <- _T("$(#quartus.bin)");

Собственно значения — это есть глобальные переменные C::B, которые развернутся в путь к квартусу. Концепция глобальных переменных C::B весьма удобна для переноса проектов с компа на комп — даже если пути к библиотеками не совпадают не надо править никакие конфиги. Определяются глобальные переменные в диалоге, до которого можно добраться через меню Settings->Global variables… Определение нужной нам переменной может выглядеть так:
Определение глобальной переменной
Далее описываем наш визард в функции BeginWizard():

    local wiz_type = Wizard.GetWizardType();
    if (wiz_type == wizProject) //проверяем правильность вызова
	//(кстати, в одном файле могут располагаться визарды разных типов, например, как в визардах console, wxwidgets и тд.)
    {
        local intro_msg = _T("Welcome to the new Altera CPLD/FPGA project wizard!\n" +
                             "This wizard will guide you to create a new Altera CPLD/FPGA project.\n" +
                             "You must have installed QuartusII software and $(#quartus.bin)\n" +
                             "variable defined to use projects of this type.\n\n" +
                             "When you 're ready to proceed, please click \"Next\"..."); //послание юзеру =)

        Wizard.AddInfoPage(_T("qIntro"), intro_msg); //страничка с посланием
        Wizard.AddProjectPathPage(); //страница выбора имени и директории проекта
        Wizard.AddPage(_T("qProjOptions")); //наша кастомная страница
    }

Когда юзер прочитает приветствие, проверим определена ли глобальная переменная $(#quartus) и установлен ли в системе квартус

function OnLeave_qIntro(fwd)
{
    local cmd_file = ::wxString();
    if (fwd)
    {
        local dir_nomacro = ReplaceMacros(QuartusPath, true); //подстановка пути вместо имени переменной
			//если переменная ещё не определена в системе, пользователю предложат её определить
			//Далее следует ещё код проверки корректности пути, но я его опущу
        local dir_nomacro_bin = ReplaceMacros(QuartusPathBin, true); //всё аналогично, код проверки опять опускаю
			//кстати, замечу, что так как переменная $(#quartus.bin) не является встроенной, как, например $(#quartus.lib), то раскрывается она ровно в то, что мы туда запишем
			//$(#quartus.lib) раскрылся бы в абсолютный путь до подкаталога lib, но мне не хочется вбивать полный путь в настройках переменной, 
			//поэтому тут будет относительный путь до директории bin, который можно определить либо как "bin", либо как "bin64", в зависимости от системы
        if (PLATFORM == PLATFORM_MSW) //в зависимости от OS выбираем имя исполняемого файла
            cmd_file = _T("quartus.exe");
        else
            cmd_file = _T("quartus");
        if (!VerifyFile(dir_nomacro + wxFILE_SEP_PATH + dir_nomacro_bin, cmd_file, _T("executable"))) return false; //финальная проверка корректности пути
        QuartusPathBinFull = dir_nomacro + wxFILE_SEP_PATH + dir_nomacro_bin; //сохраняем раскрытый путь для дальнейшего использования
    }
    return true;
}

Здесь есть нюанс — если пользователь нажмёт на галку «Skip this page next time», то страница эта больше не будет отображаться, а значит и не будет выполняться функция OnLeave_qIntro. Нам, в общем-то, всё равно — мы уже заставили пользователя корректно определить переменную, больше этот код можно и не выполнять, но иметь ввиду обязательно надо.
Страница выбора имени и пути проекта никакой обработки от скрипта не требует, так что переходим к нашей кастомной форме:

function OnEnter_qProjOptions(fwd)
{
    local path = ::wxString();
    local family_list = ::wxString();
    local cat_cmd = ::wxString();
    if (fwd && Wizard.GetTextControlValue(_T("txtProjName")).IsEmpty()) //инициализация только в первый раз, если юзер нажал Back, а потом вернулся, не портим ему то, что он ввёл
    {
//начальная инициализация
        Wizard.SetTextControlValue(_T("txtProjName"), Wizard.GetProjectName() + _T(".qpf"));
        Wizard.SetRadioboxSelection(_T("rbQuartusProject"), 2);
//загружаем значения по умолчанию из конфига
        Wizard.SetComboboxSelection(_T("cbFamily"), ConfigManager.Read(_T("/quartus_project_wizard/fpga_family"), 5));
        Wizard.SetTextControlValue(_T("txtDevice"), ConfigManager.Read(_T("/quartus_project_wizard/fpga_device"), _T("")));
        Wizard.SetComboboxSelection(_T("cbIOStandart"), ConfigManager.Read(_T("/quartus_project_wizard/fpga_io_standart"), -1));
        Wizard.SetComboboxSelection(_T("cbTopLevel"), ConfigManager.Read(_T("/quartus_project_wizard/root_file"), 0));
        Wizard.CheckCheckbox(_T("chkSmartComp"),  IntToBool(ConfigManager.Read(_T("/quartus_project_wizard/smart_comp"), 0)));
        Wizard.CheckCheckbox(_T("chkRunQuartus"), IntToBool(ConfigManager.Read(_T("/quartus_project_wizard/run_quartus"), 1)));
        Wizard.CheckCheckbox(_T("chkShowReport"), IntToBool(ConfigManager.Read(_T("/quartus_project_wizard/show_report"), 1)));
//в зависимости от платформы выбираем как называется команда cat
        if (PLATFORM == PLATFORM_MSW)
            cat_cmd = _T("type");
        else
            cat_cmd = _T("cat");

        Wizard.SetTextControlValue(_T("txtCatCmd"), ConfigManager.Read(_T("/quartus_project_wizard/cat_command"), cat_cmd));
        Wizard.EnableWindow(_T("txtCatCmd"), Wizard.IsCheckboxChecked(_T("chkShowReport")));
    }
}

function OnLeave_qProjOptions(fwd)
{
    if (fwd)
    {
//считываем введённые пользователем данные
        QuartusProj = Wizard.GetRadioboxSelection(_T("rbQuartusProject"));
        if(QuartusProj == 2)
        {
//не забываем введённое пользователем сохранить в конфиг, как значения по умолчанию
            FPGA_Family = Wizard.GetComboboxStringSelection(_T("cbFamily"));
            ConfigManager.Write(_T("/quartus_project_wizard/fpga_family"), Wizard.GetComboboxSelection(_T("cbFamily")));
            FPGA_Device = Wizard.GetTextControlValue(_T("txtDevice"));
            ConfigManager.Write(_T("/quartus_project_wizard/fpga_device"), FPGA_Device);
            FPGA_IOStandart = Wizard.GetComboboxStringSelection(_T("cbIOStandart"));
            ConfigManager.Write(_T("/quartus_project_wizard/fpga_io_standart"), Wizard.GetComboboxSelection(_T("cbIOStandart")));
            local toplevel_file_type = Wizard.GetComboboxStringSelection(_T("cbTopLevel"));
            ConfigManager.Write(_T("/quartus_project_wizard/root_file"), Wizard.GetComboboxSelection(_T("cbTopLevel")));
            FPGA_CompSmart = Wizard.IsCheckboxChecked(_T("chkSmartComp"));
            ConfigManager.Write(_T("/quartus_project_wizard/smart_comp"), BoolToInt(FPGA_CompSmart));
            FPGA_RunQuartus = Wizard.IsCheckboxChecked(_T("chkRunQuartus"));
            ConfigManager.Write(_T("/quartus_project_wizard/run_quartus"), BoolToInt(FPGA_RunQuartus));
            if(toplevel_file_type.Matches(_T("Verilog HDL")))
            {
                FPGA_TLFileLang = 1;
            } else if(toplevel_file_type.Matches(_T("VHDL"))) {
                FPGA_TLFileLang = 2;
            } else {
                FPGA_TLFileLang = 0;
            }
        }
        FPGA_ShowReport = Wizard.IsCheckboxChecked(_T("chkShowReport"));
        ConfigManager.Write(_T("/quartus_project_wizard/show_report"), BoolToInt(FPGA_ShowReport));
        CatCommand = Wizard.GetTextControlValue(_T("txtCatCmd"));
        if(FPGA_ShowReport) ConfigManager.Write(_T("/quartus_project_wizard/cat_command"), CatCommand);
    }
    return true;
}

На этом страницы закончились, приступаем к генерации проекта. В тупую копировать кучу файлов из каталога в проект нам не интересно. Можно было бы так реализовать добавление top-level исходника, но нам же нужно копировать тот или иной файл, в зависимости от выбранного языка описания. Так что функции GetFilesDir() у нас не будет. А вот GetGeneratedFile(file_index) нам понадобится. С этой функцией идея вот в чём — C::B крутится в цикле вызывая функцию до тех пор, пока она не вернёт не валидный файл (или пока не счётчик не дотикает до 50 — такой лимит на количество создаваемых файлов), а потом создаёт соответствующие файлы и добавляет их в создаваемый проект (во все таргеты, надо отметить). Контент файлов мы будем генерировать на основе шаблонов, для чего попрём и исправим под себя набор функций обработки шаблонов из скрипта для wxwidgets, или для плугинов. Я их приводить не буду, там ничего особо интересного, ограничусь только функцией GetGeneratedFile:

function GetGeneratedFile(file_index)
{
    local result = _T(";");
    if(QuartusProj == 2) //если выбран пункт "создать новый проект для квартуса"
    {
        switch (file_index) //какой файл у нас запрашивают?
        {
            case 0: result = Wizard.GetProjectName() + _T(".qpf") + _T(";") + GenerateFile(Wizard.FindTemplateFile(_T("QuartusII/files/project.qpf"))); break; //генерим qpf
            case 1: result = Wizard.GetProjectName() + _T(".qsf") + _T(";") + GenerateFile(Wizard.FindTemplateFile(_T("QuartusII/files/project.qsf"))); break; //генерим qsf
            case 2: 
                if(FPGA_TLFileLang == 1)	//выбираем, какой исходник надо создать
	            result = _T("root.v") + _T(";") + GenerateFile(Wizard.FindTemplateFile(_T("QuartusII/files/root.v")));
	        else if (FPGA_TLFileLang == 2)
	            result = _T("root.vhd") + _T(";") + GenerateFile(Wizard.FindTemplateFile(_T("QuartusII/files/root.vhd")));
	        else
                    result = _T(";");
	        break;
        }
    }
    return result; //выплёвываем имя файла и его контент, разделённые ";"
}


Ну и, наконец, финишная прямая — итоговая настройка создаваемого проекта.

function SetupProject(project)
{
    local proj_name = Wizard.GetProjectName();
    local index = 0;
    project.AddFile(0, proj_name + _T(".qpf"), false, false, 50); //если мы не создавали файлы прокта, то добавим имеющиеся
    project.AddFile(0, proj_name + _T(".qsf"), false, false, 50); //если же создавали, то они уже в проекта и метод AddFile ничего не сделает
    project.AddFile(0, proj_name + _T(".flow.rpt"), false, false, 50); //для удобства добавляю в проект
    project.AddFile(0, proj_name + _T(".fit.rpt"), false, false, 50); //файлы отчёта о компиляции
    project.AddFile(0, proj_name + _T(".map.rpt"), false, false, 50); //скорее всего они сейчас не существуют,
    project.AddFile(0, proj_name + _T(".sta.rpt"), false, false, 50); //но будут созданы квартусом при первой сборке
    project.AddFile(0, proj_name + _T(".asm.rpt"), false, false, 50); //а добавить несуществующий файл в проект нам никто не мешает
    for(index = project.GetBuildTargetsCount(); index != 0; index--) //удаляем преддефайненные таргеты Release и Debug
    {
        project.RemoveBuildTarget(index-1);
    }
    project.AddBuildTarget(proj_name); //создаём новый таргет с именем, совпадающим с именем проекта (можно считать, то это ревизия =) )
    local target = project.GetBuildTarget(proj_name); //получаем объект только что созданного таргета
    if (!IsNull(target))
    {
        target.SetTargetType(ttCommandsOnly); //говорим, что это не компилируемый таргет
    }
//в качестве бонуса к некомпилируемому таргету получаем возможность запустить пре/пост-билд команды без запуска "компиляции"
//используем это для своих целей
    project.AddCommandsBeforeBuild(QuartusPath + wxFILE_SEP_PATH + QuartusPathBin + wxFILE_SEP_PATH + _T("quartus_cmd ") + proj_name + _T(" -c ") + proj_name); //эта строчка позволяет запустить компиляцию квартусом по кнопке Build (Ctrl+F9)
    if(FPGA_ShowReport)
        project.AddCommandsAfterBuild(CatCommand + _T(" \"") + proj_name + _T(".flow.rpt\"")); //а эта - получить отчёт после завершения компиляции

    if(QuartusProj == 1) //если нужно запустить квартус для создания нового проекта
    {

        if(QuartusPathBinFull.IsEmpty()) //вспоминаем про нюанс ;)
        {
            local dir_nomacro = ReplaceMacros(QuartusPath, true);
            local dir_nomacro_bin = ReplaceMacros(QuartusPathBin, true);
            QuartusPathBinFull = dir_nomacro + wxFILE_SEP_PATH + dir_nomacro_bin;
        }
        
        IO.Execute(QuartusPathBinFull + wxFILE_SEP_PATH + _T("quartus")); // IO.Execute - функция не безопасная, по-этому пользователя спросят, хочет ли он действительно выполнить эту команду (настраивается в настройках скриптов)
    }
    if(QuartusProj == 2) //если мы сами создаём проект для квартуса
    {
        if(FPGA_TLFileLang > 0)
        {
            local root_filename = ::wxString();
            if(FPGA_TLFileLang == 1)
                root_filename = _T("root.v")
            if(FPGA_TLFileLang == 2)
                root_filename = _T("root.vhd")
            local root_file = project.GetFileByFilename(root_filename, true, true);
            root_file.AddBuildTarget(proj_name); //файл автоматически при создании добавился во все таргеты, но мы их снесли и теперь он бесхозный. В принципе, нам это не важно, но для порядка добавим его в созданный таргет
        }
        if(FPGA_RunQuartus) //если надо запустить квартус для донастройки
        {
            if(QuartusPathBinFull.IsEmpty()) //см. выше
            {
                local dir_nomacro = ReplaceMacros(QuartusPath, true);
                local dir_nomacro_bin = ReplaceMacros(QuartusPathBin, true);
                QuartusPathBinFull = dir_nomacro + wxFILE_SEP_PATH + dir_nomacro_bin;
            }

            IO.Execute(QuartusPathBinFull + wxFILE_SEP_PATH + _T("quartus ") + proj_name + _T(".qpf"));
        }
    }
    return true;
}


Далее у меня идут вспомогательные функции упёртые из других скриптов и особого интереса не представляющие.
В прилагаемом архиве есть директория QuartusII, в которой находится всё, что нужно для мастера. Берём её и кладём в каталог… share/CodeBlocks/templates/wizard? А вот и нет. Да, визарды лежат там, но этот родные визарды, которые живут в C::B и обновляются с ним, а пользовательские должны лежать отдельно, в домашнем каталоге пользователя. Для винды это, например C:\Documents and Settings\[username]\Application Data\codeblocks\share\codeblocks\templates\wizard. Вот туда мы и положим наш каталог QuartusII. Конечно, никто нам не мешает его и в основной каталог положить, тем более, что мы создаём новый визард, а не правим существующий, то есть при обновлении его никто не затрёт, но это идеологически не православно =).
Теперь нам надо сообщить кодблоксу о существовании нашего визарда ([15]). В среде C::B жмём New -> Project и в появившемся диалоге жмём ПКМ на любой иконке визарда, в появившемся меню выбираем Edit global registration script, читаем предупреждение и получаем скрипт, в котором перечислены все визарды.

Ну тут всё понятно — делаем по образцу.

    RegisterWizard(wizProject,     _T("QuartusII"),    _T("Altera FPGA Project"),   _T("Embedded Systems"));

Порядок, в котором идут строчки, не важен, важно название. Я сначала назвал визард как-то в стиле «QuartusII Project», но в процессе отладки, задолбавшись прокручивать список визардов, решил его переименовать во что-нибудь поближе к началу алфавита =). Последнее поле — категория — нужно только для фильтра категорий в самом верху диалога выбора, так что писать туда можно что угодно, хотя рекомендуют выбирать из того, что уже есть.
Сохраняем файл, перезапускаем C::B и идём проверять. Кстати, обратите внимание, что название нашего визарда написано красным — это, как раз, индикатор того, что скрипт берётся из пользовательской директории, а не из основной.
Теперь создание новых проектов для квартуса просто и удобно =).
Да, кстати, не забудем в тулсах везде исправить путь к квартусу на $(#quartus)/$(#quartus.bin) — потом может пригодиться (например при обновлении квартуса).

Часть I — Часть II — Часть III

Ссылки
  1. Моя очередь мигать диодиками — статья на we.easyelectronics.ru
  2. Scintilla — статья в wikipedia
  3. Дефолтовые кейбиндинги в сцинцилле
  4. Squirrel — статья в wikipedia
  5. Файлы проектов Code::Blocks — официальное wiki Code::Blocks
  6. Squirrel programming language — документация на официальном сайте
  7. Раздел о скриптовании — официальное wiki Code::Blocks
  8. Code::Blocks SDK
  9. Документация на wxWidgets
  10. Инструкция по скачиванию исходников Code::Blocks из VCS
  11. Описание основных функций, использующихся для создания визардов — официальное wiki Code::Blocks
  12. Описание формата ресурсов XRC
  13. Сравнение различных RAD для wxWidgets
  14. События, генерируемые системой при работе визарда — официальное wiki Code::Blocks
  15. Вводная в написание собственных визардов — официальное wiki Code::Blocks
  16. Adding support for non C/C++ files to the build system — официальное wiki Code::Blocks
  17. Регулярные_выражения — статья в wikipedia

Приложенный архив

  • +3
  • 30 мая 2011, 10:56
  • Alatar

Комментарии (2)

RSS свернуть / развернуть
Еще раз спасибо за интересный цикл статей. Но можно я опять побуду grammar nazi?

«Run Quartus II to create project manually»,
«Run Quartus II to set advanced options»

Все, я больше не буду. Честно-честно.
0
=) Спасибо, сейчас поправлю
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.