Dynamically create a bus testing harness in Simulink

by

 

Dynamically create a test harness in Simulink given a bus object with arbitrary underlying structure

bus_test_harness.m
%% bus_test_harness.m
% This script will dynamically create a test harness in Simulink for a bus
% object of arbitrary nesting structure. To use the script just update the
% first two lines of the Initialization section to use the name of your
% main bus object.
%
% BASIC DESCRIPTION: The script works by starting with the top level bus
% object and looping through its elements. For any elements that are not
% bus objects, it will add a Constant block to the model and copy over the
% data type and signal size. For elements that are nested bus objects, it
% adds a Bus Creator block, sets it to use the bus object as a nonvirtual
% bus, and increments a counter to loop back through the new bus's
% elements. Each new block is connected to the Bus Creator above it in the
% hierarchy when it is added to the model. The blocks are then repositioned
% for visual clarity. Finally the model is updated to check if there were
% any problems with the setup.
%
% A note on the 'level' of an object:
%
% I use the term 'level' in this script to describe how many layers an
% object is below the main bus object. For example, in the diagram below
% the main bus object B1 is on level 1, the element E1 and nested bus
% object B2 are on level 2, and the elements E2 and E3 are on level 3. This
% is used when positioning the Constant and Bus Creator blocks in the model
% to visually distinguish the different levels and make the diagram more
% readable.
%
%               B1
%  L1          /  \
% -------------------------
%            /      \
%  L2       B2      E1
%          /  \
% -------------------------
%  L3    /      \
%       E2      E3

% Copyright 2012 The MathWorks, Inc.

%% Create and open new model
modelname = 'test_harness_model';
new_system(modelname);
open_system(modelname);
set_param(modelname,'BusObjectLabelMismatch','none'); %turn off warnings for signal name mismatch, this script does not match them.

%% Initialization
% UPDATE THESE TWO LINES TO THE NAME OF YOUR TOP LEVEL BUS OBJECT
currentBus = parentbus;
Bus(1).Name = 'parentbus';

numBC = 1; %counter for number of required Bus Creator blocks
numConst = 0; %counter for number of required Constant blocks
numUnsorted = 1; %counter for number of found buses that have not been sorted
k = 1; %counter for number iterations of while loop
numloops = 0; L = 2; lastBC = 1; %counters to track level of elements

numelem(1) = length(currentBus.Elements); %number of bus elements at top level
Bus(1).InputNum = numelem; %number of elements in currentBus
Bus(1).TargetPortNum = 1; %input port number of downstream block to connect to
Bus(1).Level = 1; %level of the object
Bus(1).handle = add_block('simulink/Signal Routing/Bus Creator',...
    [modelname '/Bus Creator'],'MakeNameUnique','on','Inputs',...
    num2str(Bus(numBC).InputNum),'OutDataTypeStr',['Bus: ' Bus(numBC).Name],...
    'NonVirtualBus','on'); %add Bus Creator block to model and save block handle

%% Loop through nesting structure

while numUnsorted > 0 %if there are more bus objects that we haven't sorted through...
    %loop through all the elements of the currentBus
    for j=1:numelem(k)
        ElemDataType = currentBus.Elements(j).DataType;
        if strcmp(ElemDataType(1:3),'Bus') %if the element of currentBus is a nested bus...
            numBC = numBC + 1; %increment number of required Bus Creator blocks
            numUnsorted = numUnsorted + 1; %one more bus to sort through  
            Bus(numBC).Name = ElemDataType(5:end); %name of nested bus
            Bus(numBC).InputNum = eval(['length(' ElemDataType(5:end) '.Elements)']); %number of inputs on the Bus Creator block
            Bus(numBC).TargetPortNum = j; %for loop iteration is equal to the input port on the parent bus that this block connects to
            Bus(numBC).Level = L; %block level
            Bus(numBC).handle = add_block('simulink/Signal Routing/Bus Creator',...
                [modelname '/Bus Creator'],'MakeNameUnique','on','Inputs',...
                num2str(Bus(numBC).InputNum),'OutDataTypeStr',ElemDataType,...
                'NonVirtualBus','on'); %add Bus Creator block to model and save block handle
            add_line(modelname,[get_param(Bus(numBC).handle,'name') '/1'],...
                [get_param(Bus(k).handle,'name') '/' num2str(Bus(numBC).TargetPortNum)]); %connect outport to parent bus inport
        else %if the element is a non-bus create a Constant block to supply the input signal
            numConst = numConst + 1;
            Const(numConst).TargetPortNum = j; %for loop iteration is equal to the input port on the Bus Creator that this block connects to
            Const(numConst).Level = L; %block level
            Const(numConst).handle = add_block('built-in/Constant',[modelname '/Constant'],...
                'MakeNameUnique','on','Value',['ones([' num2str(currentBus.Elements(j).Dimensions) '])'],...
                'OutDataTypeStr',ElemDataType); %add Constant block and save block handle
            add_line(modelname,[get_param(Const(numConst).handle,'name') '/1'],...
                [get_param(Bus(k).handle,'name') '/' num2str(Const(numConst).TargetPortNum)]); %connect Constant block to parent Bus Creator
        end
    end
    %increment counters
    numUnsorted = numUnsorted - 1;
    k = k + 1;
    %update current bus and its size if there are more to sort through
    if numUnsorted > 0
        currentBus = eval(Bus(k).Name); %update the currentBus to the next bus object to sort through
        numelem(k) = length(currentBus.Elements); %number of elements in the new currentBus
        %determine if the level number needs to be incremented by setting
        %how many while loop iterations to run with a particular level
        %value
        if numloops == 0 %last level is complete
            L = L + 1; %increment level number
            %number of buses at last level is the number of while loop iterations to hold this level value at
            numloops = numBC - lastBC - 1; %subtract out 1 to account for the top level bus
            lastBC = numBC; %store current number of buses for comparison at end of next level
        else
            numloops = numloops - 1; %increment counter if there's more loops to go through
        end
    end
end

%% Position blocks
%This is a pretty simple spacing algorithm that evenly spaces the Constant
%and Bus Creator blocks vertically and spaces them horizontally according
%to their level.

%place Constant blocks using their level to modify horizontal position
for x = 1:numConst
    set_param(Const(x).handle,'Position',[0+50*(L-Const(x).Level) 25+50*x...
        25+50*(L-Const(x).Level) 50+50*x]);
end
%place Bus Creators using their level to modify horizontal position
for y = 1:numBC
    set_param(Bus(y).handle,'Position',[50+60*(L-Bus(y).Level+1) 25+100*y...
        55+60*(L-Bus(y).Level+1) 75+100*y]);
end

%% Try to update model to test if everything is set up correctly
eval([modelname '([],[],[],''compile'')'])
eval([modelname '([],[],[],''term'')']) %end simulation mode so model can close/save
set_param(modelname,'ShowPortDataTypes','on') %display signal data types
set_param(modelname,'ShowLineDimensions','on') %display signal dimensions

Contact us