% Joshua Mack, Sam Bellestri, Nia Simmonds IREECE 2015
% FloP_xToTheY_Generic_tb:
%   Takes in a given test domain along with the corresponding exponent and fractional widths of the floating-point format and number of iterations
%   And then generates a VHDL testbench for simulating a specific configuration of x^y. The resulting file is saved to the current Matlab working directory
%   As "FloP_xToTheY_Generic_tb.vhd".

function [] = FloP_xToTheY_Generic_tb(xLower, xUpper, xPoints, yPoints, iterations, expWidth, fracWidth)

    % Declare a few constants that will be used throughout the process.
    numBits = expWidth+fracWidth+1;
    iterWidth = floor(log2(fracWidth))+1;
    q = quantizer('float',[numBits expWidth]);

    negIter = -5:1:0; posIter = 1:1:iterations;
    scalingFactor = prod(sqrt(1-(1-2.^(negIter-2)).^2)) * prod(sqrt(1-(2.^(-posIter)).^2)) *  0.998044956480943;

    % The easiest way to understand this piece is to run the script and look at the output file.
    % Actually, probably one of the other ones that includes a bunch of "\t" character escapes to format things nicely would be better.
    % Then come back to here.
    file = fopen('FloP_xToTheY_Generic_tb.vhd', 'wt');
    fprintf(file,'library IEEE;\n');
    fprintf(file,'use IEEE.STD_LOGIC_1164.ALL;\n');
    fprintf(file,'use std.textio.all;\n');
    fprintf(file,'use ieee.std_logic_textio.all;\n');
    fprintf(file,'library std;\n');
    fprintf(file,'use std.env.all;\n');
    fprintf(file,'use ieee.std_logic_1164.all;\n');
    fprintf(file,'use ieee.math_real."floor";\n');
    fprintf(file,'use ieee.math_real."log2";\n');
    fprintf(file,'use ieee.numeric_std.all;\n\n');
    fprintf(file,'entity FloP_xToTheY_Generic_tb is\n end FloP_xToTheY_Generic_tb;\n\n');
    fprintf(file,['architecture Behavioral of FloP_xToTheY_Generic_tb is\n', ...
        'component FloP_xToTheY is\n', ...
        'Generic ( fileName1 : string := "%dBitExpandedLUT.txt"; fileName2 : string := "%dBitLUT.txt"; \n expWidth : integer := %d; fracWidth : integer := %d; iterWidth : integer := %d );\n',...
        'Port ( x, y, InverseScalingFactor : in std_logic_vector(expWidth+fracWidth downto 0);\n'...
           'clk, start : in std_logic;\n'...
           'M, N : in std_logic_vector(iterWidth-1 downto 0);\n'...
           'output : out std_logic_vector(expWidth+fracWidth downto 0);\n'...
           'done : out std_logic );\n'],numBits,numBits,expWidth,fracWidth,iterWidth);
    fprintf(file,['end component;\n\n', ...
    'constant file1 : string := "%dBitExpandedLUT.txt";\n', ...
    'constant file2 : string := "%dBitLUT.txt";\n', ...
    'constant expWidth : integer := %d;\n', ...
    'constant fracWidth : integer := %d;\n', ...
    'constant iterWidth : integer := %d;\n', ...
        'signal clk, start, done : std_logic;\n', ...
        'signal x, y, InvScalFact, output : std_logic_vector (expWidth+fracWidth downto 0);\n', ...
        'signal M, N : std_logic_vector (iterWidth-1 downto 0);\n', ...
        'begin\n',...
            'myXToTheY : FloP_xToTheY Generic Map(fileName1 => file1, fileName2 => file2,\n', ...
                    'expWidth => expWidth, fracWidth => fracWidth, iterWidth => iterWidth)\n', ...
            'Port Map(x => x, y => y, InverseScalingFactor => InvScalFact, clk => clk, start => start, M => M, N => N, output => output, done => done);\n', ...
            'clock : process begin\n',...
                'clk <= ''0''; wait for 1 ns;\n',...
                'clk <= ''1''; wait for 1 ns;\n',...
           'end process;\n\n',...
            'testProc : process\n',... 
             'FILE test_out_data1: TEXT open WRITE_MODE is "%diterations%dbits_xToTheY.txt";\n',...
             'variable L1              : LINE;\n\n',...
         'begin\n\n',...
        '-- Using N = "%d", M = "%d"\n',...
        'N <= "%s"; M <= "%s";\n',...
        '-- The main pieces that need to be in place to get nonscaled, separated values of Cosh and Sinh.\n',...
        'InvScalFact <= "%s";\n\n'],numBits,numBits,expWidth,fracWidth,iterWidth,iterations,numBits,iterations,5,dec2bin(iterations,iterWidth),dec2bin(5, iterWidth), num2bin(q,1/scalingFactor));

    % All preparation work is complete, begin writing test cases to the file
    fprintf(file, '-------------------------Begin Testing----------------------------\n');
    iterNum = 1;
    x = linspace(xLower, xUpper, xPoints);
    for i = 1:1:xPoints
        % Note that y varies with x according to the convergence interval for x^y with M = 5.
        y = linspace(-12.42644/log(x(i)), 12.42644/log(x(i)), yPoints);
        for j = 1:1:yPoints
            fprintf(file,'-- Iteration %d\n',iterNum);
            fprintf(file,'-- %f\n',x(i));
            fprintf(file,'x <= "%s";\n', num2bin(q, x(i)));
            fprintf(file,'-- %f\n',y(j));
            fprintf(file,'y <= "%s";\n', num2bin(q, y(j)));
            fprintf(file,'start <= ''1''; wait for 2 ns; start <= ''0''; wait for 500 ns;\n');
            fprintf(file,'write(L1, output);\n'); fprintf(file, 'writeline(test_out_data1,L1);\n\n');
            iterNum = iterNum + 1;
        end
    end     
    
    fprintf(file,['stop(0);\n',...
        'end process;\n',...
        'end Behavioral;\n']);
    
  fclose(file);
end 