exponenta event banner

Определение причин регенерации кода Simulink Accelerator

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

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

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

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

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

Создание временной рабочей папки

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

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

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

В этом примере мы будем использовать простую модель slAccelDemoWhyRebuild.

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

При первом запуске модели в режиме ускорителя она генерирует и компилирует код в соответствии с ожиданиями.

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

Теперь мы изменяем некоторые параметры в модели. Мы установим следующие настройки для блока «Интегратор»: - set 'Ignore limit and reset when linearizing' to '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]

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

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

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

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

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

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, мы можем посмотреть на эти элементы в двух массивах ContiveChecksumItems

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», указывающий блок с изменяющимися данными. Идентификатором для обоих является IgnureLimit, который сообщает нам, что это была измененная настройка блока, что приводит к разной контрольной сумме для модели. Настройка начального условия блока не отображается в подробных данных контрольной суммы. Поэтому мы ожидаем, что если изменяется только настройка для начального условия, то перестроение не произойдет.

Избегайте перестроения при последовательном моделировании

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

Давайте снова смоделируем модель в режиме акселератора. Мы ожидаем, что он будет перестроен для этого моделирования, потому что мы изменили параметр («IgnireLimit») для вычисления контрольной суммы выше.

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

Теперь давайте изменим только параметр «Начальное условие» и смоделируем снова. Мы ожидаем, что на этот раз не произойдет никакой перестройки.

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» не приводит к регенерации кода для моделирования в режиме ускорителя.

Очистка

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

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