
%

%   Copyright 2015-2018 The MathWorks, Inc.

classdef (Abstract) Plugin_Tcl_Quartus < handle
    % Quartus/Qsys Tcl generation functions
    
    %% all functions are required to be implemented in plugin folder
    methods (Abstract, Static)
        
        cmdStr = getCmdQsysGenerate(cmdQsysGen, targetL, ...
                dirQsysGen, projectFileName, ipRepositoryFolder)

    end
    
    %-------------------READ BEFORE MODIFYING BELOW------------------------
    % To change these methods in future releases, first move the method you
    % wish to move into the version specific child classes and then modify
    % this method in the current version only. This will help maintain
    % backward compatibility with previous Quartus/Qsys tools that are no
    % longer tested in BaT
    %----------------------------------------------------------------------
    methods( Static )
         function getTclClockResetConnection(fid,clk,rst)
            fprintf(fid, 'add_connection %s ${HDLCODERIPINST}.ip_clk\n', clk);
            fprintf(fid, 'add_connection %s ${HDLCODERIPINST}.ip_rst\n', rst);
        end
        
        function getTclSetClockFreq(fid,clkNum,freq,inst)
            % This function only works for "Altera_pll", which is on all
            % their modern FPGAs, but not for "altpll", which is on the MAX
            % 10. To handle max10 altpll, we would need to calculate the
            % multiply and divide and set them via the commands below.
            % fprintf(fid, 'set_instance_parameter_value %s {CLK%d_MULTIPLY_BY} {3}\n', inst,clkNum);
            % fprintf(fid, 'set_instance_parameter_value %s {CLK%d_DIVIDE_BY} {2}\n', inst,clkNum);
            
            fprintf(fid, 'set_instance_parameter_value %s {gui_output_clock_frequency%d} {%f}\n', inst,clkNum,freq);

        end
        
        function getTclCreateClockConstraint(fid,clkName,clkFreq)
            % generate FPGA clock period constrain       
            % Calculate clock period in ns
            clkPeriodInNs = 1e3/clkFreq;            
            
            fprintf(fid, ...
                'create_clock -name %s -period %fns -waveform {0.0ns %fns} [get_ports {%s}]\n', ...
                clkName, clkPeriodInNs, clkPeriodInNs/2, clkName);
            fprintf(fid, ...
                'derive_pll_clocks\n');
            fprintf(fid, ...
                'derive_clock_uncertainty\n');
        end

        function getTclQsysAddConnection(fid,connectionStart,connectionEnd)
            fprintf(fid, 'add_connection %s %s\n', connectionStart, connectionEnd);        
        end

        function getTclQsysSetConnectionParam(fid,connectionName, param, value)
            fprintf(fid, 'set_connection_parameter_value %s %s {%s}\n',...
                connectionName, param, value);
        end
        
        function getTclTimingCheck(fid, timingQueryTcl)
            fprintf(fid, 'set_global_assignment -name TIMEQUEST_REPORT_SCRIPT %s\n', timingQueryTcl);
        end        
        
        function generateTimingQueryTclCheck(fid, bitsStreamName, bitsStreamNameTf, reportErrorOnTiming, quartusTimingReportCopy, runExt, SlackTolerance)
            fprintf(fid, 'create_timing_netlist\n'); 
            fprintf(fid, 'read_sdc\n'); 
            fprintf(fid, 'update_timing_netlist\n'); 
            fprintf(fid, 'report_timing -append\n');            
            fprintf(fid, 'set worst_path [get_timing_paths -npaths 1 -setup]\n'); 
            fprintf(fid, 'foreach_in_collection path $worst_path {\n');
            fprintf(fid, '\tset slack [get_path_info $path -slack] }\n');                        
            fprintf(fid, 'if {$slack > 0} {\n' );
            fprintf(fid, '\tset worst_path [get_timing_paths -npaths 1 -hold]\n');
            fprintf(fid, '\tforeach_in_collection path $worst_path {\n');
            fprintf(fid, '\tset slack [get_path_info $path -slack] }\n');
            fprintf(fid, '}\n');             
            if SlackTolerance == 0
               fprintf(fid, 'if {$slack < 0} {\n');
            else
               fprintf(fid, 'if {$slack < -%4.4f} {\n', SlackTolerance);
            end
            fprintf(fid, '\treport_timing -setup -multi_corner -file %s -panel_name {Report Setup Timing} -npaths 3 -detail full_path\n', quartusTimingReportCopy); 
            fprintf(fid, '\treport_timing -append -hold -multi_corner -file %s -panel_name {Report Hold Timing} -npaths 3 -detail full_path\n', quartusTimingReportCopy);             
			% When run build externally and ref design parameter is set to error
			% then rename the bitstream to _timingfailure
            if runExt && reportErrorOnTiming
                fprintf(fid, '\tfile rename %s %s \n',bitsStreamName, bitsStreamNameTf);
			% When run build internally, make a copy of the bitstream which is required 
			% for split bitstream generation for Arria10
			% After the run build, delete one of the bitstream in EmbeddedToolDriver
			% class based on the ref design parameter
            elseif ~runExt
                fprintf(fid, '\tfile copy %s %s \n',bitsStreamName, bitsStreamNameTf);
            end
            if reportErrorOnTiming                
                fprintf(fid, '\treturn -code error [format "ERROR- Timing constraints NOT met!\nThe worst slack is $slack ns\n"]\n');                
            else
                fprintf(fid, '\tputs " Timing constraints NOT met! "\n');
            end
            fprintf(fid, '}\n'); 
        end
        %% MATLAB AXI Master Tcl connections
        function getTclQsysConnectionwithJTAGAXIMaster (fid,clk,reset,isAXI4InterfaceInUse, targetFreq)
            % Add hdl verifier IP to the blok design, Connect clock and
            % reset connections to the hdlverifier IP
            fprintf(fid, 'add_instance AXI_Manager_0 AXI_Manager 2.0\n');
            % If the target frequency is greater than 100 MHz then insert
            % new pll with 50 MHz and drive JTAG AXI Master at 50 MHz.
            if targetFreq > 100
              fprintf(fid, 'add_instance jtag_axi_pll_1 altera_pll 18.1\n');
              fprintf(fid, 'set_instance_parameter_value jtag_axi_pll_1 {gui_output_clock_frequency0} {50.000000} \n');
              fprintf(fid, 'add_connection %s jtag_axi_pll_1.refclk\n',clk);
              fprintf(fid, 'add_connection %s jtag_axi_pll_1.reset\n',reset);              
              fprintf(fid, 'add_connection jtag_axi_pll_1.outclk0 AXI_Manager_0.aclk\n');
              fprintf(fid, 'add_connection %s AXI_Manager_0.aresetn\n',reset); 
            % Drive at JTAG AXI Master at DUT Target Frequency if Frequency is <= 100 MHz  
            else
              fprintf(fid, 'add_connection %s AXI_Manager_0.aclk\n',clk);
              fprintf(fid, 'add_connection %s AXI_Manager_0.aresetn\n',reset);              
            end
            

            if ~isAXI4InterfaceInUse
                fprintf(fid, 'add_connection AXI_Manager_0.axm_m0 ${HDLCODERIPINST}.s_axi\n');
            end
        end

        function addQuartusQip(fid, qipFilepath)
            % Add the .qip file to the quartus project
            fprintf(fid, 'set_global_assignment -name QIP_FILE %s\n', qipFilepath);

        end
    end
end

% LocalWords:  HDLCODERIPINST rst fns pll altpll fid
