% Joshua Mack, Sam Bellestri, Nia Simmonds IREECE 2015
% UltimateTestScript_eToTheX_Fixed:
%   Acts as a completely autonomous test script for testing fixed-point e^x.
%   After establishing the test domain for each case and the arrays specifying the cases that need testing, 
%   The script simply iterates through each combination, generates LUTs, generates a testbench, executes the Vivado simulation,
%   analyzes the results, and proceeds to the next test case. Requires some directories to be manually specified by hand, though, 
%   and it can be a bit tricky working around (1) Matlab's hatred of double quotation marks (") and (2) unix's hatred of spaces. 
%   If you're careful, though, and only replace the path names between the single and double quotes, it should transfer to other machines fine.

%   Extra note: this version is one of the few scripts that includes an optional argument called "mode".
%   It is used to select between "Normal Mode" -- the mode used in all other UltimateTestScript files -- and "Architecture Exploration Mode".
%   This second mode was created because we were unsure which fixed-point formats would work well across our e^x, ln(x), and x^y architectures.
%   So, we had the computer hold the number of iterations constant and instead modified the script to look at each combination of fracWidth and intWidth
%   And then plot the results in a 3D PSNR plot of IntWidth vs FracWidth vs PSNR for a fixed number of iterations in the architecture.

function [] = UltimateTestScript_eToTheX_Fixed(mode)

if nargin == 0
    mode = 'normal';
end

