function bus_creator_hier(sliceXfrmr, sysH, busInfo, outBlkName, value, origPortH)
    % Utility to create bus hierarchy from CompiledBusStruct.

    %   Copyright 2014-2021 The MathWorks, Inc.
    
    % Layout constants
    GroundExt = [10 10];
    CreatorWidth = 6;
    DeltaH = 40;
    DeltaV = 40;
    
    % Globals
    InputIndex = 1;
    
    busElemInfoMap = containers.Map('KeyType','char','ValueType','any');
    
    % Build signal hier
    [portH, portYPos, depthCnt, outName] = add_sub_bus_hier(busInfo);
    
    % Add the output port block
    outWidth = 30;
    outHeight = 10;
    oLeft = (depthCnt+1)*DeltaH;
    oRight = oLeft+outWidth;
    oTop = portYPos - (outHeight/2);
    oBottom = portYPos + (outHeight/2);
    outPos = [oLeft oTop oRight oBottom];
    outH = add_block('built-in/Outport', [getfullname(sysH) '/' outBlkName], 'Position', outPos);
    ph = get_param(outH, 'PortHandles');
    wire(portH, ph.Inport, outName);
    
    % Nested utility functions
    function wire(src, dest, name)
        lineH = add_line(sysH, src, dest, 'autorouting', 'on');
        if ~isempty(name)
            set_param(lineH, 'Name', name);
        end
    end
    
    function [portH, portYPos] = add_leaf_input(srcPortH,childBusInfo)
        [gPos, portYPos] = get_block_position();
        gPath = sprintf('%s/G%d', getfullname(sysH), InputIndex);

        % Getting compiled port information to terminate the port by a
        % constant block that has the same type and size.
        if isa(value,'containers.Map') && isKey(value,srcPortH)
            t = value(srcPortH);
            valStr = Transform.SliceTransformer.convValue2Str(t.value);
        elseif ischar(value)
            valStr = value;
        else
            valStr = Transform.SliceTransformer.convValue2Str(value);
        end
        if childBusInfo.dfsDataTypeElemIdx > -1
            busName = childBusInfo.parentBusObjectName;
            if isKey(busElemInfoMap,busName)
                busElemInfo = busElemInfoMap(busName);
            else
                busElemInfo = slInternal('busDiagnostics','getDFSElementsInBus',...
                              sliceXfrmr.origMdlName,busName);
                busElemInfoMap(busName) =  busElemInfo;       
            end

            % Find the bus element info corresponding to current child
            for idx = 1:length(busElemInfo)
                if isequal(busElemInfo(idx).flatIndex, childBusInfo.flatDataTypeElemIdx)
                    matchingBusElementInfo = busElemInfo(idx);
                    break;
                end
            end
            assert(~isempty(matchingBusElementInfo));

            gH = sliceXfrmr.replaceByConstant(gPath, gPos, valStr, ...
                srcPortH, [], matchingBusElementInfo);
        else
            gH = sliceXfrmr.replaceByConstant(gPath, gPos, valStr, srcPortH);
        end
        
        prth = get_param(gH, 'PortHandles');
        portH = prth.Outport;
        
        InputIndex = InputIndex + 1;        
    end

    function [portH, portYPos] = add_constant_with_busobject(thisBusInfo)
        gPath = sprintf('%s/G%d', getfullname(sysH), InputIndex);
        [gPos, portYPos] = get_block_position();
        if isstruct(modelslicerprivate('evalinModel', sliceXfrmr.origMdlName, value))
            valStr = value;
        else
            valStr = '0';
        end
        blkH = sliceXfrmr.replaceByConstant(gPath,gPos, valStr, origPortH);
        set_param(blkH,'OutDataTypeStr',['Bus: ' thisBusInfo.busObjectName],'ShowName','off');
        portHandles = get_param(blkH,'PortHandles');
        portH = portHandles.Outport;
        InputIndex = InputIndex + 1;
        % g2451643: Issue in grounding virtual bus in generated slice
        % Switches expect the virtuality of all data inports to be
        % consistent. So, if the inport bus is virtual, use a signal 
        % conversion block to convert the output of the constant block to
        % output a virtual bus.
        parentBlock = get_param(origPortH, 'Parent');
        parentBlockType = get_param(parentBlock, 'BlockType');
        if strcmp(parentBlockType,'MultiPortSwitch') && ...
                strcmp(get_param(origPortH, 'CompiledBusType'),'VIRTUAL_BUS')
            gPos = [gPos(1) + 20 40 gPos(3) + 20  50];
            sigConvBlk = add_block('built-in/signalConversion', ...
                [getfullname(sysH) '/signalConversion'], ...
                'Position', gPos, ...
                'MakeNameUnique','on', ...
                'ConversionOutput', 'Virtual Bus');
            sigConvBlkPortHandles = get_param(sigConvBlk,'PortHandles');
            sigConvBlkInportH = sigConvBlkPortHandles.Inport;
            add_line(getfullname(sysH), portH, sigConvBlkInportH);
            portH = sigConvBlkPortHandles.Outport;
        end
    end
    
    function [gPos, portYPos] = get_block_position()
        gTop = InputIndex*DeltaV;
        gLeft = DeltaH;
        gBottom = gTop + GroundExt(2);
        gRight = gLeft + GroundExt(1);
        gPos = [gLeft gTop gRight gBottom];
        portYPos = gTop + (GroundExt(2)/2);
    end
    function [blkH, inPortHs, outPort] = add_bus_creator(pos, srcBlkH, inputCnt, busObjName)
        bObj = get(srcBlkH,'Object');
        if strcmp(get(srcBlkH,'BlockType'),'BusCreator') && ~bObj.isSynthesized
            % In case of BusCreator, we can copy the block to preserve all
            % the block properties.
            srcBlockName = strrep(get_param(srcBlkH,'Name'),'/','//');
            blkH = add_block(getfullname(srcBlkH),  [getfullname(sysH) '/' srcBlockName], ...
                'MakeNameUnique',      'on', ...
                'Position',            pos,...
                'ShowName',            'off');
        else
            name = sprintf('creator%d', InputIndex);
            blkH = add_block('built-in/BusCreator',  [getfullname(sysH) '/' name], ...
                'MakeNameUnique',      'on', ...
                'Position',            pos, ...
                'Inputs',              num2str(inputCnt), ...
                'ShowName',            'off');
            if ~isempty(busObjName)
                set_param(blkH,'OutDataTypeStr',['Bus: ' busObjName],'NonVirtualBus','on');
            end
        end
        
        portHandles = get_param(blkH,'PortHandles');
        inPortHs = portHandles.Inport;
        outPort = portHandles.Outport;
    end

    % Main recursive function
    function [portH, portYPos, depthCnt, name] = add_sub_bus_hier(childBusInfo)
        name = childBusInfo.name;
        
        if isempty(childBusInfo.signals)
            % This is a leaf
            depthCnt = 1;
            if isempty(childBusInfo.srcPort) || childBusInfo.srcPort < 1
                srcPortNum = 1;
            else
                srcPortNum = childBusInfo.srcPort;
            end
            tph = get_param(childBusInfo.src,'PortHandles');
            srcPortH = tph.Outport(srcPortNum);
            [portH, portYPos] = add_leaf_input(srcPortH,childBusInfo);
        elseif ~isempty(childBusInfo.busObjectName) ...
                && isstruct(modelslicerprivate('evalinModel', sliceXfrmr.origMdlName, value))
            depthCnt = 1;
            [portH, portYPos] = add_constant_with_busobject(childBusInfo);
        elseif any(strcmp(get(childBusInfo.signals(1).src,'BlockType'), ...
                {'ModelReference','Inport'})) && ~isempty(childBusInfo.busObjectName)
            % If the source block is Model Reference or Root-level Inport, 
            % we cannot ground it by bus creator and constants hierarchy  
            % because it gives bus object as its data type. 
            % Therefore we force to terminate it by a constant with 
            % bus object as its data type.
            depthCnt = 1;
            [portH, portYPos] = add_constant_with_busobject(childBusInfo);
        else
            % This is a sub-bus
            childCnt = numel(childBusInfo.signals);
            childPorts = zeros(1, childCnt);
            childPosY = zeros(1, childCnt);
            childNames = cell(1, childCnt);
            depthCnt = 1;
            
            % Recurse to children and build their hierarchy
            for idx=1:childCnt
                [childPorts(idx), childPosY(idx), cdepth, childNames{idx}] = ...
                    add_sub_bus_hier(childBusInfo.signals(idx));
                
                if (cdepth+1) > depthCnt
                    depthCnt = cdepth+1;
                end
            end
            
            % Compute bus creator position
            left = depthCnt*DeltaH;
            right = left+CreatorWidth;
            top = childPosY(1);
            bottom = childPosY(end);

            % Exeption handling
            if top == bottom
                bottom = top + 30;
            end
            if right == left
                right = left + 10;
            end
            creatorPos = [left top right bottom];
            portYPos = (top+bottom)/2;
            
            % Add the creator and wire it using signal names
            [~, bcInPortHs, portH] = add_bus_creator(creatorPos, childBusInfo.src, childCnt, childBusInfo.busObjectName);
            
            for idx=1:childCnt
                wire(childPorts(idx), bcInPortHs(idx), childNames{idx});
            end
        end
    end
end

