Определение, почему Simulink Accelerator регенерирует код

Иногда Simulink ® регенерирует цель симуляции для модели в начале симуляции в Accelerator Mode (TM), но не всегда понятно, почему происходит регенерация. В этом примере показано, как использовать команды Simulink ® MATLAB ®, чтобы определить, почему Simulink регенерирует код для симуляций режима Accelerator.

Режим Accelerator Simulink ускоряет симуляцию вашей модели, создавая исполняемую версию модели, называемую целью симуляции, и запуская эту цель вместо интерпретации модели, как это делается во время симуляции Режим normal mode. Режим Accelerator создаёт цель симуляции, генерируя код C из вашей модели и вызывая функцию MATLAB ® mex, чтобы скомпилировать и динамически связать сгенерированный код с Simulink.

Процесс генерации кода и компиляции происходит в первый раз, когда вы симулируете свою модель в Режим Accelerator. Кроме того, генерация кода может произойти при последующих симуляциях, особенно если модель изменяется между симуляциями (для примера, после сложения блока). Генерация кода занимает много времени, и часто желательно избегать его, порядок максимизировать количество симуляций за заданный временной промежуток.

Simulink использует контрольную сумму модели, чтобы определить, нужно ли регенерировать код. Эта контрольная сумма является массивом из четырех целых чисел, вычисленных с помощью алгоритма контрольной суммы md5, основанного на атрибутах модели и блоков, которые она содержит. Любое изменение в модели, которое изменяет контрольную сумму, заставляет Simulink регенерировать цель симуляции для Режима Accelerator.

Иногда не ясно, какое изменение модели вызвало изменение контрольной суммы и, следовательно, регенерацию кода. Этот пример показов, как исследовать, почему Simulink должен регенерировать код для симуляции Режима Accelerator для заданной модели и ее строения.

Создайте временную рабочую директорию

Поскольку симуляция в Режим Accelerator создает некоторые файлы, сначала перейдите во временную область.

originalDir = pwd;
tempDir = tempname;
mkdir(tempDir)
cd(tempDir)

Откройте пример модели

Мы будем использовать простую модель, slAccelDemoWhyRebuild, на протяжении всего этого примера.

model = 'slAccelDemoWhyRebuild';
open_system(model)
set_param(model,'AccelVerboseBuild','on');

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

