function varargout = hdlsettingsdspb(sys)
% HDLSETTINGSDSPB    Helper function for DSP Builder integration
% 
% HDLSETTINGSDSPB(SYS) helps you setup HDL code generation properties for a
% Simulink model that is partially implemented in Altera DSP Builder. The
% argument SYS is a string specifying the full Simulink path to the
% subsystem implemented in DSP Builder.
%
% HDLSETTINGSDSPB(SYS) does the following:
% 1. Extracts settings from the DSP Builder blocks in your model.
% 2. Sets HDL code generation properties that are compatible with the
%    extracted settings.
% 3. Specifies a black box implementation for the DSP Builder subsystem.
% 4. Returns the control statements generated in 2 & 3 in a new file in the
%    editor.
%
% You can use the generated file to create a new control file for your
% model, or copy and paste the control statements to an existing control
% file.
% 
% T = HDLSETTINGSDSPB(SYS) returns the control statements in string T
% instead of a new file.
% 
%   See also HDLNEWCONTROLFILE

%   Copyright 2008-2011 The MathWorks, Inc.
%     


% Step 1: Get code generation settings from DSP Builder

toplvl = getTopLevel(sys);
dspb = getDspBuilderParams(toplvl);


% Step 2: Specify implementation for the DSP Builder subsystem

nl = '\n';
bl = blanks(10);

