classdef intelsoc < soc.internal.SocBase & ...
        soc.internal.mixin.Unsaveable & ...
        soc.internal.mixin.InternalAccess
    % INTELSOC Hardware connection object for Intel SoC devices.
    %
    % intelsoc(hostname,username,password) returns a hardware connection object.

    %   Copyright 2021 The MathWorks, Inc.
    properties (Constant, Access = protected)
        SocKey = 'SOCFPGA'; % Preferences group key
    end

    methods
        function obj = intelsoc(varargin)
            % Establish connection
            obj = obj@soc.internal.SocBase(varargin{:});
        end

        %% Program FPGA
        function programFPGA(obj, bitFile, dtbFile, nvPairs)
            %PROGRAMFPGA Load a bitstream image to FPGA.
            %
            % programFPGA(z,bitFile,dtbFile) loads the FPGA bitstream and
            % the associated device tree file to the Intel SoC hardware
            % board followed by a reboot.
            %
            % Example:
            % programFPGA(hw,'mstreamnco_zc706.bit','mstreamnco_zc706.dtb')
            %
            % Generate split bitstreams (core and peripheral rbf files) for
            % Early I/O feature. Early I/O release is a feature to enable
            % DDR prior programming the core rbf file for speeding the boot
            % time. This feature splits the FPGA configuration sequence
            % into two parts. The first part configures the FPGA I/O and
            % enables the HPS external memory interface (EMIF). The second
            % part configures the FPGA fabric. This feature allows more
            % flexibility for designs that need faster boot time. If split
            % bitstream is enabled in the ref design then we generate two
            % rbf files: core and peripheral rbf files. Together these two
            % rbf files contains the same data as the single rbf file that
            % is generated when the early I/O feature is not used.
            
            arguments
                obj
                bitFile char {mustBeNonzeroLengthText, mustBeTextScalar}
                dtbFile char {mustBeNonzeroLengthText, mustBeTextScalar}
                nvPairs.SystemInitPath char {mustBeTextScalar} = '';
                nvPairs.ReferenceDesignName char {mustBeTextScalar} = '';
                nvPairs.UseSplitBitstream logical = false;
            end

            systemInitPath = nvPairs.SystemInitPath;
            rdName = nvPairs.ReferenceDesignName;
            useSplitBitstream = nvPairs.UseSplitBitstream;

            displayProgramStartMessage(obj);

            if useSplitBitstream
                % Resolve split bitstream. There are two separate files,
                % the core rbf file and peripheral rbf file. They are named
                % <bitname>.core.rbf and <bitname>.periph.rbf respectively
                [filePath,fileName,~] = fileparts(bitFile);
                fileName = strrep(fileName, '.periph','');
                fileName = strrep(fileName, '.core','');

                bitFile = fullfile(filePath, [fileName, '.core.rbf']);
                soc.internal.util.resolveFile(bitFile);

                periphRbf = fullfile(filePath, [fileName, '.periph.rbf']);
                soc.internal.util.resolveFile(periphRbf);
            else
                periphRbf = '';
            end

            % #1 - Copy bitstream and device tree to hardware
            [rembitFile, remdtbFile, remsysInit] = copyProgrammingFiles(obj, bitFile, dtbFile, systemInitPath);

            % #2 - Set boot params
            result = setBootParams(obj, rembitFile, remdtbFile, remsysInit, rdName, useSplitBitstream, periphRbf);
            system(obj, 'sync');
            disp(result);

            % #3 - Reboot hardware & wait for board to respond
            reboot(obj);
        end

        function boardName = getDefaultBoardName(obj)
            boardName = 'Intel SoC';
        end
    end

    methods (Access = protected)
        function result = setBootParams(obj, rembitFile, remdtbFile, remsysInit, rdName, useSplitBitstream, periphRbf)
            result = setBootParams@soc.internal.SocBase(obj, rembitFile, remdtbFile, remsysInit, rdName);

            if useSplitBitstream
                % Overwrite peripheral rbf file
                putFile(obj, periphRbf, '/mnt/socfpga.periph.rbf');
                
                % Get the "fpgafile" environment variable. This environment
                % variable is set by running the "mw_setboot" command. It
                % will be returned as a string such as
                % "fpgafile=hdlcoder_rd/socfpga.core.rbf"
                fpgaFileEnv = strtrim(system(obj, 'fw_printenv fpgafile'));
                rbfCoreImage = extractAfter(fpgaFileEnv,'=');

                % Set "rbfcoreimage" environment variable which corresponds
                % to the core rbf location
                system(obj, ['fw_setenv rbfcoreimage ', rbfCoreImage]);
            end
        end
    end

    methods (Static, Hidden)
        %% Helper functiosn to statically edit saved preferences
        function deviceAddr = getSavedDeviceAddress()
            deviceAddr = getSavedDeviceAddress@soc.internal.SocBase(intelsoc.SocKey);
        end

        function username = getSavedUsername()
            username = getSavedUsername@soc.internal.SocBase(intelsoc.SocKey);
        end

        function password = getSavedPassword()
            password = getSavedPassword@soc.internal.SocBase(intelsoc.SocKey);
        end

        function port = getSavedSSHPort()
            port = getSavedSSHPort@soc.internal.SocBase(intelsoc.SocKey);
        end

        function setSavedDeviceAddress(deviceAddr)
            setSavedDeviceAddress@soc.internal.SocBase(deviceAddr, intelsoc.SocKey);
        end

        function setSavedUsername(username)
            setSavedUsername@soc.internal.SocBase(username, intelsoc.SocKey);
        end

        function setSavedPassword(password)
            setSavedPassword@soc.internal.SocBase(password, intelsoc.SocKey);
        end

        function setSavedSSHPort(port)
            setSavedSSHPort@soc.internal.SocBase(port, intelsoc.SocKey);
        end
    end
end