simOutput = evalc(['sim(''',model,''')']);
if ~isempty(strfind(simOutput,'Building the Accelerator target for model'))
    disp('Built Simulink Accelerator mex file')
else
    disp('Did not build Simulink Accelerator mex file')
end
Built Simulink Accelerator mex file

Если симуляция запускается снова, без каких-либо изменений модели, мы ожидаем, что Simulink может повторно использовать существующий код и не должен регенерировать код. Выполните те же команды для проверки.

simOutput = evalc(['sim(''',model,''')']);
if ~isempty(strfind(simOutput,'Building the Accelerator target for model'))
    disp('Built Simulink Accelerator mex file')
else
    disp('Did not build Simulink Accelerator mex file')
end
Did not build Simulink Accelerator mex file

Теперь мы меняем некоторые параметры в модели. Мы установим следующие настройки для блока 'Integrator': - установите 'Ignore limit и сбросьте при линеаризации' на ' on ' - установите 'Initial Condition' на '5'

set_param([model,'/Integrator'],'IgnoreLimit','on');
set_param([model,'/Integrator'],'InitialCondition','5');

Когда мы запускаем симуляцию снова, мы видим, что Simulink регенерирует код.

simOutput = evalc(['sim(''',model,''')']);
if ~isempty(strfind(simOutput,'Building the Accelerator target for model'))
    disp('Built Simulink Accelerator mex file')
else
    disp('Did not build Simulink Accelerator mex file')
end
Built Simulink Accelerator mex file

Мы хотели бы знать почему.

Чтобы определить, является ли ранее сгенерированный код все еще действительным для текущего строения модели, Simulink сравнивает контрольную сумму модели, используемую для генерации кода, с текущей контрольной суммой. Если они равны, ранее сгенерированный код все еще действителен, и Simulink Accelerator Mode повторно использует его для текущей симуляции. Если значения различаются, Simulink Accelerator Mode регенерирует и перестраивает код. Таким образом, изучение деталей расчета контрольной суммы может выявить, почему Simulink регенерировал код.

Получение сведений о контрольной сумме

Следующая команда получает детали расчета контрольной суммы модели:

[cs1,csdet1]=Simulink.BlockDiagram.getChecksum(model);

Первый выход - само значение контрольной суммы модели. Второй выход приводит детали того, что ушло в расчет контрольной суммы.

Давайте установим измененные параметры блоков на их исходные значения и получим контрольную сумму и детали для этого строения

set_param([model,'/Integrator'],'IgnoreLimit','off');
set_param([model,'/Integrator'],'InitialCondition','0');
[cs2,csdet2]=Simulink.BlockDiagram.getChecksum(model);

Сравнение этих двух значений контрольной суммы эквивалентно определению, будет ли Simulink Accelerator регенерировать код. Обратите внимание, что значения контрольной суммы различаются, так как мы ожидаем, основываясь на том, что Simulink Accelerator регенерирует код каждый раз, когда он запускается.

if (cs1 ~= cs2)
    disp('Checksums are different')
else
    disp('Checksums are the same')
end
Checksums are different

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

csdet1
csdet1 = 

  struct with fields:

          ContentsChecksum: [1x1 struct]
         InterfaceChecksum: [1x1 struct]
     ContentsChecksumItems: [186x1 struct]
    InterfaceChecksumItems: [49x1 struct]

Подробные данные контрольной суммы представляют собой массив структур с четырьмя полями. Два поля являются контрольными суммами компонентов контрольной суммы модели (они называются ContentsChecksum и InterfaceChecksum), а два других являются соответствующими подробными данными контрольной суммы. Эти детали соответствуют различной информации, которая была использована при расчете двух контрольных сумм компонентов. Контрольная сумма модели [структурная] является функцией ContentsChecksum и InterfaceChecksum.

Во-первых, давайте определим, лежит ли различие в содержимом модели или интерфейсе модели

if (csdet1.ContentsChecksum.Value ~= csdet2.ContentsChecksum.Value)
    disp('Contents checksums are different')
else
    disp('Contents checksums are the same')
end
if (csdet1.InterfaceChecksum.Value ~= csdet2.InterfaceChecksum.Value)
    disp('Interface checksums are different')
else
    disp('Interface checksums are the same')
end
Contents checksums are different
Interface checksums are the same

Используйте подробные сведения, чтобы определить, почему изменена контрольная сумма

Теперь, когда мы знаем, что изменение находится в ContentsChecksum, мы можем просмотреть ContentsChecksumItems, чтобы увидеть, что изменилось.

idxForDifferences=[];
for idx = 1:length(csdet1.ContentsChecksumItems)
    if (~strcmp(csdet1.ContentsChecksumItems(idx).Handle, ...
                csdet2.ContentsChecksumItems(idx).Handle))
        idxForDifferences=[idxForDifferences,idx];
        disp(['Handles different for item ',num2str(idx)]);
    end
    if (~strcmp(csdet1.ContentsChecksumItems(idx).Identifier, ...
                csdet2.ContentsChecksumItems(idx).Identifier))
        disp(['Identifiers different for item ',num2str(idx)]);
        idxForDifferences=[idxForDifferences,idx];
    end
    if(ischar(csdet1.ContentsChecksumItems(idx).Value))
        if (~strcmp(csdet1.ContentsChecksumItems(idx).Value, ...
                    csdet2.ContentsChecksumItems(idx).Value))
            disp(['String Values different for item ',num2str(idx)]);
            idxForDifferences=[idxForDifferences,idx];
        end
    end
    if(isnumeric(csdet1.ContentsChecksumItems(idx).Value))
        if (csdet1.ContentsChecksumItems(idx).Value ~= ...
            csdet2.ContentsChecksumItems(idx).Value)
            disp(['Numeric values are different for item ',num2str(idx)]);
            idxForDifferences=[idxForDifferences,idx];
        end
    end
end
String Values different for item 40

Теперь, когда мы знаем, что различия в элементах индексов, перечисленных в idxForDifferences, можно просмотреть эти элементы в двух массивах ContentsChecksumItems

blk1 = csdet1.ContentsChecksumItems(idxForDifferences(1)).Handle
blk2 = csdet2.ContentsChecksumItems(idxForDifferences(1)).Handle
id1 = csdet1.ContentsChecksumItems(idxForDifferences(1)).Identifier
id2 = csdet2.ContentsChecksumItems(idxForDifferences(1)).Identifier
blk1 =

    'slAccelDemoWhyRebuild/Integrator'


blk2 =

    'slAccelDemoWhyRebuild/Integrator'


id1 =

    'IgnoreLimit'


id2 =

    'IgnoreLimit'

Указатель для обоих элементов является 'slAccelDemoWhyRebuild/Integrator', что указывает на блок с изменяющимися данными. Идентификатор для обоих является 'IgnoreLimit', который говорит нам, что это была настройка блока, которая изменилась, что привело к другой контрольной сумме для модели. Настройка начального условия блока не отображается в подробных данных контрольной суммы. Поэтому мы ожидаем, что если будет изменена только настройка начального условия, то перестроение не произойдет.

Избегайте перестроения при последующих симуляциях

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

Давайте снова симулируем модель в режиме Accelerator. Мы ожидаем, что он перестроится для этой симуляции, потому что мы изменили параметр ('IgnoreLimit') для расчета контрольной суммы выше.

simOutput = evalc(['sim(''',model,''')']);
if ~isempty(strfind(simOutput,'Building the Accelerator target for model'))
    disp('Built Simulink Accelerator mex file')
else
    disp('Did not build Simulink Accelerator mex file')
end
Built Simulink Accelerator mex file

Теперь давайте изменим только настройку Initial Condition и снова промоделируем. Мы ожидаем, что на этот раз не должно произойти никакого восстановления.

set_param([model,'/Integrator'],'InitialCondition','-3');
simOutput = evalc(['sim(''',model,''')']);
if ~isempty(strfind(simOutput,'Building the Accelerator target for model'))
    disp('Built Simulink Accelerator mex file')
else
    disp('Did not build Simulink Accelerator mex file')
end
Did not build Simulink Accelerator mex file

Как ожидалось из анализа контрольной суммы, изменение параметра для 'Initial Condition' не вызывает регенерации кода для симуляции в режиме Accelerator.

Очистка

Закройте модель и удалите сгенерированные файлы.

bdclose(model)
clear([model,'_acc'])
cd(originalDir)
rmdir(tempDir,'s')