str = ['%% Use Altera black box implementation for the DSP Builder subsystem' nl ...
    'c.forEach(''' sys ''',...' nl ...
    bl '''built-in/SubSystem'', {},...' nl ...
    bl '''AlteraBlackBox'',...' nl ...
    bl '{''AddClockPort'', ''on'', ''ClockInputPort'', ''' dspb.clk.name ''',...' nl ...
    bl ' ''AddResetPort'', ''on'', ''ResetInputPort'', ''' dspb.rst.name ''',...' nl ...
    bl ' ''AddClockEnablePort'', ''off''});' nl];


% Step 3: Specify code generation properties

bl = blanks(6);

str = [str nl ...
    'targetdir = ''hdlsrc'';' nl ...
    'dspbdir = ''' strrep(dspb.expdir,'\','/') ''';' nl ...
    'projectdir = ''quartusproj'';' nl];

% Set TargetDirectory, and other properties to be compatible with DSP
% Builder code generation settings
str = [str nl ...
    '%% Match DSP Builder code generation settings' nl ...
    'c.set(''TargetDirectory'', targetdir,...' nl ...
    bl '''TargetLanguage'', ''VHDL'',...' nl ...
    bl '''ResetValue'', ''' dspb.rst.value ''',...' nl ...
    bl '''ClockHighTime'', ' num2str(dspb.clk.period/2) ',...' nl ...
    bl '''ClockLowTime'', ' num2str(dspb.clk.period/2) ',...' nl ...
    bl '''TestBenchClockEnableDelay'', 0);' nl];

% Set HDLSynth[FilePostfix, Init, Cmd, Term] properties to create synthesis
% tcl script for Quartus II.
str = [str nl ...
    '%% Create Quartus II synthesis script' nl ...
    'c.set(''HDLSynthFilePostfix'', ''_quartus.tcl'',...' nl ...
    bl '''HDLSynthInit'',...' nl ...
    bl '[''load_package flow\\n'',...' nl ...
    bl ' ''set top_level %%s\\n'',...' nl ...
    bl ' ''set hdlc_dir "'' strrep(fullfile(pwd,targetdir),''\\'',''/'') ''"\\n'',...' nl ...
    bl ' ''set dspb_dir "'' hdlgetfullpath(dspbdir) ''"\\n'',...' nl ...
    bl ' ''set proj_dir "$hdlc_dir/../'' projectdir ''"\\n'',...' nl ...
    bl ' ''file mkdir $proj_dir\\n'',...' nl ...
    bl ' ''cd $proj_dir\\n'',...' nl ...
    bl ' ''project_new $top_level -revision $top_level -overwrite\\n'',...' nl ...
    bl ' ''set_global_assignment -name FAMILY "' dspb.family '"\\n'',...' nl ...
    bl ' ''set_global_assignment -name DEVICE ' dspb.device '\\n'',...' nl ...
    bl ' ''set_global_assignment -name TOP_LEVEL_ENTITY $top_level\\n'',...' nl ...
    bl ' ''set_global_assignment -name QIP_FILE $dspb_dir/' toplvl  '.qip\\n''],...' nl ...
    bl '''HDLSynthCmd'',...' nl ...
    bl '''set_global_assignment -name VHDL_FILE $hdlc_dir/%%s\\n'',...' nl ...
    bl '''HDLSynthTerm'', ''execute_flow -compile\\n'');' nl];

% Set HDLCompile[Init, VHDLCmd] properties
str = [str nl ...
    'c.set(''HDLCompileInit'',...' nl ...
    bl '[''vlib work\\n'',...' nl ...
    bl ' ''set hdlc_dir "'' strrep(fullfile(pwd,targetdir),''\\'',''/'') ''"\\n''],...' nl ...
    bl '''HDLCompileVHDLCmd'', ''vcom %%s $hdlc_dir/%%s\\n'');' nl];

% Step 4: Write code generation settings to new file or string

str = sprintf(str);
if nargout == 0
    hdlneweditorfile(str);
else
    varargout(1) = {str};
end

%--------------------------------------------------------------------------       
function toplvl = getTopLevel(sys)
% Return the path name of the top-level of current block/system SYS

toplvl = hdlgettoplevel(sys);

if isempty(toplvl)
    error(message('hdlcoder:hdlcoderdemos:invalidtoplevel'));
end

%--------------------------------------------------------------------------       
function dspb = getDspBuilderParams(toplvl)
% Get code generation properties from the Signal Compiler block and the
% Clock block, if exist, in the model

% Find the Signal Compiler block in the model top-level
sc_blk = find_system(toplvl, 'SearchDepth', 1, 'ReferenceBlock', ...
    'allblocks_alteradspbuilder2/Signal Compiler');

if length(sc_blk) ~= 1
    error(message('hdlcoder:hdlcoderdemos:sigcompilerblk'));
else
    sc_blk = char(sc_blk);
    
    % Get target device
    dspb.family = get_param(sc_blk, 'DeviceFamily');
    dspb.device = get_param(sc_blk, 'DeviceName');
    
    % Get HDL export directory
    dspb.expdir = get_param(sc_blk, 'ExportDir');
end

% Find the Clock block in the model top-level
clk_blk = find_system(toplvl, 'SearchDepth', 1, 'ReferenceBlock', ...
    'allblocks_alteradspbuilder2/Clock');

if length(clk_blk) > 1
    error(message('hdlcoder:hdlcoderdemos:clockblk'));
elseif isempty(clk_blk)
    % Use default clock and reset settings
    dspb.clk.name = 'clock';
    dspb.rst.name = 'aclr';
    dspb.clk.period = 20;
    dspb.rst.value = 'active-low';
else
    clk_blk = char(clk_blk);
    
    % Get clock and reset settings from Clock block
    dspb.clk.name = get_param(clk_blk, 'Name');
    dspb.rst.name = get_param(clk_blk, 'Reset');
    clk_period = slResolve(get_param(clk_blk, 'ClockPeriod'), clk_blk);
    clk_unit = get_param(clk_blk, 'ClockPeriodUnit');

    cp_value = {'ps', 'ns', 'us', 'ms', 'sec'};
    cp_scale = [1e-3 1 1e3 1e6 1e9];
    dspb.clk.period = clk_period * cp_scale(find(strcmp(clk_unit, cp_value)));

    if strcmp('Active Low', get_param(clk_blk, 'ResetType'))
        dspb.rst.value = 'active-low';
    else
        dspb.rst.value = 'active-high';
    end
end