if strcmp(mode, 'normal') == 1

    % Normal mode: used for when you've decided on a set of architectures and want to test them vs number of iterations
    fprintf('Running in Normal Mode...\n\n');

    % Disable underflow/overflow warnings. Some will probably happen and it's part of the process. They're annoying.
    warning('off', 'fixed:fi:overflow');
    warning('off', 'fixed:fi:underflow');

    % Define the domain that we're testing over
    xMin = -12.42644;
    xMax = 12.42644;
    xPts = 1000;
    
    % Define the formats that we're testing. Note that the script tests bitWidth vs numIter, where bitWidth(i) = fracArr(i) + intArr(i), and numIter(j) = iterArr(j).
    fracArr = [8 8 12 16 20 24 28 32 32 32 32 32 32 32];
    intArr = [16 20 20 20 20 20 20 20 24 28 32 36 40 44];
    iterArr = [8 12 16 20 24 28 32 36 40];

    % Define the name of the file that results get saved to at the end of the script
    fileName = ['eToTheX_Fixed_PSNR_', num2str(length(fracArr)*length(iterArr)), '_Cases_', num2str(xMin, 2), '_to_', num2str(xMax, 2), '_', datestr(datetime('today')), '.txt'];
    
    % Name of the VHDL testbench file
    tbName = 'Fixed_eToTheX_Generic_tb.vhd';
    % Path to testbench directory -- Generic form: "*PathToVHDLProj*/VHDLProj.srcs/sim_1/new" (or maybe something totally different if you're not using Vivado)
    VHDLPath = '"C:/Data/College Stuff/IREECE 2015/Cylinder Pressure Estimation/VHDL/Fixed_eToTheX/Fixed_eToTheX.srcs/sim_1/new/"';
    % Path to Matlab directory -- the current working directory for Matlab. This line can surely be replaced with some usage of "pwd"
    MatlabPath = '"C:/Data/College Stuff/IREECE 2015/Cylinder Pressure Estimation/Matlab Files/';
    
    % Clear the results directory of any previously generated files
    % It enabled us to be sure that the results we were getting were, in fact, from this test run. Otherwise, for example, a given configuration's simulation may fail
    % (say, 40 bits, 24 iterations), in which case the script hands control from the Vivado simulator back to Matlab, and the only way that the script knows the simulation failed
    % is if it cannot find the resulting simulation file (or if it doesn't have xPts results in it). 
    unix('rm ./FixedPt_eToTheX_VivadoResults/*');

    myPSNR = zeros(length(fracArr), length(iterArr));
    h1 = figure();
    for i = 1:1:length(fracArr)
        for j = 1:1:length(iterArr)
            if iterArr(j) <= fracArr(i)
                % Assign the parameters for this execution
                intWidth = intArr(i); fracWidth = fracArr(i); numIterations = iterArr(j);
                % Print out diagnostic information including configuration under test and timestamp.
                % If a given test case is taking a significantly longer amount of time than others, there's a good chance that the Vivado simulator process
                % needs to be terminated manually
                fprintf('\n---------------------------------\n');
                fprintf('%d bits, %d iterations, %s', intWidth+fracWidth, numIterations, datestr(now, 'HH:MM:SS PM'));
                fprintf('\n---------------------------------\n'); 

                % Path to simulation results -- aka the path to where the testbench file is being stored
                % First just establish the basic path
                simuResultsPath = '"C:/Data/College Stuff/IREECE 2015/Cylinder Pressure Estimation/VHDL/Fixed_eToTheX/Fixed_eToTheX.sim/sim_1/behav/';
                % Then concatenate the filename of the simulation results
                simuResultsPath = [simuResultsPath, num2str(numIterations), 'iterations', num2str(intWidth+fracWidth), 'bits_eToTheX_fixed.txt"'];

                % Generate testbench
                Fixed_eToTheX_Generic_tb(xMin, xMax, xPts, numIterations, intWidth, fracWidth);
                % Copy it to the vivado project
                unix(['cp ./', tbName, ' ', VHDLPath]);

                % Generate LUTs and move them to their directory
                [fileName1, fileName2] = generateFixedLUTs(numIterations,intWidth,fracWidth);
                projectPathStr = '"C:/Data/College Stuff/IREECE 2015/Cylinder Pressure Estimation/VHDL/Fixed_eToTheX/Fixed_eToTheX.srcs/sources_1/imports/"';
                unix(['mv ', fileName1, ' ', fileName2, ' ', projectPathStr]);

                % Start vivado and call the script to automate the simulation process
                % Requires the companion tcl script to be inside the "TCL_Files" directory located inside Matlab's current working directory.
                unix('C:/Xilinx/Vivado/2015.1/bin/vivado -mode tcl -source ./TCL_Files/eToTheXSim_Fixed.tcl');

                % Copy the results back to matlab directory into PSNR folder
                unix(['cp ', simuResultsPath, ' ', MatlabPath, '/FixedPt_eToTheX_VivadoResults/"']);

                % Call the matlab PSNR script
                myPSNR(i, j) = convertPlotAndPSNR_eToTheX_Fixed(xMin, xMax, xPts, intWidth, fracWidth, numIterations);

                % Repeat the Vivado simulation if the simulation failed -- either by the results file not being found or not having xPts data points
                while myPSNR(i, j) == -123456
                    unix('C:/Xilinx/Vivado/2015.1/bin/vivado -mode tcl -source ./TCL_Files/eToTheXSim_Fixed.tcl');
                    unix(['cp ', simuResultsPath, ' ', MatlabPath, '/FixedPt_eToTheX_VivadoResults/"']);
                    myPSNR(i, j) = convertPlotAndPSNR_eToTheX_Fixed(xMin, xMax, xPts, intWidth, fracWidth, numIterations);
                end
            else
                % Keep each sample equal in length regardless of cases run
                myPSNR(i, j:1:length(iterArr)) = myPSNR(i, j-1);
            end
        end
        % Plot the results
        figure(h1);
        myPlots(i) = plot(iterArr, myPSNR(i, :), 'd-'); hold all;
    end

    % Add some legends and a title to make it look nice
    for i = 1:1:length(fracArr)
       legendStrs{i} = [num2str(fracArr(i) + intArr(i)), ' bits'];
    end
    legend(myPlots, legendStrs);
    title(['Fixed Pt ${e}^{x}$ PSNR vs Number of Iterations, ', ...
            num2str(xMin), ' $\leq$ x $\leq$ ', num2str(xMax)], 'Interpreter', 'laTex');
    
    %And write the results to a file because why not?
    myFile = fopen(['./PSNR_Output/', fileName], 'w');
    fprintf(myFile, 'xMin = %f, xMax = %f, xPts = %d', xMin, xMax, xPts);
    for i = 1:1:length(myPSNR(:, 1))
        for j = 1:1:length(myPSNR(1, :))
            fprintf(myFile, 'Int Width: %d, Frac Width: %d, Num Iterations: %d\n', intArr(i), fracArr(i), iterArr(j));
            fprintf(myFile, '%f\n', myPSNR(i, j));
        end
    end
    fclose(myFile);

else

    % Other mode: used for when you have no idea what architectures to test, so you're going to test all of them with a constant (large) number of iterations and see which work well.
    fprintf('Running in Architecture Exploration Mode...\n\n');

    % Disable warnings about overflow or underflow issues -- they're going to happen here and it's part of the process 
    warning('off', 'fixed:fi:overflow');
    warning('off', 'fixed:fi:underflow');

    % Define the domain for each simulation to test over
    xMin = -12.42644;
    xMax = 12.42644;
    xPts = 1000;
    
    % Define the constant number of iterations and range of bit widths to be tested
    numIter = 40;
    fracArr = 2:2:64;
    intArr = 2:2:64;

    % Our use of this secondary mode did not require writing results to a file, but it easily can by uncommenting this code as well as the section for writing to file at the bottom of the script
    % fileName = ['eToTheX_Fixed_PSNR_', num2str(length(fracArr)*length(iterArr)), '_Cases_', num2str(xMin, 2), '_to_', num2str(xMax, 2), '_', datestr(datetime('today')), '.txt'];
    
    % Name of the VHDL testbench file
    tbName = 'Fixed_eToTheX_Generic_tb.vhd';
    % Path to testbench directory -- generic form: "*PathToVHDLProj*/VHDLProj.srcs/sim_1/new/" (or something along those lines if you're also using Vivado)
    VHDLPath = '"C:/Data/College Stuff/IREECE 2015/Cylinder Pressure Estimation/VHDL/Fixed_eToTheX/Fixed_eToTheX.srcs/sim_1/new/"';
    % Path to Matlab directory -- the current working directory for Matlab. Can surely be replaced by some usage of "pwd".
    MatlabPath = '"C:/Data/College Stuff/IREECE 2015/Cylinder Pressure Estimation/Matlab Files/';
    
    % Clear the results directory of any previously generated files
    % It enabled us to be sure that the results we were getting were, in fact, from this test run. Otherwise, for example, a given configuration's simulation may fail
    % (say, 40 bits, 24 iterations), in which case the script hands control from the Vivado simulator back to Matlab, and the only way that the script knows the simulation failed
    % is if it cannot find the resulting simulation file (or if it doesn't have xPts results in it). 
    unix('rm ./FixedPt_eToTheX_VivadoResults/*');

    myPSNR = zeros(length(fracArr), length(intArr));
    h1 = figure();
    for i = 1:1:length(fracArr)
        for j = 1:1:length(intArr)
            % Assign the parameters for this execution
            intWidth = intArr(j); fracWidth = fracArr(i); numIterations = numIter;
            fprintf('\n----------------------\n');
            fprintf('%d int bits, %d frac bits, %s', intWidth, fracWidth, datestr(now, 'HH:MM:SS PM'));
            fprintf('\n----------------------\n'); 

            % Path to simulation results
            % First just establish the basic path
            simuResultsPath = '"C:/Data/College Stuff/IREECE 2015/Cylinder Pressure Estimation/VHDL/Fixed_eToTheX/Fixed_eToTheX.sim/sim_1/behav/';
            % Then concatenate the filename of the simulation results
            simuResultsPath = [simuResultsPath, num2str(numIterations), 'iterations', num2str(intWidth+fracWidth), 'bits_eToTheX_fixed.txt"'];

            % Generate testbench
            Fixed_eToTheX_Generic_tb(xMin, xMax, xPts, numIterations, intWidth, fracWidth);
            % Copy it to the vivado project
            unix(['cp ./', tbName, ' ', VHDLPath]);

            % Generate LUTs and move them to their directory
            [fileName1, fileName2] = generateFixedLUTs(numIterations,intWidth,fracWidth);
            projectPathStr = '"C:/Data/College Stuff/IREECE 2015/Cylinder Pressure Estimation/VHDL/Fixed_eToTheX/Fixed_eToTheX.srcs/sources_1/imports/"';
            unix(['mv ', fileName1, ' ', fileName2, ' ', projectPathStr]);

            % Start vivado and call the script to automate the simulation process
            % Requires the companion tcl script to be inside the "TCL_Files" directory located inside Matlab's current working directory.
            unix('C:/Xilinx/Vivado/2015.1/bin/vivado -mode tcl -source ./TCL_Files/eToTheXSim_Fixed.tcl');

            % Copy the results back to matlab directory into PSNR folder
            unix(['cp ', simuResultsPath, ' ', MatlabPath, '/FixedPt_eToTheX_VivadoResults/"']);

            % Call the matlab PSNR script
            myPSNR(i, j) = convertPlotAndPSNR_eToTheX_Fixed(xMin, xMax, xPts, intWidth, fracWidth, numIterations);

            % Repeat the Vivado simulation if the simulation failed
            while myPSNR(i, j) == -123456
                unix('C:/Xilinx/Vivado/2015.1/bin/vivado -mode tcl -source ./TCL_Files/eToTheXSim_Fixed.tcl');
                unix(['cp ', simuResultsPath, ' ', MatlabPath, '/FixedPt_eToTheX_VivadoResults/"']);
                myPSNR(i, j) = convertPlotAndPSNR_eToTheX_Fixed(xMin, xMax, xPts, intWidth, fracWidth, numIterations);
            end
        end
    end

    % Plot the results
    figure(h1);
    [X Y] = meshgrid(fracArr, intArr);
    plot3(X, Y, myPSNR, 'd');
    xlabel('FracWidth');
    ylabel('IntWidth');

    %Add some legends and a title to make it look nice
    title(['Fixed Pt ${e}^{x}$ PSNR vs FracWidth vs IntWidth for N = 40, ', ...
            num2str(xMin), ' $\leq$ x $\leq$ ', num2str(xMax)], 'Interpreter', 'laTex');
    
    %And write the results to a file because why not?
    % myFile = fopen(['./PSNR_Output/', fileName], 'w');
    % fprintf(myFile, 'xMin = %f, xMax = %f, xPts = %d', xMin, xMax, xPts);
    % for i = 1:1:length(myPSNR(:, 1))
    %     for j = 1:1:length(myPSNR(1, :))
    %         fprintf(myFile, 'Int Width: %d, Frac Width: %d, Num Iterations: %d\n', intArr(i), fracArr(i), iterArr(j));
    %         fprintf(myFile, '%f\n', myPSNR(i, j));
    %     end
    % end
    % fclose(myFile);

end